mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-22 05:27:20 +00:00
[common] move ivshmem code into the common library
This commit is contained in:
parent
c5baf212c8
commit
89d6ea0b5d
@ -41,7 +41,6 @@ get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
|
|||||||
include_directories(
|
include_directories(
|
||||||
${PROJECT_SOURCE_DIR}/include
|
${PROJECT_SOURCE_DIR}/include
|
||||||
${CMAKE_BINARY_DIR}/include
|
${CMAKE_BINARY_DIR}/include
|
||||||
${PROJECT_TOP}/vendor/ivshmem
|
|
||||||
${PKGCONFIG_INCLUDE_DIRS}
|
${PKGCONFIG_INCLUDE_DIRS}
|
||||||
${GMP_INCLUDE_DIR}
|
${GMP_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
@ -27,7 +27,3 @@ void app_quit();
|
|||||||
|
|
||||||
// these must be implemented for each OS
|
// these must be implemented for each OS
|
||||||
const char * os_getExecutable();
|
const char * os_getExecutable();
|
||||||
|
|
||||||
unsigned int os_shmemSize();
|
|
||||||
bool os_shmemMmap(void **ptr);
|
|
||||||
void os_shmemUnmap();
|
|
||||||
|
@ -22,28 +22,24 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "common/option.h"
|
#include "common/option.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <pthread.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
struct app
|
struct app
|
||||||
{
|
{
|
||||||
const char * executable;
|
const char * executable;
|
||||||
unsigned int shmSize;
|
|
||||||
int shmFD;
|
|
||||||
void * shmMap;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct app app;
|
struct app app;
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
app.executable = argv[0];
|
||||||
|
int result = app_main(argc, argv);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void sigHandler(int signo)
|
void sigHandler(int signo)
|
||||||
{
|
{
|
||||||
@ -51,177 +47,8 @@ void sigHandler(int signo)
|
|||||||
app_quit();
|
app_quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uioOpenFile(const char * shmDevice, const char * file)
|
|
||||||
{
|
|
||||||
int len = snprintf(NULL, 0, "/sys/class/uio/%s/%s", shmDevice, file);
|
|
||||||
char * path = malloc(len + 1);
|
|
||||||
sprintf(path, "/sys/class/uio/%s/%s", shmDevice, file);
|
|
||||||
|
|
||||||
int fd = open(path, O_RDONLY);
|
|
||||||
if (fd < 0)
|
|
||||||
{
|
|
||||||
free(path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(path);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char * uioGetName(const char * shmDevice)
|
|
||||||
{
|
|
||||||
int fd = uioOpenFile(shmDevice, "name");
|
|
||||||
if (fd < 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
char * name = malloc(32);
|
|
||||||
int len = read(fd, name, 31);
|
|
||||||
if (len <= 0)
|
|
||||||
{
|
|
||||||
free(name);
|
|
||||||
close(fd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
name[len] = '\0';
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
while(len > 0 && name[len-1] == '\n')
|
|
||||||
{
|
|
||||||
--len;
|
|
||||||
name[len] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int shmOpenDev(const char * shmDevice)
|
|
||||||
{
|
|
||||||
int len = snprintf(NULL, 0, "/dev/%s", shmDevice);
|
|
||||||
char * path = malloc(len + 1);
|
|
||||||
sprintf(path, "/dev/%s", shmDevice);
|
|
||||||
|
|
||||||
int fd = open(path, O_RDWR, (mode_t)0600);
|
|
||||||
if (fd < 0)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to open: %s", path);
|
|
||||||
DEBUG_ERROR("Did you remmeber to modprobe the kvmfr module?");
|
|
||||||
free(path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(path);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool shmDeviceValidator(struct Option * opt, const char ** error)
|
|
||||||
{
|
|
||||||
char * name = uioGetName(opt->value.x_string);
|
|
||||||
if (!name)
|
|
||||||
{
|
|
||||||
*error = "Failed to get the uio device name";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(name, "KVMFR") != 0)
|
|
||||||
{
|
|
||||||
free(name);
|
|
||||||
*error = "Device is not a KVMFR device";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(name);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static StringList shmDeviceGetValues(struct Option * option)
|
|
||||||
{
|
|
||||||
StringList sl = stringlist_new(true);
|
|
||||||
|
|
||||||
DIR * d = opendir("/sys/class/uio");
|
|
||||||
if (!d)
|
|
||||||
return sl;
|
|
||||||
|
|
||||||
struct dirent * dir;
|
|
||||||
while((dir = readdir(d)) != NULL)
|
|
||||||
{
|
|
||||||
if (dir->d_name[0] == '.')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
char * name = uioGetName(dir->d_name);
|
|
||||||
if (!name)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (strcmp(name, "KVMFR") == 0)
|
|
||||||
stringlist_push(sl, strdup(dir->d_name));
|
|
||||||
|
|
||||||
free(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(d);
|
|
||||||
return sl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char * argv[])
|
|
||||||
{
|
|
||||||
app.executable = argv[0];
|
|
||||||
|
|
||||||
struct Option options[] =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
.module = "os",
|
|
||||||
.name = "shmDevice",
|
|
||||||
.description = "The IVSHMEM device to use",
|
|
||||||
.type = OPTION_TYPE_STRING,
|
|
||||||
.value.x_string = "uio0",
|
|
||||||
.validator = shmDeviceValidator,
|
|
||||||
.getValues = shmDeviceGetValues
|
|
||||||
},
|
|
||||||
{0}
|
|
||||||
};
|
|
||||||
|
|
||||||
option_register(options);
|
|
||||||
|
|
||||||
int result = app_main(argc, argv);
|
|
||||||
os_shmemUnmap();
|
|
||||||
close(app.shmFD);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool app_init()
|
bool app_init()
|
||||||
{
|
{
|
||||||
const char * shmDevice = option_get_string("os", "shmDevice");
|
|
||||||
|
|
||||||
// get the device size
|
|
||||||
int fd = uioOpenFile(shmDevice, "maps/map0/size");
|
|
||||||
if (fd < 0)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to open %s/size", shmDevice);
|
|
||||||
DEBUG_ERROR("Did you remmeber to modprobe the kvmfr module?");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char size[32];
|
|
||||||
int len = read(fd, size, sizeof(size) - 1);
|
|
||||||
if (len <= 0)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to read the device size");
|
|
||||||
close(fd);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
size[len] = '\0';
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
app.shmSize = strtoul(size, NULL, 16);
|
|
||||||
|
|
||||||
// open the device
|
|
||||||
app.shmFD = shmOpenDev(shmDevice);
|
|
||||||
app.shmMap = MAP_FAILED;
|
|
||||||
if (app.shmFD < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
DEBUG_INFO("KVMFR Device : %s", shmDevice);
|
|
||||||
|
|
||||||
signal(SIGINT, sigHandler);
|
signal(SIGINT, sigHandler);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -230,34 +57,3 @@ const char * os_getExecutable()
|
|||||||
{
|
{
|
||||||
return app.executable;
|
return app.executable;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int os_shmemSize()
|
|
||||||
{
|
|
||||||
return app.shmSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool os_shmemMmap(void **ptr)
|
|
||||||
{
|
|
||||||
if (app.shmMap == MAP_FAILED)
|
|
||||||
{
|
|
||||||
app.shmMap = mmap(0, app.shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, app.shmFD, 0);
|
|
||||||
if (app.shmMap == MAP_FAILED)
|
|
||||||
{
|
|
||||||
const char * shmDevice = option_get_string("os", "shmDevice");
|
|
||||||
DEBUG_ERROR("Failed to map the shared memory device: %s", shmDevice);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*ptr = app.shmMap;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void os_shmemUnmap()
|
|
||||||
{
|
|
||||||
if (app.shmMap == MAP_FAILED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
munmap(app.shmMap, app.shmSize);
|
|
||||||
app.shmMap = MAP_FAILED;
|
|
||||||
}
|
|
@ -21,10 +21,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "windows/mousehook.h"
|
#include "windows/mousehook.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <setupapi.h>
|
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <io.h>
|
|
||||||
|
|
||||||
#include "interface/platform.h"
|
#include "interface/platform.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
@ -32,7 +30,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "common/option.h"
|
#include "common/option.h"
|
||||||
#include "common/locking.h"
|
#include "common/locking.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "ivshmem.h"
|
|
||||||
|
|
||||||
#define ID_MENU_OPEN_LOG 3000
|
#define ID_MENU_OPEN_LOG 3000
|
||||||
#define ID_MENU_EXIT 3001
|
#define ID_MENU_EXIT 3001
|
||||||
@ -46,19 +43,11 @@ struct AppState
|
|||||||
char ** argv;
|
char ** argv;
|
||||||
|
|
||||||
char executable[MAX_PATH + 1];
|
char executable[MAX_PATH + 1];
|
||||||
HANDLE shmemHandle;
|
|
||||||
bool shmemOwned;
|
|
||||||
IVSHMEM_MMAP shmemMap;
|
|
||||||
HWND messageWnd;
|
HWND messageWnd;
|
||||||
HMENU trayMenu;
|
HMENU trayMenu;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct AppState app =
|
static struct AppState app = {0};
|
||||||
{
|
|
||||||
.shmemHandle = INVALID_HANDLE_VALUE,
|
|
||||||
.shmemOwned = false,
|
|
||||||
.shmemMap = {0}
|
|
||||||
};
|
|
||||||
|
|
||||||
// undocumented API to adjust the system timer resolution (yes, its a nasty hack)
|
// undocumented API to adjust the system timer resolution (yes, its a nasty hack)
|
||||||
typedef NTSTATUS (__stdcall *ZwSetTimerResolution_t)(ULONG RequestedResolution, BOOLEAN Set, PULONG ActualResolution);
|
typedef NTSTATUS (__stdcall *ZwSetTimerResolution_t)(ULONG RequestedResolution, BOOLEAN Set, PULONG ActualResolution);
|
||||||
@ -180,13 +169,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
|
|
||||||
struct Option options[] =
|
struct Option options[] =
|
||||||
{
|
{
|
||||||
{
|
|
||||||
.module = "os",
|
|
||||||
.name = "shmDevice",
|
|
||||||
.description = "The IVSHMEM device to use",
|
|
||||||
.type = OPTION_TYPE_INT,
|
|
||||||
.value.x_int = 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
.module = "os",
|
.module = "os",
|
||||||
.name = "logFile",
|
.name = "logFile",
|
||||||
@ -278,10 +260,6 @@ shutdown:
|
|||||||
}
|
}
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
os_shmemUnmap();
|
|
||||||
|
|
||||||
if (app.shmemHandle != INVALID_HANDLE_VALUE)
|
|
||||||
CloseHandle(app.shmemHandle);
|
|
||||||
|
|
||||||
for(int i = 0; i < app.argc; ++i)
|
for(int i = 0; i < app.argc; ++i)
|
||||||
free(app.argv[i]);
|
free(app.argv[i]);
|
||||||
@ -292,7 +270,6 @@ finish:
|
|||||||
|
|
||||||
bool app_init()
|
bool app_init()
|
||||||
{
|
{
|
||||||
const int shmDevice = option_get_int ("os", "shmDevice");
|
|
||||||
const char * logFile = option_get_string("os", "logFile" );
|
const char * logFile = option_get_string("os", "logFile" );
|
||||||
|
|
||||||
// redirect stderr to a file
|
// redirect stderr to a file
|
||||||
@ -314,56 +291,6 @@ bool app_init()
|
|||||||
// get the performance frequency for spinlocks
|
// get the performance frequency for spinlocks
|
||||||
QueryPerformanceFrequency(&app.perfFreq);
|
QueryPerformanceFrequency(&app.perfFreq);
|
||||||
|
|
||||||
HDEVINFO deviceInfoSet;
|
|
||||||
PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL;
|
|
||||||
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
|
|
||||||
|
|
||||||
deviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
|
|
||||||
memset(&deviceInterfaceData, 0, sizeof(SP_DEVICE_INTERFACE_DATA));
|
|
||||||
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
|
||||||
|
|
||||||
if (SetupDiEnumDeviceInterfaces(deviceInfoSet, NULL, &GUID_DEVINTERFACE_IVSHMEM, shmDevice, &deviceInterfaceData) == FALSE)
|
|
||||||
{
|
|
||||||
DWORD error = GetLastError();
|
|
||||||
if (error == ERROR_NO_MORE_ITEMS)
|
|
||||||
{
|
|
||||||
DEBUG_WINERROR("Unable to enumerate the device, is it attached?", error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD reqSize = 0;
|
|
||||||
SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, NULL, 0, &reqSize, NULL);
|
|
||||||
if (!reqSize)
|
|
||||||
{
|
|
||||||
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)calloc(reqSize, 1);
|
|
||||||
infData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
|
||||||
if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, infData, reqSize, NULL, NULL))
|
|
||||||
{
|
|
||||||
free(infData);
|
|
||||||
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
app.shmemHandle = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0);
|
|
||||||
if (app.shmemHandle == INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
|
||||||
free(infData);
|
|
||||||
DEBUG_WINERROR("CreateFile returned INVALID_HANDLE_VALUE", GetLastError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(infData);
|
|
||||||
SetupDiDestroyDeviceInfoList(deviceInfoSet);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,56 +298,3 @@ const char * os_getExecutable()
|
|||||||
{
|
{
|
||||||
return app.executable;
|
return app.executable;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int os_shmemSize()
|
|
||||||
{
|
|
||||||
IVSHMEM_SIZE size;
|
|
||||||
if (!DeviceIoControl(app.shmemHandle, IOCTL_IVSHMEM_REQUEST_SIZE, NULL, 0, &size, sizeof(IVSHMEM_SIZE), NULL, NULL))
|
|
||||||
{
|
|
||||||
DEBUG_WINERROR("DeviceIoControl Failed", GetLastError());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (unsigned int)size;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool os_shmemMmap(void **ptr)
|
|
||||||
{
|
|
||||||
if (app.shmemOwned)
|
|
||||||
{
|
|
||||||
*ptr = app.shmemMap.ptr;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
IVSHMEM_MMAP_CONFIG config =
|
|
||||||
{
|
|
||||||
.cacheMode = IVSHMEM_CACHE_WRITECOMBINED
|
|
||||||
};
|
|
||||||
|
|
||||||
memset(&app.shmemMap, 0, sizeof(IVSHMEM_MMAP));
|
|
||||||
if (!DeviceIoControl(
|
|
||||||
app.shmemHandle,
|
|
||||||
IOCTL_IVSHMEM_REQUEST_MMAP,
|
|
||||||
&config, sizeof(IVSHMEM_MMAP_CONFIG),
|
|
||||||
&app.shmemMap, sizeof(IVSHMEM_MMAP),
|
|
||||||
NULL, NULL))
|
|
||||||
{
|
|
||||||
DEBUG_WINERROR("DeviceIoControl Failed", GetLastError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ptr = app.shmemMap.ptr;
|
|
||||||
app.shmemOwned = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void os_shmemUnmap()
|
|
||||||
{
|
|
||||||
if (!app.shmemOwned)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!DeviceIoControl(app.shmemHandle, IOCTL_IVSHMEM_RELEASE_MMAP, NULL, 0, NULL, 0, NULL, NULL))
|
|
||||||
DEBUG_WINERROR("DeviceIoControl failed", GetLastError());
|
|
||||||
else
|
|
||||||
app.shmemOwned = false;
|
|
||||||
}
|
|
@ -26,6 +26,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "common/KVMFR.h"
|
#include "common/KVMFR.h"
|
||||||
#include "common/crash.h"
|
#include "common/crash.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
|
#include "common/ivshmem.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
@ -324,6 +325,8 @@ int app_main(int argc, char * argv[])
|
|||||||
if (!installCrashHandler(os_getExecutable()))
|
if (!installCrashHandler(os_getExecutable()))
|
||||||
DEBUG_WARN("Failed to install the crash handler");
|
DEBUG_WARN("Failed to install the crash handler");
|
||||||
|
|
||||||
|
ivshmemOptionsInit();
|
||||||
|
|
||||||
// register capture interface options
|
// register capture interface options
|
||||||
for(int i = 0; CaptureInterfaces[i]; ++i)
|
for(int i = 0; CaptureInterfaces[i]; ++i)
|
||||||
if (CaptureInterfaces[i]->initOptions)
|
if (CaptureInterfaces[i]->initOptions)
|
||||||
@ -350,25 +353,25 @@ int app_main(int argc, char * argv[])
|
|||||||
if (!app_init())
|
if (!app_init())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
unsigned int shmemSize = os_shmemSize();
|
|
||||||
uint8_t * shmemMap = NULL;
|
|
||||||
int exitcode = 0;
|
|
||||||
|
|
||||||
DEBUG_INFO("Looking Glass Host (" BUILD_VERSION ")");
|
DEBUG_INFO("Looking Glass Host (" BUILD_VERSION ")");
|
||||||
DEBUG_INFO("IVSHMEM Size : %u MiB", shmemSize / 1048576);
|
|
||||||
if (!os_shmemMmap((void **)&shmemMap) || !shmemMap)
|
struct IVSHMEM shmDev;
|
||||||
|
if (!ivshmemOpen(&shmDev))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to map the shared memory");
|
DEBUG_ERROR("Failed to open the IVSHMEM device");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
DEBUG_INFO("IVSHMEM Address : 0x%" PRIXPTR, (uintptr_t)shmemMap);
|
|
||||||
|
|
||||||
app.shmHeader = (KVMFRHeader *)shmemMap;
|
int exitcode = 0;
|
||||||
app.pointerData = (uint8_t *)ALIGN_UP(shmemMap + sizeof(KVMFRHeader));
|
DEBUG_INFO("IVSHMEM Size : %u MiB", shmDev.size / 1048576);
|
||||||
|
DEBUG_INFO("IVSHMEM Address : 0x%" PRIXPTR, (uintptr_t)shmDev.mem);
|
||||||
|
|
||||||
|
app.shmHeader = (KVMFRHeader *)shmDev.mem;
|
||||||
|
app.pointerData = (uint8_t *)ALIGN_UP(shmDev.mem + sizeof(KVMFRHeader));
|
||||||
app.pointerDataSize = 1048576; // 1MB fixed for pointer size, should be more then enough
|
app.pointerDataSize = 1048576; // 1MB fixed for pointer size, should be more then enough
|
||||||
app.pointerOffset = app.pointerData - shmemMap;
|
app.pointerOffset = app.pointerData - (uint8_t*)shmDev.mem;
|
||||||
app.frames = (uint8_t *)ALIGN_UP(app.pointerData + app.pointerDataSize);
|
app.frames = (uint8_t *)ALIGN_UP(app.pointerData + app.pointerDataSize);
|
||||||
app.frameSize = ALIGN_DN((shmemSize - (app.frames - shmemMap)) / MAX_FRAMES);
|
app.frameSize = ALIGN_DN((shmDev.size - (app.frames - (uint8_t*)shmDev.mem)) / MAX_FRAMES);
|
||||||
|
|
||||||
DEBUG_INFO("Max Cursor Size : %u MiB", app.pointerDataSize / 1048576);
|
DEBUG_INFO("Max Cursor Size : %u MiB", app.pointerDataSize / 1048576);
|
||||||
DEBUG_INFO("Max Frame Size : %u MiB", app.frameSize / 1048576);
|
DEBUG_INFO("Max Frame Size : %u MiB", app.frameSize / 1048576);
|
||||||
@ -377,7 +380,7 @@ int app_main(int argc, char * argv[])
|
|||||||
for (int i = 0; i < MAX_FRAMES; ++i)
|
for (int i = 0; i < MAX_FRAMES; ++i)
|
||||||
{
|
{
|
||||||
app.frame [i] = (FrameBuffer)(app.frames + i * app.frameSize);
|
app.frame [i] = (FrameBuffer)(app.frames + i * app.frameSize);
|
||||||
app.frameOffset[i] = (uint8_t *)app.frame[i] - shmemMap;
|
app.frameOffset[i] = (uint8_t *)app.frame[i] - (uint8_t*)shmDev.mem;
|
||||||
DEBUG_INFO("Frame %d : 0x%" PRIXPTR " (0x%08x)", i, (uintptr_t)app.frame[i], app.frameOffset[i]);
|
DEBUG_INFO("Frame %d : 0x%" PRIXPTR " (0x%08x)", i, (uintptr_t)app.frame[i], app.frameOffset[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,7 +475,7 @@ exit:
|
|||||||
iface->deinit();
|
iface->deinit();
|
||||||
iface->free();
|
iface->free();
|
||||||
fail:
|
fail:
|
||||||
os_shmemUnmap();
|
ivshmemClose(&shmDev);
|
||||||
return exitcode;
|
return exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
common/include/common/ivshmem.h
Normal file
35
common/include/common/ivshmem.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||||
|
Copyright (C) 2017-2020 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
https://looking-glass.hostfission.com
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||||
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct IVSHMEM
|
||||||
|
{
|
||||||
|
unsigned int size;
|
||||||
|
void * mem;
|
||||||
|
|
||||||
|
// internal use
|
||||||
|
void * opaque;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ivshmemOptionsInit();
|
||||||
|
bool ivshmemOpen(struct IVSHMEM * dev);
|
||||||
|
void ivshmemClose(struct IVSHMEM * dev);
|
@ -10,6 +10,7 @@ add_library(lg_common_platform_code STATIC
|
|||||||
sysinfo.c
|
sysinfo.c
|
||||||
thread.c
|
thread.c
|
||||||
event.c
|
event.c
|
||||||
|
ivshmem.c
|
||||||
)
|
)
|
||||||
|
|
||||||
if(ENABLE_BACKTRACE)
|
if(ENABLE_BACKTRACE)
|
||||||
|
227
common/src/platform/linux/ivshmem.c
Normal file
227
common/src/platform/linux/ivshmem.c
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
/*
|
||||||
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||||
|
Copyright (C) 2017-2020 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
https://looking-glass.hostfission.com
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||||
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/ivshmem.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/option.h"
|
||||||
|
#include "common/stringutils.h"
|
||||||
|
|
||||||
|
struct IVSHMEMInfo
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int uioOpenFile(const char * shmDevice, const char * file)
|
||||||
|
{
|
||||||
|
char * path;
|
||||||
|
alloc_sprintf(&path, "/sys/class/uio/%s/%s", shmDevice, file);
|
||||||
|
|
||||||
|
int fd = open(path, O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
free(path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char * uioGetName(const char * shmDevice)
|
||||||
|
{
|
||||||
|
int fd = uioOpenFile(shmDevice, "name");
|
||||||
|
if (fd < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char * name = malloc(32);
|
||||||
|
int len = read(fd, name, 31);
|
||||||
|
if (len <= 0)
|
||||||
|
{
|
||||||
|
free(name);
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
name[len] = '\0';
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
while(len > 0 && name[len-1] == '\n')
|
||||||
|
{
|
||||||
|
--len;
|
||||||
|
name[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ivshmemDeviceValidator(struct Option * opt, const char ** error)
|
||||||
|
{
|
||||||
|
char * name = uioGetName(opt->value.x_string);
|
||||||
|
if (!name)
|
||||||
|
{
|
||||||
|
*error = "Failed to get the uio device name";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(name, "KVMFR") != 0)
|
||||||
|
{
|
||||||
|
free(name);
|
||||||
|
*error = "Device is not a KVMFR device";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static StringList ivshmemDeviceGetValues(struct Option * option)
|
||||||
|
{
|
||||||
|
StringList sl = stringlist_new(true);
|
||||||
|
|
||||||
|
DIR * d = opendir("/sys/class/uio");
|
||||||
|
if (!d)
|
||||||
|
return sl;
|
||||||
|
|
||||||
|
struct dirent * dir;
|
||||||
|
while((dir = readdir(d)) != NULL)
|
||||||
|
{
|
||||||
|
if (dir->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char * name = uioGetName(dir->d_name);
|
||||||
|
if (!name)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strcmp(name, "KVMFR") == 0)
|
||||||
|
stringlist_push(sl, strdup(dir->d_name));
|
||||||
|
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(d);
|
||||||
|
return sl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ivshmemOptionsInit()
|
||||||
|
{
|
||||||
|
struct Option options[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.module = "os",
|
||||||
|
.name = "shmDevice",
|
||||||
|
.description = "The IVSHMEM device to use",
|
||||||
|
.type = OPTION_TYPE_STRING,
|
||||||
|
.value.x_string = "uio0",
|
||||||
|
.validator = ivshmemDeviceValidator,
|
||||||
|
.getValues = ivshmemDeviceGetValues
|
||||||
|
},
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
|
||||||
|
option_register(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ivshmemOpen(struct IVSHMEM * dev)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
|
||||||
|
const char * shmDevice = option_get_string("os", "shmDevice");
|
||||||
|
unsigned int devSize;
|
||||||
|
int devFD;
|
||||||
|
|
||||||
|
DEBUG_INFO("KVMFR Device : %s", shmDevice);
|
||||||
|
|
||||||
|
{
|
||||||
|
// get the device size
|
||||||
|
int fd = uioOpenFile(shmDevice, "maps/map0/size");
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to open %s/size", shmDevice);
|
||||||
|
DEBUG_ERROR("Did you remmeber to modprobe the kvmfr module?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char size[32];
|
||||||
|
int len = read(fd, size, sizeof(size) - 1);
|
||||||
|
if (len <= 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to read the device size");
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size[len] = '\0';
|
||||||
|
close(fd);
|
||||||
|
devSize = strtoul(size, NULL, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char * path;
|
||||||
|
alloc_sprintf(&path, "/dev/%s", shmDevice);
|
||||||
|
devFD = open(path, O_RDWR, (mode_t)0600);
|
||||||
|
if (devFD < 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to open: %s", path);
|
||||||
|
DEBUG_ERROR("Did you remmeber to modprobe the kvmfr module?");
|
||||||
|
free(path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void * map = mmap(0, devSize, PROT_READ | PROT_WRITE, MAP_SHARED, devFD, 0);
|
||||||
|
if (map == MAP_FAILED)
|
||||||
|
{
|
||||||
|
const char * shmDevice = option_get_string("os", "shmDevice");
|
||||||
|
DEBUG_ERROR("Failed to map the shared memory device: %s", shmDevice);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IVSHMEMInfo * info =
|
||||||
|
(struct IVSHMEMInfo *)malloc(sizeof(struct IVSHMEMInfo));
|
||||||
|
info->size = devSize;
|
||||||
|
info->fd = devFD;
|
||||||
|
|
||||||
|
dev->opaque = info;
|
||||||
|
dev->size = devSize;
|
||||||
|
dev->mem = map;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ivshmemClose(struct IVSHMEM * dev)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
|
||||||
|
struct IVSHMEMInfo * info =
|
||||||
|
(struct IVSHMEMInfo *)dev->opaque;
|
||||||
|
|
||||||
|
munmap(dev->mem, info->size);
|
||||||
|
close(info->fd);
|
||||||
|
|
||||||
|
free(info);
|
||||||
|
}
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
|
|||||||
project(lg_common_platform_code LANGUAGES C)
|
project(lg_common_platform_code LANGUAGES C)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${PROJECT_SOURCE_DIR}/include
|
${PROJECT_TOP}/vendor/ivshmem
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(lg_common_platform_code STATIC
|
add_library(lg_common_platform_code STATIC
|
||||||
@ -11,4 +11,5 @@ add_library(lg_common_platform_code STATIC
|
|||||||
thread.c
|
thread.c
|
||||||
event.c
|
event.c
|
||||||
windebug.c
|
windebug.c
|
||||||
|
ivshmem.c
|
||||||
)
|
)
|
||||||
|
153
common/src/platform/windows/ivshmem.c
Normal file
153
common/src/platform/windows/ivshmem.c
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||||
|
Copyright (C) 2017-2020 Geoffrey McRae <geoff@hostfission.com>
|
||||||
|
https://looking-glass.hostfission.com
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free Software
|
||||||
|
Foundation; either version 2 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||||
|
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/ivshmem.h"
|
||||||
|
#include "common/option.h"
|
||||||
|
#include "common/windebug.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include "ivshmem.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <setupapi.h>
|
||||||
|
#include <io.h>
|
||||||
|
|
||||||
|
struct IVSHMEMInfo
|
||||||
|
{
|
||||||
|
HANDLE handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ivshmemOptionsInit()
|
||||||
|
{
|
||||||
|
static struct Option options[] = {
|
||||||
|
{
|
||||||
|
.module = "os",
|
||||||
|
.name = "shmDevice",
|
||||||
|
.description = "The IVSHMEM device to use",
|
||||||
|
.type = OPTION_TYPE_INT,
|
||||||
|
.value.x_int = 0
|
||||||
|
},
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
|
||||||
|
option_register(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ivshmemOpen(struct IVSHMEM * dev)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
|
||||||
|
HANDLE devHandle;
|
||||||
|
|
||||||
|
{
|
||||||
|
HDEVINFO devInfoSet;
|
||||||
|
PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL;
|
||||||
|
SP_DEVICE_INTERFACE_DATA devInterfaceData = {0};
|
||||||
|
|
||||||
|
devInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
|
||||||
|
devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||||
|
|
||||||
|
const int shmDevice = option_get_int("os", "shmDevice");
|
||||||
|
if (SetupDiEnumDeviceInterfaces(devInfoSet, NULL, &GUID_DEVINTERFACE_IVSHMEM, shmDevice, &devInterfaceData) == FALSE)
|
||||||
|
{
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
if (error == ERROR_NO_MORE_ITEMS)
|
||||||
|
{
|
||||||
|
DEBUG_WINERROR("Unable to enumerate the device, is it attached?", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD reqSize = 0;
|
||||||
|
SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, NULL, 0, &reqSize, NULL);
|
||||||
|
if (!reqSize)
|
||||||
|
{
|
||||||
|
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)calloc(reqSize, 1);
|
||||||
|
infData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||||
|
if (!SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, infData, reqSize, NULL, NULL))
|
||||||
|
{
|
||||||
|
free(infData);
|
||||||
|
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
devHandle = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0);
|
||||||
|
if (devHandle == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||||
|
free(infData);
|
||||||
|
DEBUG_WINERROR("CreateFile returned INVALID_HANDLE_VALUE", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(infData);
|
||||||
|
SetupDiDestroyDeviceInfoList(devInfoSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
IVSHMEM_SIZE size;
|
||||||
|
if (!DeviceIoControl(devHandle, IOCTL_IVSHMEM_REQUEST_SIZE, NULL, 0, &size, sizeof(IVSHMEM_SIZE), NULL, NULL))
|
||||||
|
{
|
||||||
|
DEBUG_WINERROR("DeviceIoControl Failed", GetLastError());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IVSHMEM_MMAP_CONFIG config = { .cacheMode = IVSHMEM_CACHE_WRITECOMBINED };
|
||||||
|
IVSHMEM_MMAP map = { 0 };
|
||||||
|
if (!DeviceIoControl(
|
||||||
|
devHandle,
|
||||||
|
IOCTL_IVSHMEM_REQUEST_MMAP,
|
||||||
|
&config, sizeof(IVSHMEM_MMAP_CONFIG),
|
||||||
|
&map , sizeof(IVSHMEM_MMAP),
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
DEBUG_WINERROR("DeviceIoControl Failed", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IVSHMEMInfo * info =
|
||||||
|
(struct IVSHMEMInfo *)malloc(sizeof(struct IVSHMEMInfo));
|
||||||
|
|
||||||
|
info->handle = devHandle;
|
||||||
|
|
||||||
|
dev->opaque = info;
|
||||||
|
dev->size = (unsigned int)size;
|
||||||
|
dev->mem = map.ptr;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ivshmemClose(struct IVSHMEM * dev)
|
||||||
|
{
|
||||||
|
assert(dev);
|
||||||
|
|
||||||
|
struct IVSHMEMInfo * info =
|
||||||
|
(struct IVSHMEMInfo *)dev->opaque;
|
||||||
|
|
||||||
|
if (!DeviceIoControl(info->handle, IOCTL_IVSHMEM_RELEASE_MMAP, NULL, 0, NULL, 0, NULL, NULL))
|
||||||
|
DEBUG_WINERROR("DeviceIoControl failed", GetLastError());
|
||||||
|
|
||||||
|
free(info);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user