Add vkm library

virtual-keyboard-mouse
nirenjan 2026-03-19 17:42:45 -07:00
parent 2bc59ae6f0
commit ae57db0db9
7 changed files with 866 additions and 1 deletions

View File

@ -8,7 +8,12 @@ if not dep_hidapi.found()
dep_hidapi = dependency('hidapi', required: true)
endif
dep_evdev = dependency('libevdev', required: false)
if host_machine.system() == 'linux'
dep_evdev = dependency('libevdev', required: false)
else
# Create a dummy dependency
dep_evdev = dependency('', required: false)
endif
dep_systemd = dependency('systemd', required: false)
dep_udev = dependency('udev', required: false)
@ -110,6 +115,7 @@ includes = include_directories('.', 'libx52', 'libx52io', 'libx52util')
subdir('libx52')
subdir('libx52io')
subdir('libx52util')
subdir('vkm')
subdir('bugreport')
subdir('cli')
subdir('joytest')

35
vkm/meson.build 100644
View File

@ -0,0 +1,35 @@
vkm_version = '0.1.0'
vkm_files = files(
'vkm_common.c'
)
vkm_stub_files = files(
'vkm_stub.c'
)
if host_machine.system() == 'linux'
vkm_dep = dep_evdev
if dep_evdev.found()
vkm_platform_files = files(
'vkm_linux_evdev.c'
)
else
vkm_platform_files = vkm_stub_files
endif
else
vkm_platform_files = vkm_stub_files
endif
lib_vkm = library('vkm', vkm_files + vkm_platform_files,
install: true,
version: vkm_version,
dependencies: [vkm_dep],
include_directories: [includes])
install_headers('vkm.h', subdir: 'vkm')
pkgconfig.generate(lib_vkm,
name: 'vkm',
description: 'Linux/Unix library to control Saitek X52/X52Pro joystick extended functionality.',
subdirs: meson.project_name(),
version: vkm_version)

22
vkm/vkm-internal.h 100644
View File

@ -0,0 +1,22 @@
/*
* VKM common functions
*
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
*
* SPDX-LicenseIdentifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include <stdbool.h>
#include "vkm.h"
#ifndef VKM_INTERNAL_H
#define VKM_INTERNAL_H
struct vkm_mouse_button_state {
vkm_button_state pressed[VKM_MOUSE_BTN_MAX];
};
vkm_button_state _vkm_get_mouse_button_state(struct vkm_mouse_button_state *state, vkm_mouse_button button);
void _vkm_set_mouse_button_state(struct vkm_mouse_button_state *sstate, vkm_mouse_button button, vkm_button_state state);
#endif // !defined VKM_INTERNAL_H

273
vkm/vkm.h
View File

@ -21,6 +21,7 @@
#define VKM_H
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
@ -45,6 +46,149 @@ typedef struct vkm_context vkm_context;
*/
typedef int32_t vkm_result;
/**
* @brief Feature identifiers
*
* These are used to check for platform support of the relevant feature.
*/
typedef enum {
/** Check if mouse is supported on this platform */
VKM_FEAT_MOUSE = (1 << 0),
/** Check if US keyboard is supported on this platform */
VKM_FEAT_KEYBOARD_US = (1 << 1),
/* Additional flags may be added in the future */
} vkm_feature;
/**
* @brief Error code list
*/
typedef enum {
/** No error, indicates success */
VKM_SUCCESS = 0,
/** Unknown error, used as a catch-all error state */
VKM_ERROR_UNKNOWN,
/** Not started, must call vkm_start first */
VKM_ERROR_NOT_READY,
/** Out of memory */
VKM_ERROR_OUT_OF_MEMORY,
/** Invalid parameter(s) */
VKM_ERROR_INVALID_PARAM,
/** Not supported */
VKM_ERROR_NOT_SUPPORTED,
/** Unable to create virtual devices */
VKM_ERROR_DEV_FAILURE,
/** Unable to write event */
VKM_ERROR_EVENT,
/** No state change in the event, please retry */
VKM_ERROR_NO_CHANGE,
/* Maximum error states, do not use in external code*/
VKM_ERROR_MAX,
} vkm_error_code;
/**
* @brief Option list
*/
typedef enum {
/**
* @brief Set the high resolution scrolling behavior of the mouse
*
* This option must be passed a boolean which lets VKM know whether to
* enable or disable high resolution scrolling.
*
* Defaults to true.
*/
VKM_OPT_HI_RES_SCROLL,
/**
* @brief Enable or disable horizontal scrolling of the mouse
*
* This option must be passed in a boolean which lets VKM know whether to
* enable or disable horizontal scrolling. If horizontal scrolling is
* disabled, then any requests to \ref vkm_mouse_scroll with
* \ref VKM_MOUSE_SCROLL_LEFT or \ref VKM_MOUSE_SCROLL_RIGHT will return
* \ref VKM_ERROR_INVALID_PARAM.
*
* Defaults to false.
*/
VKM_OPT_HORIZONTAL_SCROLL,
/**
* @brief Set the virtual device name in the system.
*
* This option sets the name of the virtual input device in the system.
* If not set, the virtual device will have a name determined by the
* timestamp at which it was initialized.
*
* Only applicable on Linux.
*/
VKM_OPT_DEVICE_NAME,
/* Max number of options, do not use in external code */
VKM_OPT_MAX
} vkm_option;
/**
* @brief Button state
*/
typedef enum {
/** Button is released */
VKM_BUTTON_RELEASED,
/** Button is pressed */
VKM_BUTTON_PRESSED,
/* Max number of button states, do not use in external code */
VKM_BUTTON_STATE_MAX
} vkm_button_state;
/**
* @brief Mouse button identifiers
*/
typedef enum {
/** Mouse left button */
VKM_MOUSE_BTN_LEFT,
/** Mouse right button */
VKM_MOUSE_BTN_RIGHT,
/** Mouse middle button */
VKM_MOUSE_BTN_MIDDLE,
/* Max number of mouse buttons, do not use in external code */
VKM_MOUSE_BTN_MAX
} vkm_mouse_button;
/**
* @brief Scroll directions
*/
typedef enum {
/** Scroll up */
VKM_MOUSE_SCROLL_UP,
/** Scroll down */
VKM_MOUSE_SCROLL_DOWN,
/** Scroll left (horizontal scrolling) */
VKM_MOUSE_SCROLL_LEFT,
/** Scroll right (horizontal scrolling) */
VKM_MOUSE_SCROLL_RIGHT,
/* Maximum number of scroll states, do not use in external code */
VKM_MOUSE_SCROLL_MAX
} vkm_mouse_scroll_direction;
/**
* @brief Initialize the VKM library
*
@ -82,6 +226,135 @@ vkm_result vkm_init(vkm_context **ctx);
*/
void vkm_exit(vkm_context *ctx);
/**
* @brief Start any virtual keyboard/mouse devices on the platform
*
* This must be done before injecting any events, and after setting all
* options through \ref vkm_set_option.
*
* @param[in] ctx Context pointer
*
* @returns
* - \ref VKM_SUCCESS on successful start
* - \ref VKM_ERROR_INVALID_PARAM on bad pointer
* - \ref VKM_ERROR_UNKNOWN on other errors
*/
vkm_result vkm_start(vkm_context *ctx);
/**
* @brief check if VKM is started and ready
*
* @param[in] ctx Context pointer
*
* @returns boolean indicating if ready or not.
*/
bool vkm_is_ready(vkm_context *ctx);
/**
* @brief Check if VKM is supported on this platform
*
* On some platforms, there is no support yet for the virtual keyboard/mouse.
* This function will return a boolean indicating if it is supported or not.
*
* @returns boolean indicating support.
*/
bool vkm_platform_supported(void);
/**
* @brief Check if a particular feature is enabled on this platform
*
* Features may be limited on a per-platform basis.
*
* @param[in] feat Feature identifier
*
* @returns boolean indicating if feature is supported or not.
*/
bool vkm_feature_supported(vkm_feature feat);
/**
* @brief Set an option flag for VKM.
*
* Option flags control the behavior of VKM. All options must be set before
* calling \ref vkm_start.
*
* @param[in] ctx Context pointer
* @param[in] option Which option to set
* @param[in] ... Any required arguments for the specified option
*
* @returns
* - \ref VKM_SUCCESS on success
* - \ref VKM_ERROR_INVALID_PARAM if the option or arguments are invalid
* - \ref VKM_ERROR_NOT_SUPPORTED if the option is valid but not supported on this platform
*/
vkm_result vkm_set_option(vkm_context *ctx, vkm_option option, ...);
/**
* @brief Move the mouse by the specified amount
*
* The move mouse takes in a delta of x and y coordinates that tell the system
* to move the mouse by those relative numbers.
*
* @param[in] ctx Context pointer
* @param[in] dx Delta by which to move the mouse in the horizontal axis
* @param[in] dy Delta by which to move the mouse in the vertical axis
*
* @returns
* - \ref VKM_SUCCESS on successful move
* - \ref VKM_ERROR_UNKNOWN on a generic error
* - \ref VKM_ERROR_NOT_SUPPORTED if the mouse move is not supported on this platform
*/
vkm_result vkm_mouse_move(vkm_context *ctx, int dx, int dy);
/**
* @brief Click the mouse button
*
* Send a mouse button event, this may be either a button down or button up event.
*
* @param[in] ctx Context pointer
* @param[in] button Button identifier
* @param[in] state Button state (press or release)
*
* @returns
* - \ref VKM_SUCCESS on successful move
* - \ref VKM_ERROR_UNKNOWN on a generic error
* - \ref VKM_ERROR_NOT_SUPPORTED if the mouse button click is not supported on this platform
*/
vkm_result vkm_mouse_click(vkm_context *ctx, vkm_mouse_button button, vkm_button_state state);
/**
* @brief Scroll the mouse wheel
*
* Send a single scroll event to the mouse wheel
*
* @param[in] ctx Context pointer
* @param[in] dir Scroll direction
* @param[in] state Button state (press or release)
*
* @returns
* - \ref VKM_SUCCESS on successful move
* - \ref VKM_ERROR_UNKNOWN on a generic error
* - \ref VKM_ERROR_INVALID_PARAM if horizontal scrolling is not enabled
* and \p dir is \ref VKM_MOUSE_SCROLL_LEFT or \ref VKM_MOUSE_SCROLL_RIGHT
* - \ref VKM_ERROR_NOT_SUPPORTED if the mouse scrolling is not supported on this platform
*/
vkm_result vkm_mouse_scroll(vkm_context *ctx, vkm_mouse_scroll_direction dir);
/**
* @brief Send a sync packet to the OS
*
* On some platforms, a sync packet is necessary for the previously injected
* events to actually get reflected in the system. For platforms where this
* is not needed, this is a noop.
*
* @param[in] ctx Context pointer
*
* @returns
* - \ref VKM_SUCCESS on successful move
* - \ref VKM_ERROR_UNKNOWN on a generic error
* - \ref VKM_ERROR_INVALID_PARAM if parameters are invalid
*/
vkm_result vkm_sync(vkm_context *ctx);
#ifdef __cplusplus
}
#endif

27
vkm/vkm_common.c 100644
View File

@ -0,0 +1,27 @@
/*
* VKM common functions
*
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
*
* SPDX-LicenseIdentifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include <stdlib.h>
#include "vkm-internal.h"
vkm_button_state _vkm_get_mouse_button_state(struct vkm_mouse_button_state *state, vkm_mouse_button button)
{
if (state == NULL) {
return false;
}
return state->pressed[button];
}
void _vkm_set_mouse_button_state(struct vkm_mouse_button_state *sstate, vkm_mouse_button button, vkm_button_state state)
{
if (sstate != NULL) {
sstate->pressed[button] = state;
}
}

View File

@ -0,0 +1,373 @@
/*
* VKM Linux evdev implementation
*
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
*
* SPDX-LicenseIdentifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <inttypes.h>
#include "libevdev/libevdev.h"
#include "libevdev/libevdev-uinput.h"
#include "vkm-internal.h"
struct vkm_context {
struct libevdev *dev;
struct libevdev_uinput *uidev;
char *name;
bool mouse_hi_res_scroll;
bool mouse_horizontal_scroll;
// Existing state of buttons
struct vkm_mouse_button_state mouse_buttons;
};
static const int mouse_button_map[VKM_MOUSE_BTN_MAX] = {
[VKM_MOUSE_BTN_LEFT] = BTN_LEFT,
[VKM_MOUSE_BTN_RIGHT] = BTN_RIGHT,
[VKM_MOUSE_BTN_MIDDLE] = BTN_MIDDLE,
};
static const int button_value_map[VKM_BUTTON_STATE_MAX] = {
[VKM_BUTTON_PRESSED] = 1,
[VKM_BUTTON_RELEASED] = 0,
};
vkm_result vkm_init(vkm_context **ctx)
{
char name_buf[32];
char *name;
vkm_result rc;
if (ctx == NULL) {
return VKM_ERROR_INVALID_PARAM;
}
*ctx = calloc(1, sizeof(*ctx));
if (*ctx == NULL) {
return VKM_ERROR_OUT_OF_MEMORY;
}
rc = VKM_SUCCESS;
// Create a default name for the virtual device, this will be replaced when
// the user calls vkm_set_option with the appropriate name field.
snprintf(name_buf, sizeof(name_buf), "VKM virtual device @%08" PRIx64,
(uint64_t)(time(NULL)));
name = strdup(name_buf);
if (name == NULL) {
rc = VKM_ERROR_OUT_OF_MEMORY;
goto error;
}
(*ctx)->name = name;
// Set defaults for the flags
error:
if (name) {
free(name);
}
free(*ctx);
*ctx = NULL;
return rc;
}
void vkm_exit(vkm_context *ctx)
{
char *name;
struct libevdev_uinput *uidev;
struct libevdev *dev;
if (ctx == NULL) {
return;
}
dev = ctx->dev;
uidev = ctx->uidev;
name = ctx->name;
memset(ctx, 0, sizeof(*ctx));
free(name);
if (uidev) {
libevdev_uinput_destroy(uidev);
}
if (dev) {
libevdev_free(dev);
}
free(ctx);
}
// Enable mouse events
static void enable_mouse_events(vkm_context *ctx)
{
libevdev_enable_event_type(ctx->dev, EV_REL);
libevdev_enable_event_code(ctx->dev, EV_REL, REL_X, NULL);
libevdev_enable_event_code(ctx->dev, EV_REL, REL_Y, NULL);
libevdev_enable_event_code(ctx->dev, EV_REL, REL_WHEEL, NULL);
if (ctx->mouse_hi_res_scroll) {
libevdev_enable_event_code(ctx->dev, EV_REL, REL_WHEEL_HI_RES, NULL);
}
if (ctx->mouse_horizontal_scroll) {
libevdev_enable_event_code(ctx->dev, EV_REL, REL_HWHEEL, NULL);
if (ctx->mouse_hi_res_scroll) {
libevdev_enable_event_code(ctx->dev, EV_REL, REL_HWHEEL_HI_RES, NULL);
}
}
libevdev_enable_event_type(ctx->dev, EV_KEY);
libevdev_enable_event_code(ctx->dev, EV_KEY, BTN_LEFT, NULL);
libevdev_enable_event_code(ctx->dev, EV_KEY, BTN_RIGHT, NULL);
libevdev_enable_event_code(ctx->dev, EV_KEY, BTN_MIDDLE, NULL);
}
vkm_result vkm_start(vkm_context *ctx)
{
int rc;
if (ctx == NULL) {
return VKM_ERROR_INVALID_PARAM;
}
if (ctx->uidev) {
// Already initialized, no need to do anything else
return VKM_SUCCESS;
}
if (!(ctx->dev)) {
// This will happen on the first time this function is called, but
// will get cleared up if the uinput device is not created, so this
// is just a safety check.
ctx->dev = libevdev_new();
if (ctx->dev == NULL) {
return VKM_ERROR_DEV_FAILURE;
}
libevdev_set_name(ctx->dev, ctx->name);
enable_mouse_events(ctx);
}
rc = libevdev_uinput_create_from_device(ctx->dev, LIBEVDEV_UINPUT_OPEN_MANAGED,
&(ctx->uidev));
if (rc != 0) {
goto error;
}
return VKM_SUCCESS;
error:
libevdev_free(ctx->dev);
ctx->dev = NULL;
return VKM_ERROR_DEV_FAILURE;
}
bool vkm_is_ready(vkm_context *ctx) {
if (ctx == NULL) {
return false;
}
return (ctx->dev != NULL && ctx->uidev != NULL);
}
bool vkm_platform_supported(void)
{
return true;
}
bool vkm_feature_supported(vkm_feature feat)
{
switch (feat) {
case VKM_FEAT_MOUSE:
return true;
case VKM_FEAT_KEYBOARD_US:
default:
return false;
}
return false;
}
vkm_result vkm_set_option(vkm_context *ctx, vkm_option option, ...)
{
va_list ap;
bool flag;
char *name;
char *name2;
vkm_result rc;
if (ctx == NULL) {
return VKM_ERROR_INVALID_PARAM;
}
va_start(ap, option);
rc = VKM_SUCCESS;
switch (option) {
case VKM_OPT_HI_RES_SCROLL:
// read as int, as bool is promoted to int when passed as a va_arg
flag = va_arg(ap, int);
ctx->mouse_hi_res_scroll = (bool)flag;
break;
case VKM_OPT_HORIZONTAL_SCROLL:
// read as int, as bool is promoted to int when passed as a va_arg
flag = va_arg(ap, int);
ctx->mouse_horizontal_scroll = (bool)flag;
break;
case VKM_OPT_DEVICE_NAME:
name = va_arg(ap, char *);
name2 = strdup(name);
if (name2 == NULL) {
rc = VKM_ERROR_OUT_OF_MEMORY;
} else {
free(ctx->name);
ctx->name = name2;
}
break;
default:
rc = VKM_ERROR_INVALID_PARAM;
break;
}
va_end(ap);
return rc;
}
vkm_result vkm_mouse_move(vkm_context *ctx, int dx, int dy)
{
int rc;
if (ctx == NULL) {
return VKM_ERROR_INVALID_PARAM;
}
if (!vkm_is_ready(ctx)) {
return VKM_ERROR_NOT_READY;
}
if (!dx && !dy) {
return VKM_ERROR_NO_CHANGE;
}
if (dx) {
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, REL_X, dx);
if (rc != 0) {
return VKM_ERROR_EVENT;
}
}
if (dy) {
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, REL_X, dx);
if (rc != 0) {
return VKM_ERROR_EVENT;
}
}
return VKM_SUCCESS;
}
vkm_result vkm_mouse_click(vkm_context *ctx, vkm_mouse_button button, vkm_button_state state)
{
int rc;
vkm_button_state existing;
if (ctx == NULL) {
return VKM_ERROR_INVALID_PARAM;
}
if (button >= VKM_MOUSE_BTN_MAX) {
return VKM_ERROR_INVALID_PARAM;
}
if (state >= VKM_BUTTON_STATE_MAX) {
return VKM_ERROR_INVALID_PARAM;
}
if (!vkm_is_ready(ctx)) {
return VKM_ERROR_NOT_READY;
}
existing = _vkm_get_mouse_button_state(&(ctx->mouse_buttons), button);
if (existing == state) {
return VKM_ERROR_NO_CHANGE;
}
rc = libevdev_uinput_write_event(ctx->uidev, EV_KEY,
mouse_button_map[button], button_value_map[state]);
if (rc != 0) {
return VKM_ERROR_EVENT;
}
return VKM_SUCCESS;
}
vkm_result vkm_mouse_scroll(vkm_context *ctx, vkm_mouse_scroll_direction dir)
{
int axis;
int val;
int rc;
if (ctx == NULL) {
return VKM_ERROR_INVALID_PARAM;
}
if (dir >= VKM_MOUSE_SCROLL_MAX) {
return VKM_ERROR_INVALID_PARAM;
}
if (!vkm_is_ready(ctx)) {
return VKM_ERROR_NOT_READY;
}
if (dir == VKM_MOUSE_SCROLL_LEFT || dir == VKM_MOUSE_SCROLL_RIGHT) {
if (!ctx->mouse_horizontal_scroll) {
return VKM_ERROR_NOT_SUPPORTED;
}
axis = REL_HWHEEL;
val = (dir == VKM_MOUSE_SCROLL_LEFT) ? -1 : +1;
} else {
axis = REL_WHEEL;
val = (dir == VKM_MOUSE_SCROLL_UP) ? +1 : -1;
}
if (val == 0) {
return VKM_ERROR_NO_CHANGE;
}
rc = libevdev_uinput_write_event(ctx->uidev, EV_REL, axis, val);
if (rc != 0) {
return VKM_ERROR_EVENT;
}
return VKM_SUCCESS;
}
vkm_result vkm_sync(vkm_context *ctx)
{
int rc;
if (ctx == NULL) {
return VKM_ERROR_INVALID_PARAM;
}
if (!vkm_is_ready(ctx)) {
return VKM_ERROR_NOT_READY;
}
rc = libevdev_uinput_write_event(ctx->uidev, EV_SYN, SYN_REPORT, 0);
if (rc != 0) {
return VKM_ERROR_EVENT;
}
return VKM_SUCCESS;
}

129
vkm/vkm_stub.c 100644
View File

@ -0,0 +1,129 @@
/*
* VKM stub implementation
*
* Copyright (C) 2026 Nirenjan Krishnan <nirenjan@nirenjan.org>
*
* SPDX-LicenseIdentifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include <stdlib.h>
#include <stdarg.h>
#include "vkm.h"
// Dummy struct
struct vkm_context {
};
#define DUMMY_CTX (vkm_context *)0xDEADBEEF
vkm_result vkm_init(vkm_context **ctx)
{
if (ctx != NULL) {
*ctx = DUMMY_CTX;
return VKM_SUCCESS;
}
return VKM_ERROR_INVALID_PARAM;
}
void vkm_exit(vkm_context *ctx)
{
(void)ctx;
}
vkm_result vkm_start(vkm_context *ctx)
{
if (ctx != DUMMY_CTX) {
return VKM_ERROR_INVALID_PARAM;
}
return VKM_SUCCESS;
}
bool vkm_platform_supported(void)
{
return false;
}
bool vkm_feature_supported(vkm_feature feat)
{
(void)feat;
return false;
}
vkm_result vkm_set_option(vkm_context *ctx, vkm_option option, ...)
{
va_list ap;
bool flag;
char *name;
vkm_result rc;
if (ctx != DUMMY_CTX) {
return VKM_ERROR_INVALID_PARAM;
}
va_start(ap, option);
rc = VKM_SUCCESS;
switch (option) {
case VKM_OPT_HI_RES_SCROLL:
case VKM_OPT_HORIZONTAL_SCROLL:
// read as int, as bool is promoted to int when passed as a va_arg
flag = va_arg(ap, int);
(void)flag;
break;
case VKM_OPT_DEVICE_NAME:
name = va_arg(ap, char *);
(void)name;
break;
default:
rc = VKM_ERROR_INVALID_PARAM;
break;
}
va_end(ap);
return rc;
}
vkm_result vkm_mouse_move(vkm_context *ctx, int dx, int dy)
{
(void)dx;
(void)dy;
if (ctx != DUMMY_CTX) {
return VKM_ERROR_INVALID_PARAM;
}
return VKM_SUCCESS;
}
vkm_result vkm_mouse_click(vkm_context *ctx, vkm_mouse_button button, vkm_button_state state)
{
if (ctx != DUMMY_CTX) {
return VKM_ERROR_INVALID_PARAM;
}
if (button >= VKM_MOUSE_BTN_MAX) {
return VKM_ERROR_INVALID_PARAM;
}
if (state >= VKM_BUTTON_STATE_MAX) {
return VKM_ERROR_INVALID_PARAM;
}
return VKM_SUCCESS;
}
vkm_result vkm_mouse_scroll(vkm_context *ctx, vkm_mouse_scroll_direction dir)
{
if (ctx != DUMMY_CTX) {
return VKM_ERROR_INVALID_PARAM;
}
if (dir >= VKM_MOUSE_SCROLL_MAX) {
return VKM_ERROR_INVALID_PARAM;
}
return VKM_SUCCESS;
}