Code refactor

This commit is contained in:
Denis 2023-12-06 00:32:51 +01:00
parent 89f348f7c1
commit d6e6162db5
No known key found for this signature in database
GPG key ID: DD9B63F805CF5C03
21 changed files with 1342 additions and 1251 deletions

View file

@ -15,7 +15,7 @@ set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# Adding something we can run - Output name matches target name
add_executable(${EXECUTABLE_NAME} dev_iio.c input_dev.c logic.c main.c output_dev.c platform.c queue.c settings.c virt_ds4.c virt_ds5.c)
add_executable(${EXECUTABLE_NAME} dev_iio.c input_dev.c logic.c main.c output_dev.c platform.c queue.c settings.c virt_ds4.c virt_ds5.c virt_mouse_kbd.c virt_evdev.c devices_status.c)
target_link_libraries(${EXECUTABLE_NAME} PRIVATE Threads::Threads -levdev -ludev -lconfig)

View file

@ -2,7 +2,7 @@
CFLAGS= -O3 -march=znver4 -D _DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112L -std=c11 -fPIE -pedantic -Wall -flto=full # -Werror
LDFLAGS=-lpthread -levdev -ludev -lconfig -lrt -lm -flto=full
CC=clang
OBJECTS=main.o input_dev.o dev_iio.o output_dev.o queue.o logic.o platform.o settings.o virt_ds4.o virt_ds5.o
OBJECTS=main.o input_dev.o dev_iio.o output_dev.o queue.o logic.o platform.o settings.o virt_ds4.o virt_ds5.o virt_mouse_kbd.o virt_evdev.o devices_status.o
TARGET=rogue-enemy
all: $(TARGET)

5
TODO.md Normal file
View file

@ -0,0 +1,5 @@
# TODO
List of great ideas that are such only as soon as I don't implement them:
- In DualShock one can simulate touchpad press with back buttons
- DualShock and DualSense do send HID reports at 800Hz, so the iio sampling rate should be at least 1600Hz

45
devices_status.c Normal file
View file

@ -0,0 +1,45 @@
#include "devices_status.h"
void kbd_status_init(keyboard_status_t *const stats) {
stats->connected = true;
}
void gamepad_status_init(gamepad_status_t *const stats) {
stats->connected = true;
stats->joystick_positions[0][0] = 0;
stats->joystick_positions[0][1] = 0;
stats->joystick_positions[1][0] = 0;
stats->joystick_positions[1][1] = 0;
stats->dpad = 0x00;
stats->l2_trigger = 0;
stats->r2_trigger = 0;
stats->triangle = 0;
stats->circle = 0;
stats->cross = 0;
stats->square = 0;
stats->r3 = 0;
stats->r3 = 0;
stats->option = 0;
stats->share = 0;
stats->center = 0;
stats->r4 = 0;
stats->l4 = 0;
stats->r5 = 0;
stats->l5 = 0;
stats->motors_intensity[0] = 0;
stats->motors_intensity[1] = 0;
stats->rumble_events_count = 0;
stats->gyro[0] = 0;
stats->gyro[1] = 0;
stats->gyro[2] = 0;
stats->accel[0] = 0;
stats->accel[1] = 0;
stats->accel[2] = 0;
stats->flags = 0;
}
void devices_status_init(devices_status_t *const stats) {
gamepad_status_init(&stats->gamepad);
kbd_status_init(&stats->kbd);
// TODO: mouse init
}

73
devices_status.h Normal file
View file

@ -0,0 +1,73 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/time.h>
typedef struct gamepad_status {
bool connected;
int32_t joystick_positions[2][2]; // [0 left | 1 right][x axis | y axis]
uint8_t dpad; // 0x00 x - | 0x01 x -> | 0x02 x <- | 0x00 y - | 0x10 y ^ | 0x10 y . |
uint8_t l2_trigger;
uint8_t r2_trigger;
uint8_t triangle;
uint8_t circle;
uint8_t cross;
uint8_t square;
uint8_t l1;
uint8_t r1;
uint8_t r3;
uint8_t l3;
uint8_t option;
uint8_t share;
uint8_t center;
uint8_t l4;
uint8_t r4;
uint8_t l5;
uint8_t r5;
struct timeval last_gyro_motion_time;
struct timeval last_accel_motion_time;
double gyro[3]; // | x, y, z| right-hand-rules -- in rad/s
double accel[3]; // | x, y, z| positive: right, up, towards player -- in m/s^2
int16_t raw_gyro[3];
int16_t raw_accel[3];
uint64_t rumble_events_count;
uint8_t motors_intensity[2]; // 0 = left, 1 = right
volatile uint32_t flags;
} gamepad_status_t;
typedef struct keyboard_status {
bool connected;
} keyboard_status_t;
typedef struct devices_status {
// this mutex MUST be grabbed when reading and/or writing below properties
pthread_mutex_t mutex;
gamepad_status_t gamepad;
keyboard_status_t kbd;
} devices_status_t;
void kbd_status_init(keyboard_status_t *const stats);
void gamepad_status_init(gamepad_status_t *const stats);
void devices_status_init(devices_status_t *const stats);

140
logic.c
View file

@ -3,67 +3,44 @@
#include "queue.h"
#include "virt_ds4.h"
#include "virt_ds5.h"
#include "virt_evdev.h"
#include "virt_mouse_kbd.h"
static const char* configuration_file = "/etc/ROGueENEMY/config.cfg";
int logic_create(logic_t *const logic) {
int ret = 0;
logic->flags = 0x00000000U;
memset(logic->gamepad.joystick_positions, 0, sizeof(logic->gamepad.joystick_positions));
logic->gamepad.dpad = 0x00;
logic->gamepad.l2_trigger = 0;
logic->gamepad.r2_trigger = 0;
logic->gamepad.triangle = 0;
logic->gamepad.circle = 0;
logic->gamepad.cross = 0;
logic->gamepad.square = 0;
logic->gamepad.r3 = 0;
logic->gamepad.r3 = 0;
logic->gamepad.option = 0;
logic->gamepad.share = 0;
logic->gamepad.center = 0;
logic->gamepad.r4 = 0;
logic->gamepad.l4 = 0;
logic->gamepad.r5 = 0;
logic->gamepad.l5 = 0;
logic->gamepad.rumble_events_count = 0;
memset(logic->gamepad.gyro, 0, sizeof(logic->gamepad.gyro));
memset(logic->gamepad.accel, 0, sizeof(logic->gamepad.accel));
logic->gamepad.flags = 0;
init_config(&logic->controller_settings);
const int fill_config_res = fill_config(&logic->controller_settings, configuration_file);
if (fill_config_res != 0) {
fprintf(stderr, "Unable to fill configuration from file %s -- defaults will be used\n", configuration_file);
}
const int mutex_creation_res = pthread_mutex_init(&logic->gamepad_mutex, NULL);
if (mutex_creation_res != 0) {
fprintf(stderr, "Unable to create mutex: %d\n", mutex_creation_res);
return mutex_creation_res;
devices_status_init(&logic->dev_stats);
ret = queue_init(&logic->rumble_events_queue, 1);
if (ret != 0) {
fprintf(stderr, "Unable to create the rumble events queue: %d\n", ret);
goto logic_create_err;
}
ret = pthread_mutex_init(&logic->dev_stats.mutex, NULL);
if (ret != 0) {
fprintf(stderr, "Unable to create mutex: %d\n", ret);
goto logic_create_err;
}
const int queue_init_res = queue_init(&logic->input_queue, 128);
const int virt_ds4_thread_creation = pthread_create(&logic->virt_ds4_thread, NULL, virt_ds4_thread_func, (void*)(logic));
if (virt_ds4_thread_creation != 0) {
fprintf(stderr, "Error creating virtual DualShock4 thread: %d. Will use evdev as output.\n", virt_ds4_thread_creation);
logic->gamepad_output = GAMEPAD_OUTPUT_EVDEV;
} else {
printf("Creation of virtual DualShock4 succeeded: using it as the defaut output.\n");
logic->flags |= LOGIC_FLAGS_VIRT_DS4_ENABLE;
logic->gamepad_output = GAMEPAD_OUTPUT_DS4;
}
const int virt_ds5_thread_creation = pthread_create(&logic->virt_ds5_thread, NULL, virt_ds5_thread_func, (void*)(logic));
if (virt_ds4_thread_creation != 0) {
fprintf(stderr, "Error creating virtual DualSense thread: %d.\n", virt_ds5_thread_creation);
} else {
printf("Creation of virtual DualShock4 succeeded: using it as the defaut output.\n");
logic->flags |= LOGIC_FLAGS_VIRT_DS5_ENABLE;
logic->gamepad_output = GAMEPAD_OUTPUT_DS5;
}
if (queue_init_res < 0) {
fprintf(stderr, "Unable to create queue: %d\n", queue_init_res);
return queue_init_res;
}
bool lizard_thread_started = false;
const int init_platform_res = init_platform(&logic->platform);
if (init_platform_res == 0) {
printf("RC71L platform correctly initialized\n");
@ -71,35 +48,74 @@ int logic_create(logic_t *const logic) {
logic->flags |= LOGIC_FLAGS_PLATFORM_ENABLE;
if (is_mouse_mode(&logic->platform)) {
printf("Gamepad output will default to evdev when the controller is set in mouse mode.\n");
logic->gamepad_output = GAMEPAD_OUTPUT_EVDEV;
} else if (is_gamepad_mode(&logic->platform)) {
logic->gamepad_output = (logic->flags & LOGIC_FLAGS_VIRT_DS5_ENABLE) ? GAMEPAD_OUTPUT_DS5 : ((logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) ? GAMEPAD_OUTPUT_DS4: GAMEPAD_OUTPUT_EVDEV);
} else if (is_macro_mode(&logic->platform)) {
logic->gamepad_output = (logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) ? GAMEPAD_OUTPUT_DS4 : GAMEPAD_OUTPUT_EVDEV;
}
printf("Device is in lizard mode\n");
// TODO: start the appropriate output thread
printf("Gamepad output is %d\n", (int)logic->gamepad_output);
lizard_thread_started = true;
}
} else {
fprintf(stderr, "Unable to initialize Asus RC71L MCU: %d\n", init_platform_res);
logic->gamepad_output = (logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) ? GAMEPAD_OUTPUT_DS4 : GAMEPAD_OUTPUT_EVDEV;
}
queue_init(&logic->rumble_events_queue, 1);
init_config(&logic->controller_settings);
const int fill_config_res = fill_config(&logic->controller_settings, configuration_file);
if (fill_config_res != 0) {
fprintf(stderr, "Unable to fill configuration from file %s\n", configuration_file);
if (!lizard_thread_started) {
logic_start_output_dev_thread(logic);
}
return 0;
logic_create_err:
return ret;
}
int is_rc71l_ready(const logic_t *const logic) {
return logic->flags & LOGIC_FLAGS_PLATFORM_ENABLE;
}
void logic_terminate_output_thread(logic_t *const logic) {
if (logic->virt_dev_thread_running) {
void* thread_return = NULL;
pthread_join(logic->virt_dev_thread, &thread_return);
}
}
int logic_start_output_mouse_kbd_thread(logic_t *const logic) {
// TODO: logic->dev_stats.mouse_kbd.connected = true;
const int ret = pthread_create(&logic->virt_dev_thread, NULL, virt_mouse_kbd_thread_func, (void*)(&logic->dev_stats));
logic->virt_dev_thread_running = ret == 0;
return ret;
}
int logic_start_output_dev_thread(logic_t *const logic) {
logic->dev_stats.gamepad.connected = true;
int ret = -EINVAL;
switch (logic->controller_settings.gamepad_output_device) {
case 0:
ret = pthread_create(&logic->virt_dev_thread, NULL, virt_evdev_thread_func, (void*)(&logic->dev_stats));
break;
case 1:
ret = pthread_create(&logic->virt_dev_thread, NULL, virt_ds5_thread_func, (void*)(&logic->dev_stats));
break;
case 2:
ret = pthread_create(&logic->virt_dev_thread, NULL, virt_ds4_thread_func, (void*)(&logic->dev_stats));
break;
default:
fprintf(stderr, "Invalid output device specified\n");
ret = -EINVAL;
break;
}
logic->virt_dev_thread_running = ret == 0;
return ret;
}
/*
int logic_copy_gamepad_status(logic_t *const logic, gamepad_status_t *const out) {
int res = 0;
@ -172,7 +188,6 @@ logic_copy_gamepad_status_err:
return res;
}
int logic_begin_status_update(logic_t *const logic) {
int res = 0;
@ -188,6 +203,7 @@ logic_begin_status_update_err:
void logic_end_status_update(logic_t *const logic) {
pthread_mutex_unlock(&logic->gamepad_mutex);
}
*/
void logic_request_termination(logic_t *const logic) {
logic->flags |= LOGIC_FLAGS_TERMINATION_REQUESTED;

99
logic.h
View file

@ -3,6 +3,7 @@
#include "platform.h"
#include "queue.h"
#include "settings.h"
#include "devices_status.h"
#define PRESS_AND_RELEASE_DURATION_FOR_CENTER_BUTTON_MS 80
#define PRESS_TIME_BEFORE_CROSS_BUTTON_MS 250
@ -12,63 +13,9 @@
#define GAMEPAD_STATUS_FLAGS_PRESS_AND_REALEASE_CENTER 0x00000001U
#define GAMEPAD_STATUS_FLAGS_OPEN_STEAM_QAM 0x00000002U
typedef struct gamepad_status {
int32_t joystick_positions[2][2]; // [0 left | 1 right][x axis | y axis]
uint8_t dpad; // 0x00 x - | 0x01 x -> | 0x02 x <- | 0x00 y - | 0x10 y ^ | 0x10 y . |
uint8_t l2_trigger;
uint8_t r2_trigger;
uint8_t triangle;
uint8_t circle;
uint8_t cross;
uint8_t square;
uint8_t l1;
uint8_t r1;
uint8_t r3;
uint8_t l3;
uint8_t option;
uint8_t share;
uint8_t center;
uint8_t l4;
uint8_t r4;
uint8_t l5;
uint8_t r5;
struct timeval last_gyro_motion_time;
struct timeval last_accel_motion_time;
double gyro[3]; // | x, y, z| right-hand-rules -- in rad/s
double accel[3]; // | x, y, z| positive: right, up, towards player -- in m/s^2
int16_t raw_gyro[3];
int16_t raw_accel[3];
uint64_t rumble_events_count;
uint8_t motors_intensity[2]; // 0 = left, 1 = right
volatile uint32_t flags;
} gamepad_status_t;
#define LOGIC_FLAGS_VIRT_DS4_ENABLE 0x00000001U
#define LOGIC_FLAGS_VIRT_DS5_ENABLE 0x00000002U
#define LOGIC_FLAGS_PLATFORM_ENABLE 0x00000010U
#define LOGIC_FLAGS_TERMINATION_REQUESTED 0x80000000U
typedef enum gamepad_output {
GAMEPAD_OUTPUT_EVDEV = 0,
GAMEPAD_OUTPUT_DS4,
GAMEPAD_OUTPUT_DS5,
} gamepad_output_t;
typedef struct rumble_message {
uint16_t strong_magnitude;
uint16_t weak_magnitude;
@ -78,21 +25,15 @@ typedef struct logic {
rc71l_platform_t platform;
pthread_mutex_t gamepad_mutex;
gamepad_status_t gamepad;
devices_status_t dev_stats;
queue_t input_queue;
pthread_t virt_ds4_thread;
pthread_t virt_ds5_thread;
pthread_t virt_dev_thread;
bool virt_dev_thread_running;
volatile uint32_t flags;
// the mutex is not needed if only one thread is writing this and others are checking with equality
//pthread_mutex_t gamepad_output_mutex;
gamepad_output_t gamepad_output;
queue_t rumble_events_queue;
controller_settings_t controller_settings;
@ -103,12 +44,38 @@ int logic_create(logic_t *const logic);
int is_rc71l_ready(const logic_t *const logic);
void logic_request_termination(logic_t *const logic);
int logic_termination_requested(logic_t *const logic);
/**
* This function starts the output thread for the user-chosed gamepad.
*
* Before starting the thread this function will also modify the status of "connected"
* so that the started thread won't exit immediatly.
* This function can only be called when the mutex is not needed:
* in practice this is true when no output thread is active
*/
int logic_start_output_dev_thread(logic_t *const logic);
/**
* This function starts the output thread for the keyboard&mouse.
*
* Before starting the thread this function will also modify the status of "connected"
* so that the started thread won't exit immediatly.
*
* This function can only be called when the mutex is not needed:
* in practice this is true when no output thread is active
*/
int logic_start_output_mouse_kbd_thread(logic_t *const logic);
void logic_terminate_output_thread(logic_t *const logic);
/*
int logic_copy_gamepad_status(logic_t *const logic, gamepad_status_t *const out);
int logic_begin_status_update(logic_t *const logic);
void logic_end_status_update(logic_t *const logic);
*/
void logic_request_termination(logic_t *const logic);
int logic_termination_requested(logic_t *const logic);

144
main.c
View file

@ -8,8 +8,6 @@
logic_t global_logic;
static output_dev_t out_gamepadd_dev = {
.gamepad_fd = -1,
.imu_fd = -1,
.logic = &global_logic,
};
@ -68,6 +66,24 @@ static input_dev_t in_xbox_dev = {
.ev_input_filter_fn = input_filter_identity,
};
int start_input_devices(pthread_t *out_threads, input_dev_t **in_devs, uint16_t in_count) {
int ret = 0;
uint16_t i = 0;
for (i = 0; i < in_count; ++i) {
const int thread_creation = pthread_create(&out_threads[i], NULL, input_dev_thread_func, (void*)(in_devs[i]));
if (thread_creation != 0) {
fprintf(stderr, "Error creating input thread for device %d: %d\n", (int)i, thread_creation);
ret = -1;
// TODO: logic_request_termination(&global_logic);
goto start_input_devices_err;
}
}
start_input_devices_err:
return ret;
}
void sig_handler(int signo)
{
if (signo == SIGINT) {
@ -77,49 +93,34 @@ void sig_handler(int signo)
}
int main(int argc, char ** argv) {
int ret = 0;
const int logic_creation_res = logic_create(&global_logic);
if (logic_creation_res < 0) {
fprintf(stderr, "Unable to create logic: %d", logic_creation_res);
return EXIT_FAILURE;
}
int imu_fd = create_output_dev("/dev/uinput", output_dev_imu);
if (imu_fd < 0) {
fprintf(stderr, "Unable to create IMU virtual device\n");
return EXIT_FAILURE;
input_dev_t *in_devs[] = {
&in_xbox_dev,
&in_iio_dev,
&in_asus_kb_1_dev,
&in_asus_kb_2_dev,
&in_asus_kb_3_dev,
};
const uint16_t input_dev_count = (sizeof(in_devs) / sizeof(input_dev_t *));
pthread_t *out_threads = malloc(sizeof(pthread_t) * input_dev_count);
ret = start_input_devices(out_threads, in_devs, input_dev_count);
if (ret != 0) {
fprintf(stderr, "Unable to start input devices: %d -- terminating...\n", ret);
goto input_threads_err;
}
int gamepad_fd = create_output_dev("/dev/uinput", output_dev_gamepad);
if (gamepad_fd < 0) {
close(imu_fd);
fprintf(stderr, "Unable to create gamepad virtual device\n");
return EXIT_FAILURE;
}
int mouse_fd = create_output_dev("/dev/uinput", output_dev_mouse);
if (mouse_fd < 0) {
close(gamepad_fd);
close(imu_fd);
fprintf(stderr, "Unable to create mouse virtual device\n");
return EXIT_FAILURE;
}
out_gamepadd_dev.gamepad_fd = gamepad_fd;
out_gamepadd_dev.imu_fd = imu_fd;
out_gamepadd_dev.mouse_fd = mouse_fd;
/*
__sighandler_t sigint_hndl = signal(SIGINT, sig_handler);
if (sigint_hndl == SIG_ERR) {
fprintf(stderr, "Error registering SIGINT handler\n");
return EXIT_FAILURE;
}
*/
int ret = 0;
pthread_t gamepad_thread;
pthread_t xbox_thread, asus_kb_1_thread, asus_kb_2_thread, asus_kb_3_thread, iio_thread;
const int gamepad_thread_creation = pthread_create(&gamepad_thread, NULL, output_dev_thread_func, (void*)(&out_gamepadd_dev));
if (gamepad_thread_creation != 0) {
@ -129,69 +130,22 @@ int main(int argc, char ** argv) {
goto gamepad_thread_err;
}
const int xbox_thread_creation = pthread_create(&xbox_thread, NULL, input_dev_thread_func, (void*)(&in_xbox_dev));
if (xbox_thread_creation != 0) {
fprintf(stderr, "Error creating xbox input thread: %d\n", xbox_thread_creation);
ret = -1;
logic_request_termination(&global_logic);
goto xbox_drv_thread_err;
/*
// TODO: once the application is able to exit de-comment this
__sighandler_t sigint_hndl = signal(SIGINT, sig_handler);
if (sigint_hndl == SIG_ERR) {
fprintf(stderr, "Error registering SIGINT handler\n");
return EXIT_FAILURE;
}
*/
for (uint16_t j = 0; j < input_dev_count; ++j) {
pthread_join(out_threads[j], NULL);
}
const int asus_kb_1_thread_creation = pthread_create(&asus_kb_1_thread, NULL, input_dev_thread_func, (void*)(&in_asus_kb_1_dev));
if (asus_kb_1_thread_creation != 0) {
fprintf(stderr, "Error creating asus keyboard (1) input thread: %d\n", asus_kb_1_thread_creation);
ret = -1;
logic_request_termination(&global_logic);
goto asus_kb_1_thread_err;
}
const int asus_kb_2_thread_creation = pthread_create(&asus_kb_2_thread, NULL, input_dev_thread_func, (void*)(&in_asus_kb_2_dev));
if (asus_kb_2_thread_creation != 0) {
fprintf(stderr, "Error creating asus keyboard (2) input thread: %d\n", asus_kb_2_thread_creation);
ret = -1;
logic_request_termination(&global_logic);
goto asus_kb_2_thread_err;
}
const int asus_kb_3_thread_creation = pthread_create(&asus_kb_3_thread, NULL, input_dev_thread_func, (void*)(&in_asus_kb_3_dev));
if (asus_kb_3_thread_creation != 0) {
fprintf(stderr, "Error creating asus keyboard (3) input thread: %d\n", asus_kb_3_thread_creation);
ret = -1;
logic_request_termination(&global_logic);
goto asus_kb_3_thread_err;
}
const int iio_thread_creation = pthread_create(&iio_thread, NULL, input_dev_thread_func, (void*)(&in_iio_dev));
if (iio_thread_creation != 0) {
fprintf(stderr, "Error creating iio input thread: %d\n", asus_kb_3_thread_creation);
ret = -1;
logic_request_termination(&global_logic);
goto iio_thread_err;
}
pthread_join(iio_thread, NULL);
iio_thread_err:
pthread_join(asus_kb_3_thread, NULL);
asus_kb_3_thread_err:
pthread_join(asus_kb_2_thread, NULL);
asus_kb_2_thread_err:
pthread_join(asus_kb_1_thread, NULL);
asus_kb_1_thread_err:
pthread_join(xbox_thread, NULL);
xbox_drv_thread_err:
pthread_join(gamepad_thread, NULL);
gamepad_thread_err:
ioctl(gamepad_fd, UI_DEV_DESTROY);
close(gamepad_fd);
// TODO: free(imu_dev.events_list);
// TODO: free(gamepadd_dev.events_list);
input_threads_err:
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}

File diff suppressed because it is too large Load diff

View file

@ -3,62 +3,11 @@
#include "queue.h"
#include "logic.h"
// Emulates a "Generic" controller:
#define OUTPUT_DEV_NAME "ROGueENEMY"
#define OUTPUT_DEV_VENDOR_ID 0x108c
#define OUTPUT_DEV_PRODUCT_ID 0x0323
#define OUTPUT_DEV_VERSION 0x0111
/*
// Emulates a steam controller
#define OUTPUT_DEV_NAME "Steam Controller"
#define OUTPUT_DEV_VENDOR_ID 0x28de
#define OUTPUT_DEV_PRODUCT_ID 0x1102
#define OUTPUT_DEV_VERSION 0x0111
#define OUTPUT_DEV_BUS_TYPE BUS_USB
*/
/*
//Emulates an Xbox one wireless controller:
#define OUTPUT_DEV_NAME "Xbox Wireless Controller"
#define OUTPUT_DEV_VENDOR_ID 0x045e
#define OUTPUT_DEV_PRODUCT_ID 0x028e
#define OUTPUT_DEV_BUS_TYPE BUS_BLUETOOTH
*/
/*
// Emulates a DualShock controller
#define OUTPUT_DEV_NAME "Sony Interactive Entertainment DualSense Wireless Controller"
#define OUTPUT_DEV_VENDOR_ID 0x054c
#define OUTPUT_DEV_PRODUCT_ID 0x0ce6
#define OUTPUT_DEV_VERSION 0x8111
#define OUTPUT_DEV_BUS_TYPE BUS_USB
*/
#define PHYS_STR "00:11:22:33:44:55"
#define ACCEL_RANGE 512
#define GYRO_RANGE 2000 // max range is +/- 35 radian/s
#define GYRO_DEADZONE 1 // degrees/s to count as zero movement
#undef INCLUDE_TIMESTAMP
#undef INCLUDE_OUTPUT_DEBUG
typedef enum output_dev_type {
output_dev_gamepad,
output_dev_imu,
output_dev_mouse,
} output_dev_type_t;
typedef struct output_dev {
int gamepad_fd;
int imu_fd;
int mouse_fd;
logic_t *logic;
} output_dev_t;
int create_output_dev(const char* uinput_path, output_dev_type_t type);
void *output_dev_thread_func(void *ptr);

View file

@ -643,7 +643,7 @@ int init_platform(rc71l_platform_t *const platform) {
int cycle_mode(rc71l_platform_t *const platform) {
if (platform == NULL) {
return -1;
return -ENOENT;
}
char new_mode_str[] = {
@ -659,7 +659,7 @@ int cycle_mode(rc71l_platform_t *const platform) {
char *const dev_name = find_device(platform->udev);
if (dev_name == NULL) {
fprintf(stderr, "Unable to locate Asus MCU hidraw to mode-switch. Mode will not be switched.\n");
return platform->mode;
return -ENOENT;
}
const int next_mode = (platform->mode + 1) % platform->modes_count;
@ -667,13 +667,11 @@ int cycle_mode(rc71l_platform_t *const platform) {
const int reset_res = hidraw_cycle_to_mode(dev_name, next_mode);
if (reset_res != 0) {
fprintf(stderr, "Unable to change mode of Asus MCU over hidraw: %d.\n", reset_res);
}
free(dev_name);
return platform->mode;
}
platform->mode = next_mode;
return reset_res;
} else if (platform->platform_mode == rc71l_platform_mode_asus_mcu) {
FILE* mode_file = fopen(platform_input_path, "w");
if (mode_file == NULL) {
@ -685,7 +683,7 @@ int cycle_mode(rc71l_platform_t *const platform) {
const int write_bytes = fwrite((void*)&new_mode_str[0], 1, len, mode_file);
if (write_bytes < len) {
fprintf(stderr, "Error writing new mode: expected to write %d bytes, %d written.\n", (int)len, (int)write_bytes);
return -2;
return -EIO;
}
platform->mode = new_mode;
@ -693,17 +691,9 @@ int cycle_mode(rc71l_platform_t *const platform) {
fclose(mode_file);
}
return platform->mode;
}
int is_gamepad_mode(rc71l_platform_t *const platform) {
return platform != NULL ? platform->mode == 0 : 0;
return -ENOENT;
}
int is_mouse_mode(rc71l_platform_t *const platform) {
return platform != NULL ? platform->mode == 1 : 0;
}
int is_macro_mode(rc71l_platform_t *const platform) {
return platform != NULL ? platform->mode == 2 : 0;
}

View file

@ -21,8 +21,3 @@ int init_platform(rc71l_platform_t *const platform);
int cycle_mode(rc71l_platform_t *const platform);
int is_mouse_mode(rc71l_platform_t *const platform);
int is_gamepad_mode(rc71l_platform_t *const platform);
int is_macro_mode(rc71l_platform_t *const platform);

View file

@ -1,11 +1,11 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <stdatomic.h>
#include <inttypes.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

View file

@ -6,6 +6,7 @@ void init_config(controller_settings_t *const conf) {
conf->ff_gain = 100;
conf->enable_qam = 0;
conf->nintendo_layout = 0;
conf->gamepad_output_device = 1;
}
int fill_config(controller_settings_t *const conf, const char* file) {
@ -46,6 +47,13 @@ int fill_config(controller_settings_t *const conf, const char* file) {
fprintf(stderr, "nintendo_layout (bool) configuration not found. Default value will be used.\n");
}
int gamepad_output_device;
if (config_lookup_int(&cfg, "gamepad_output_device", &gamepad_output_device) != CONFIG_FALSE) {
conf->gamepad_output_device = gamepad_output_device;
} else {
fprintf(stderr, "gamepad_output_device (int) configuration not found. Default value will be used.\n");
}
config_destroy(&cfg);
fill_config_err:

View file

@ -6,6 +6,14 @@ typedef struct controller_settings {
uint16_t ff_gain;
int enable_qam;
int nintendo_layout;
/**
* 0 is virtual evdev
* 1 is DualSense
* 2 is DualShock
* 3 is Xbox one
*/
int gamepad_output_device;
} controller_settings_t;
void init_config(controller_settings_t *const conf);

View file

@ -339,7 +339,7 @@ static void destroy(int fd)
* uhid program shouldn't do this but instead just forward the raw report.
* However, for ducomentational purposes, we try to detect LED events here and
* print debug messages for it. */
static void handle_output(struct uhid_event *ev, logic_t *const logic)
static void handle_output(struct uhid_event *ev, gamepad_status_t *const gamepad_stats)
{
// Rumble and LED messages are adverised via OUTPUT reports; ignore the rest
if (ev->u.output.rtype != UHID_OUTPUT_REPORT)
@ -397,19 +397,10 @@ static void handle_output(struct uhid_event *ev, logic_t *const logic)
const uint8_t lightbar_blink_on = ev->u.output.data[9];
const uint8_t lightbar_blink_off = ev->u.output.data[10];
if ((valid_flag0 & DS4_OUTPUT_VALID_FLAG0_MOTOR) && (logic->gamepad_output == LOGIC_FLAGS_VIRT_DS4_ENABLE)) {
const int lock_res = pthread_mutex_lock(&logic->gamepad_mutex);
if (lock_res != 0) {
printf("Unable to lock gamepad mutex: %d, rumble will not be updated.\n", lock_res);
return;
}
logic->gamepad.motors_intensity[0] = motor_left;
logic->gamepad.motors_intensity[1] = motor_right;
++logic->gamepad.rumble_events_count;
pthread_mutex_unlock(&logic->gamepad_mutex);
if ((valid_flag0 & DS4_OUTPUT_VALID_FLAG0_MOTOR)) {
gamepad_stats->motors_intensity[0] = motor_left;
gamepad_stats->motors_intensity[1] = motor_right;
++gamepad_stats->rumble_events_count;
#if defined(VIRT_DS4_DEBUG)
printf(
@ -423,7 +414,7 @@ static void handle_output(struct uhid_event *ev, logic_t *const logic)
}
}
static int event(int fd, logic_t *const logic)
static int event(int fd, gamepad_status_t *const gamepad_stats)
{
struct uhid_event ev;
ssize_t ret;
@ -469,7 +460,7 @@ static int event(int fd, logic_t *const logic)
#if defined(VIRT_DS4_DEBUG)
printf("UHID_OUTPUT from uhid-dev\n");
#endif
handle_output(&ev, logic);
handle_output(&ev, gamepad_stats);
break;
case UHID_OUTPUT_EV:
#if defined(VIRT_DS4_DEBUG)
@ -570,42 +561,6 @@ static int event(int fd, logic_t *const logic)
return 0;
}
static uint8_t get_buttons_byte_by_gs(const gamepad_status_t *const gs) {
uint8_t res = 0;
res |= gs->triangle ? 0x80 : 0x00;
res |= gs->circle ? 0x40 : 0x00;
res |= gs->cross ? 0x20 : 0x00;
res |= gs->square ? 0x10 : 0x00;
return res;
}
static uint8_t get_buttons_byte2_by_gs(const gamepad_status_t *const gs) {
uint8_t res = 0;
res |= gs->r3 ? 0x80 : 0x00;
res |= gs->l3 ? 0x40 : 0x00;
res |= gs->share ? 0x20 : 0x00;
res |= gs->option ? 0x10 : 0x00;
res |= gs->r2_trigger > 200 ? 0x08 : 0x00;
res |= gs->l2_trigger > 200 ? 0x04 : 0x00;
res |= gs->r1 ? 0x02 : 0x00;
res |= gs->l1 ? 0x01 : 0x00;
return res;
}
static uint8_t get_buttons_byte3_by_gs(const gamepad_status_t *const gs) {
uint8_t res = 0;
res |= gs->center ? 0x01 : 0x00;
return res;
}
typedef enum ds4_dpad_status {
DPAD_N = 0,
DPAD_NE = 1,
@ -643,13 +598,8 @@ static ds4_dpad_status_t ds4_dpad_from_gamepad(uint8_t dpad) {
/**
* This function arranges HID packets as described on https://www.psdevwiki.com/ps4/DS4-USB
*/
static int send_data(int fd, logic_t *const logic) {
gamepad_status_t gs;
const int gs_copy_res = logic_copy_gamepad_status(logic, &gs);
if (gs_copy_res != 0) {
fprintf(stderr, "Unable to copy the gamepad status: %d\n", gs_copy_res);
return gs_copy_res;
}
static void compose_hid_report_buffer(int fd, gamepad_status_t *const gamepad_stats, uint8_t buf[64]) {
gamepad_status_t gs = *gamepad_stats;
const int64_t time_us = gs.last_gyro_motion_time.tv_sec * 1000000 + gs.last_gyro_motion_time.tv_usec;
@ -702,13 +652,7 @@ static int send_data(int fd, logic_t *const logic) {
// see https://www.psdevwiki.com/ps4/DS4-USB
// [12] battery level
uint8_t buf[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// buf[12] battery level
/*
* kernel will do:
@ -742,21 +686,33 @@ static int send_data(int fd, logic_t *const logic) {
const int16_t a_y = (int16_t)(-1) * gs.raw_accel[1]; // Swap Y and Z
const int16_t a_z = (int16_t)(-1) * gs.raw_accel[2]; // Swap Y and Z
buf[0] = 0x01; // [00] report ID (0x01)
buf[1] = ((uint64_t)((int64_t)gs.joystick_positions[0][0] + (int64_t)32768) >> (uint64_t)8); // L stick, X axis
buf[2] = ((uint64_t)((int64_t)gs.joystick_positions[0][1] + (int64_t)32768) >> (uint64_t)8); // L stick, Y axis
buf[3] = ((uint64_t)((int64_t)gs.joystick_positions[1][0] + (int64_t)32768) >> (uint64_t)8); // R stick, X axis
buf[4] = ((uint64_t)((int64_t)gs.joystick_positions[1][1] + (int64_t)32768) >> (uint64_t)8); // R stick, Y axis
buf[5] = get_buttons_byte_by_gs(&gs) | (uint8_t)ds4_dpad_from_gamepad(gs.dpad);
buf[6] = get_buttons_byte2_by_gs(&gs);
buf[5] =
(gs.triangle ? 0x80 : 0x00) |
(gs.circle ? 0x40 : 0x00) |
(gs.cross ? 0x20 : 0x00) |
(gs.square ? 0x10 : 0x00) |
(uint8_t)ds4_dpad_from_gamepad(gs.dpad);
buf[6] =
(gs.r3 ? 0x80 : 0x00) |
(gs.l3 ? 0x40 : 0x00) |
(gs.share ? 0x20 : 0x00) |
(gs.option ? 0x10 : 0x00) |
(gs.r2_trigger > 200 ? 0x08 : 0x00) |
(gs.l2_trigger > 200 ? 0x04 : 0x00) |
(gs.r1 ? 0x02 : 0x00) |
(gs.l1 ? 0x01 : 0x00);
/*
static uint8_t counter = 0;
buf[7] = (((counter++) % (uint8_t)64) << ((uint8_t)2)) | get_buttons_byte3_by_gs(&gs);
*/
buf[7] = get_buttons_byte3_by_gs(&gs);
buf[7] = gs.center ? 0x01 : 0x00;
buf[8] = gs.l2_trigger;
buf[9] = gs.r2_trigger;
@ -777,6 +733,56 @@ static int send_data(int fd, logic_t *const logic) {
buf[48] = 0x80; // IDK... it seems constant...
buf[35] = 0x80; // IDK... it seems constant...
buf[44] = 0x80; // IDK... it seems constant...
}
/**
* Thread function emulating the DualShock4 controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs.
*
* See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3
*/
void *virt_ds4_thread_func(void *ptr) {
devices_status_t *const stats = (devices_status_t*)ptr;
fprintf(stderr, "Open uhid-cdev %s\n", path);
int fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, fd);
return NULL;
}
fprintf(stderr, "Create uhid device\n");
int ret = create(fd);
if (ret) {
close(fd);
return NULL;
}
uint8_t buf[64];
for (;;) {
usleep(1250);
memset(buf, 0, sizeof(buf));
const int lock_res = pthread_mutex_lock(&stats->mutex);
if (lock_res != 0) {
printf("Unable to lock gamepad mutex: %d\n", lock_res);
continue;
}
// main virtual device logic
{
event(fd, &stats->gamepad);
if (stats->gamepad.connected) {
compose_hid_report_buffer(fd, &stats->gamepad, buf);
} else {
printf("DualShock has been terminated: closing the device.\n");
break;
}
}
pthread_mutex_unlock(&stats->mutex);
struct uhid_event l = {
.type = UHID_INPUT2,
@ -789,57 +795,10 @@ static int send_data(int fd, logic_t *const logic) {
memcpy(&l.u.input2.data[0], &buf[0], l.u.input2.size);
return uhid_write(fd, &l);
uhid_write(fd, &l);
}
/**
* Thread function emulating the DualShock4 controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs.
*
* See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3
*/
void *virt_ds4_thread_func(void *ptr) {
logic_t *const logic = (logic_t*)ptr;
for (;;) {
if (logic->gamepad_output != GAMEPAD_OUTPUT_DS4) {
// sleep for 500ms before re-checking
usleep(500000);
continue;
}
fprintf(stderr, "Open uhid-cdev %s\n", path);
int fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, fd);
continue;
}
fprintf(stderr, "Create uhid device\n");
int ret = create(fd);
if (ret) {
close(fd);
continue;
}
for (;;) {
usleep(1250);
event(fd, logic);
if (logic->gamepad_output == GAMEPAD_OUTPUT_DS4) {
const int res = send_data(fd, logic);
if (res < 0) {
fprintf(stderr, "Error sending HID report: %d\n", res);
}
} else {
printf("DualShock has been terminated: closing the device.\n");
goto virt_ds4_thread_func_reset;
}
}
virt_ds4_thread_func_reset:
destroy(fd);
}
return NULL;
}

View file

@ -99,7 +99,7 @@ static void destroy(int fd)
uhid_write(fd, &ev);
}
static void handle_output(struct uhid_event *ev, logic_t *const logic)
static void handle_output(struct uhid_event *ev, gamepad_status_t *const gamepad_stats)
{
// Rumble and LED messages are adverised via OUTPUT reports; ignore the rest
if (ev->u.output.rtype != UHID_OUTPUT_REPORT)
@ -140,21 +140,11 @@ static void handle_output(struct uhid_event *ev, logic_t *const logic)
uint8_t lightbar_green = ev->u.output.data[46];
uint8_t lightbar_blue = ev->u.output.data[47];
if ((valid_flag0 & DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT) && (logic->gamepad_output == LOGIC_FLAGS_VIRT_DS5_ENABLE)) {
if ((valid_flag0 & DS_OUTPUT_VALID_FLAG0_HAPTICS_SELECT)) {
if ((valid_flag2 & DS_OUTPUT_VALID_FLAG2_COMPATIBLE_VIBRATION2) || (valid_flag0 & DS_OUTPUT_VALID_FLAG0_COMPATIBLE_VIBRATION)) {
const int lock_res = pthread_mutex_lock(&logic->gamepad_mutex);
if (lock_res != 0) {
printf("Unable to lock gamepad mutex: %d, rumble will not be updated.\n", lock_res);
return;
}
logic->gamepad.motors_intensity[0] = motor_left;
logic->gamepad.motors_intensity[1] = motor_right;
++logic->gamepad.rumble_events_count;
pthread_mutex_unlock(&logic->gamepad_mutex);
gamepad_stats->motors_intensity[0] = motor_left;
gamepad_stats->motors_intensity[1] = motor_right;
++gamepad_stats->rumble_events_count;
#if defined(VIRT_DS5_DEBUG)
printf(
@ -169,7 +159,7 @@ static void handle_output(struct uhid_event *ev, logic_t *const logic)
}
}
static int event(int fd, logic_t *const logic)
static int event(int fd, gamepad_status_t *const gamepad_stats)
{
struct uhid_event ev;
ssize_t ret;
@ -215,7 +205,7 @@ static int event(int fd, logic_t *const logic)
#if defined(VIRT_DS5_DEBUG)
printf("UHID_OUTPUT from uhid-dev\n");
#endif
handle_output(&ev, logic);
handle_output(&ev, gamepad_stats);
break;
case UHID_OUTPUT_EV:
#if defined(VIRT_DS5_DEBUG)
@ -293,42 +283,6 @@ static int event(int fd, logic_t *const logic)
return 0;
}
static uint8_t get_buttons_byte_by_gs(const gamepad_status_t *const gs) {
uint8_t res = 0;
res |= gs->triangle ? 0x80 : 0x00;
res |= gs->circle ? 0x40 : 0x00;
res |= gs->cross ? 0x20 : 0x00;
res |= gs->square ? 0x10 : 0x00;
return res;
}
static uint8_t get_buttons_byte2_by_gs(const gamepad_status_t *const gs) {
uint8_t res = 0;
res |= gs->r3 ? 0x80 : 0x00;
res |= gs->l3 ? 0x40 : 0x00;
res |= gs->share ? 0x20 : 0x00;
res |= gs->option ? 0x10 : 0x00;
res |= gs->r2_trigger > 200 ? 0x08 : 0x00;
res |= gs->l2_trigger > 200 ? 0x04 : 0x00;
res |= gs->r1 ? 0x02 : 0x00;
res |= gs->l1 ? 0x01 : 0x00;
return res;
}
static uint8_t get_buttons_byte3_by_gs(const gamepad_status_t *const gs) {
uint8_t res = 0;
res |= gs->center ? 0x01 : 0x00;
return res;
}
typedef enum ds5_dpad_status {
DPAD_N = 0,
DPAD_NE = 1,
@ -363,13 +317,8 @@ static ds5_dpad_status_t ds5_dpad_from_gamepad(uint8_t dpad) {
return DPAD_RELEASED;
}
static int send_data(int fd, logic_t *const logic) {
gamepad_status_t gs;
const int gs_copy_res = logic_copy_gamepad_status(logic, &gs);
if (gs_copy_res != 0) {
fprintf(stderr, "Unable to copy the gamepad status: %d\n", gs_copy_res);
return gs_copy_res;
}
static void compose_hid_report_buffer(int fd, gamepad_status_t *const gamepad_stats, uint8_t buf[64]) {
gamepad_status_t gs = *gamepad_stats;
static uint8_t seq_num = 0x00;
@ -409,13 +358,6 @@ static int send_data(int fd, logic_t *const logic) {
const uint32_t timestamp = sim_time + (int)((double)empty_reports * DS5_SPEC_DELTA_TIME);
uint8_t buf[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const int16_t g_x = gs.raw_gyro[0];
const int16_t g_y = (int16_t)(-1) * gs.raw_gyro[1]; // Swap Y and Z
const int16_t g_z = (int16_t)(-1) * gs.raw_gyro[2]; // Swap Y and Z
@ -472,6 +414,56 @@ static int send_data(int fd, logic_t *const logic) {
buf[48] = 0x80; // IDK... it seems constant...
buf[35] = 0x80; // IDK... it seems constant...
buf[44] = 0x80; // IDK... it seems constant...
}
/**
* Thread function emulating the DualSense controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs.
*
* See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3
*/
void *virt_ds5_thread_func(void *ptr) {
devices_status_t *const stats = (devices_status_t*)ptr;
fprintf(stderr, "Open uhid-cdev %s\n", path);
int fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, fd);
return NULL;
}
fprintf(stderr, "Create uhid device\n");
int ret = create(fd);
if (ret) {
close(fd);
return NULL;
}
uint8_t buf[64];
for (;;) {
usleep(1250);
memset(buf, 0, sizeof(buf));
const int lock_res = pthread_mutex_lock(&stats->mutex);
if (lock_res != 0) {
printf("Unable to lock gamepad mutex: %d\n", lock_res);
continue;
}
// main virtual device logic
{
event(fd, &stats->gamepad);
if (stats->gamepad.connected) {
compose_hid_report_buffer(fd, &stats->gamepad, buf);
} else {
printf("DualSense has been terminated: closing the device.\n");
break;
}
}
pthread_mutex_unlock(&stats->mutex);
struct uhid_event l = {
.type = UHID_INPUT2,
@ -484,58 +476,10 @@ static int send_data(int fd, logic_t *const logic) {
memcpy(&l.u.input2.data[0], &buf[0], l.u.input2.size);
return uhid_write(fd, &l);
uhid_write(fd, &l);
}
/**
* Thread function emulating the DualSense controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs.
*
* See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3
*/
void *virt_ds5_thread_func(void *ptr) {
logic_t *const logic = (logic_t*)ptr;
for (;;) {
if (logic->gamepad_output != GAMEPAD_OUTPUT_DS5) {
// sleep for 500ms before re-checking
usleep(500000);
continue;
}
fprintf(stderr, "Open uhid-cdev %s\n", path);
int fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, fd);
continue;
}
fprintf(stderr, "Create uhid device\n");
int ret = create(fd);
if (ret) {
close(fd);
continue;
}
for (;;) {
usleep(1250);
event(fd, logic);
if (logic->gamepad_output == GAMEPAD_OUTPUT_DS5) {
const int res = send_data(fd, logic);
if (res < 0) {
fprintf(stderr, "Error sending HID report: %d\n", res);
}
} else {
printf("DualSense has been terminated: closing the device.\n");
goto virt_ds5_thread_func_reset;
}
}
virt_ds5_thread_func_reset:
destroy(fd);
}
return NULL;
}

570
virt_evdev.c Normal file
View file

@ -0,0 +1,570 @@
#include "virt_evdev.h"
#include "devices_status.h"
#include "logic.h"
static const char* uinput_path = "/dev/uinput";
static int create_gamepad_uinput() {
int fd = open(uinput_path, O_WRONLY | O_NONBLOCK);
if(fd < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_setup dev = {0};
strncpy(dev.name, OUTPUT_DEV_NAME, UINPUT_MAX_NAME_SIZE-1);
#if defined(OUTPUT_DEV_BUS_TYPE)
dev.id.bustype = OUTPUT_DEV_BUS_TYPE;
#else
dev.id.bustype = BUS_VIRTUAL;
#endif
dev.id.vendor = OUTPUT_DEV_VENDOR_ID;
dev.id.product = OUTPUT_DEV_PRODUCT_ID;
#if defined(OUTPUT_DEV_VERSION)
dev.id.version = OUTPUT_DEV_VERSION;
#endif
//if (ioctl(fd, /*UI_SET_PHYS_STR*/ 18, PHYS_STR) != 0) {
// fprintf(stderr, "Controller and gyroscope will NOT be recognized as a single device!\n");
//}
if (ioctl(fd, UI_SET_PHYS, PHYS_STR) != 0) {
fprintf(stderr, "Error setting the phys of the virtual controller.\n");
}
#if !defined(UI_SET_PHYS_STR)
#warning Will not change the PHYS
#endif
#if !defined(UI_SET_UNIQ_STR)
#warning Will not change the UNIQ
#endif
#if defined(UI_SET_PHYS_STR)
ioctl(fd, UI_SET_PHYS_STR(18), PHYS_STR);
#else
fprintf(stderr, "UI_SET_PHYS_STR unavailable.\n");
#endif
#if defined(UI_SET_UNIQ_STR)
ioctl(fd, UI_SET_UNIQ_STR(18), PHYS_STR);
#else
fprintf(stderr, "UI_SET_UNIQ_STR unavailable.\n");
#endif
//ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_BUTTONPAD);
ioctl(fd, UI_SET_EVBIT, EV_ABS);
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_EVBIT, EV_SYN);
#if defined(INCLUDE_TIMESTAMP)
ioctl(fd, UI_SET_EVBIT, EV_MSC);
ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP);
#endif
ioctl(fd, UI_SET_ABSBIT, ABS_X);
ioctl(fd, UI_SET_ABSBIT, ABS_Y);
ioctl(fd, UI_SET_ABSBIT, ABS_Z);
ioctl(fd, UI_SET_ABSBIT, ABS_RX);
ioctl(fd, UI_SET_ABSBIT, ABS_RY);
ioctl(fd, UI_SET_ABSBIT, ABS_RZ);
ioctl(fd, UI_SET_ABSBIT, ABS_HAT0X);
ioctl(fd, UI_SET_ABSBIT, ABS_HAT0Y);
ioctl(fd, UI_SET_ABSBIT, ABS_HAT2X);
ioctl(fd, UI_SET_ABSBIT, ABS_HAT2Y);
ioctl(fd, UI_SET_KEYBIT, BTN_SOUTH);
ioctl(fd, UI_SET_KEYBIT, BTN_EAST);
ioctl(fd, UI_SET_KEYBIT, BTN_NORTH);
ioctl(fd, UI_SET_KEYBIT, BTN_WEST);
ioctl(fd, UI_SET_KEYBIT, BTN_TL);
ioctl(fd, UI_SET_KEYBIT, BTN_TR);
ioctl(fd, UI_SET_KEYBIT, BTN_TL2);
ioctl(fd, UI_SET_KEYBIT, BTN_TR2);
ioctl(fd, UI_SET_KEYBIT, BTN_SELECT);
ioctl(fd, UI_SET_KEYBIT, BTN_START);
ioctl(fd, UI_SET_KEYBIT, BTN_MODE);
ioctl(fd, UI_SET_KEYBIT, BTN_THUMBL);
ioctl(fd, UI_SET_KEYBIT, BTN_THUMBR);
ioctl(fd, UI_SET_KEYBIT, BTN_GEAR_DOWN);
ioctl(fd, UI_SET_KEYBIT, BTN_GEAR_UP);
ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_UP);
ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_DOWN);
ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_LEFT);
ioctl(fd, UI_SET_KEYBIT, BTN_DPAD_RIGHT);
const struct uinput_abs_setup devAbsX = {
.code = ABS_X,
.absinfo = {
.value = 0,
.minimum = -32768,
.maximum = +32768,
.resolution = 1,
.fuzz = 16,
.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsX) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_abs_setup devAbsY = {
.code = ABS_Y,
.absinfo = {
.value = 0,
.minimum = -32768,
.maximum = +32768,
.resolution = 1,
.fuzz = 16,
.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsY) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_abs_setup devAbsZ = {
.code = ABS_Z,
.absinfo = {
.value = 0,
.minimum = 0,
.maximum = 255,
.resolution = 1,
//.fuzz = 16,
//.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsZ) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_abs_setup devAbsRX = {
.code = ABS_RX,
.absinfo = {
.value = 0,
.minimum = -32768,
.maximum = +32768,
.resolution = 1,
.fuzz = 16,
.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsRX) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_abs_setup devAbsRY = {
.code = ABS_RY,
.absinfo = {
.value = -1,
.minimum = -32768,
.maximum = +32768,
.resolution = 1,
.fuzz = 16,
.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsRY) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_abs_setup devAbsRZ = {
.code = ABS_RZ,
.absinfo = {
.value = 0,
.minimum = 0,
.maximum = 255,
.resolution = 1,
//.fuzz = 16,
//.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsRZ) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_abs_setup devAbsHat0X = {
.code = ABS_HAT0X,
.absinfo = {
.value = 0,
.minimum = -1,
.maximum = 1,
.resolution = 1,
//.fuzz = 16,
//.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsHat0X) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_abs_setup devAbsHat0Y = {
.code = ABS_HAT0Y,
.absinfo = {
.value = 0,
.minimum = -1,
.maximum = 1,
.resolution = 1,
//.fuzz = 16,
//.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsHat0Y) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_abs_setup devAbsHat2X = {
.code = ABS_HAT2X,
.absinfo = {
.value = 0,
.minimum = 0,
.maximum = 255,
.resolution = 51,
//.fuzz = 16,
//.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsHat2X) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
struct uinput_abs_setup devAbsHat2Y = {
.code = ABS_HAT2Y,
.absinfo = {
.value = 0,
.minimum = 0,
.maximum = 255,
.resolution = 51,
//.fuzz = 16,
//.flat = 128,
}
};
if(ioctl(fd, UI_ABS_SETUP, &devAbsHat2Y) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
if(ioctl(fd, UI_DEV_CREATE) < 0) {
fd = -1;
close(fd);
goto create_gamepad_uinput_err;
}
create_gamepad_uinput_err:
return fd;
}
static int create_imu_uinput() {
int fd = open(uinput_path, O_WRONLY | O_NONBLOCK);
if(fd < 0) {
fd = -1;
close(fd);
goto create_imu_uinput_err;
}
struct uinput_setup dev = {0};
strncpy(dev.name, OUTPUT_DEV_NAME, UINPUT_MAX_NAME_SIZE-1);
#if defined(OUTPUT_DEV_BUS_TYPE)
dev.id.bustype = OUTPUT_DEV_BUS_TYPE;
#else
dev.id.bustype = BUS_VIRTUAL;
#endif
dev.id.vendor = OUTPUT_DEV_VENDOR_ID;
dev.id.product = OUTPUT_DEV_PRODUCT_ID;
#if defined(OUTPUT_DEV_VERSION)
dev.id.version = OUTPUT_DEV_VERSION;
#endif
//if (ioctl(fd, /*UI_SET_PHYS_STR*/ 18, PHYS_STR) != 0) {
// fprintf(stderr, "Controller and gyroscope will NOT be recognized as a single device!\n");
//}
if (ioctl(fd, UI_SET_PHYS, PHYS_STR) != 0) {
fprintf(stderr, "Error setting the phys of the virtual controller.\n");
}
#if !defined(UI_SET_PHYS_STR)
#warning Will not change the PHYS
#endif
#if !defined(UI_SET_UNIQ_STR)
#warning Will not change the UNIQ
#endif
#if defined(UI_SET_PHYS_STR)
ioctl(fd, UI_SET_PHYS_STR(18), PHYS_STR);
#else
fprintf(stderr, "UI_SET_PHYS_STR unavailable.\n");
#endif
#if defined(UI_SET_UNIQ_STR)
ioctl(fd, UI_SET_UNIQ_STR(18), PHYS_STR);
#else
fprintf(stderr, "UI_SET_UNIQ_STR unavailable.\n");
#endif
ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_ACCELEROMETER);
ioctl(fd, UI_SET_EVBIT, EV_ABS);
#if defined(INCLUDE_TIMESTAMP)
ioctl(fd, UI_SET_EVBIT, EV_MSC);
ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP);
#endif
ioctl(fd, UI_SET_ABSBIT, ABS_X);
ioctl(fd, UI_SET_ABSBIT, ABS_Y);
ioctl(fd, UI_SET_ABSBIT, ABS_Z);
ioctl(fd, UI_SET_ABSBIT, ABS_RX);
ioctl(fd, UI_SET_ABSBIT, ABS_RY);
ioctl(fd, UI_SET_ABSBIT, ABS_RZ);
//ioctl(fd, UI_SET_KEYBIT, BTN_TRIGGER);
//ioctl(fd, UI_SET_KEYBIT, BTN_THUMB);
struct uinput_abs_setup devAbsX = {0};
devAbsX.code = ABS_X;
devAbsX.absinfo.minimum = -ACCEL_RANGE;
devAbsX.absinfo.maximum = ACCEL_RANGE;
devAbsX.absinfo.resolution = 255; // 255 units = 1g
devAbsX.absinfo.fuzz = 5;
devAbsX.absinfo.flat = 0;
if(ioctl(fd, UI_ABS_SETUP, &devAbsX) < 0) {
fd = -1;
close(fd);
goto create_imu_uinput_err;
}
struct uinput_abs_setup devAbsY = {0};
devAbsY.code = ABS_Y;
devAbsY.absinfo.minimum = -ACCEL_RANGE;
devAbsY.absinfo.maximum = ACCEL_RANGE;
devAbsY.absinfo.resolution = 255; // 255 units = 1g
devAbsY.absinfo.fuzz = 5;
devAbsY.absinfo.flat = 0;
if(ioctl(fd, UI_ABS_SETUP, &devAbsY) < 0) {
fd = -1;
close(fd);
goto create_imu_uinput_err;
}
struct uinput_abs_setup devAbsZ = {0};
devAbsZ.code = ABS_Z;
devAbsZ.absinfo.minimum = -ACCEL_RANGE;
devAbsZ.absinfo.maximum = ACCEL_RANGE;
devAbsZ.absinfo.resolution = 255; // 255 units = 1g
devAbsZ.absinfo.fuzz = 5;
devAbsZ.absinfo.flat = 0;
if(ioctl(fd, UI_ABS_SETUP, &devAbsZ) < 0) {
fd = -1;
close(fd);
goto create_imu_uinput_err;
}
struct uinput_abs_setup devAbsRX = {0};
devAbsRX.code = ABS_RX;
devAbsRX.absinfo.minimum = -GYRO_RANGE;
devAbsRX.absinfo.maximum = GYRO_RANGE;
devAbsRX.absinfo.resolution = 1; // 1 unit = 1 degree/s
devAbsRX.absinfo.fuzz = 0;
devAbsRX.absinfo.flat = GYRO_DEADZONE;
if(ioctl(fd, UI_ABS_SETUP, &devAbsRX) < 0) {
fd = -1;
close(fd);
goto create_imu_uinput_err;
}
struct uinput_abs_setup devAbsRY = {0};
devAbsRY.code = ABS_RY;
devAbsRY.absinfo.minimum = -GYRO_RANGE;
devAbsRY.absinfo.maximum = GYRO_RANGE;
devAbsRY.absinfo.resolution = 1; // 1 unit = 1 degree/s
devAbsRY.absinfo.fuzz = 0;
devAbsRY.absinfo.flat = GYRO_DEADZONE;
if(ioctl(fd, UI_ABS_SETUP, &devAbsRY) < 0) {
fd = -1;
close(fd);
goto create_imu_uinput_err;
}
struct uinput_abs_setup devAbsRZ = {0};
devAbsRZ.code = ABS_RZ;
devAbsRZ.absinfo.minimum = -GYRO_RANGE;
devAbsRZ.absinfo.maximum = GYRO_RANGE;
devAbsRZ.absinfo.resolution = 1; // 1 unit = 1 degree/s
devAbsRZ.absinfo.fuzz = 0;
devAbsRZ.absinfo.flat = GYRO_DEADZONE;
if(ioctl(fd, UI_ABS_SETUP, &devAbsRZ) < 0) {
fd = -1;
close(fd);
goto create_imu_uinput_err;
}
if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) {
fd = -1;
close(fd);
goto create_imu_uinput_err;
}
if(ioctl(fd, UI_DEV_CREATE) < 0) {
fd = -1;
close(fd);
goto create_imu_uinput_err;
}
create_imu_uinput_err:
return fd;
}
void *virt_evdev_thread_func(void *ptr) {
devices_status_t *const stats = (devices_status_t*)ptr;
const int gamepad_fd = create_gamepad_uinput();
if (gamepad_fd != 0) {
fprintf(stderr, "Unable to create the virtual evdev gamepad device: %d\n", gamepad_fd);
return NULL;
}
const int imu_fd = create_imu_uinput();
if (gamepad_fd != 0) {
fprintf(stderr, "Unable to create the virtual evdev imu device: %d\n", gamepad_fd);
return NULL;
}
for (;;) {
usleep(1250);
const int lock_res = pthread_mutex_lock(&stats->mutex);
if (lock_res != 0) {
printf("Unable to lock gamepad mutex: %d\n", lock_res);
continue;
}
// main virtual device logic
{
//event(fd, &stats->gamepad);
if (stats->gamepad.connected) {
// TODO: do whatever it takes...
} else {
printf("kbd&mouse has been terminated: closing the device.\n");
break;
}
}
pthread_mutex_unlock(&stats->mutex);
}
return NULL;
}
/*
static void emit_ev(output_dev_t *const out_dev, const message_t *const msg) {
// if events are flagged as do not emit... Do NOT emit!
if (msg->flags & INPUT_FILTER_FLAGS_DO_NOT_EMIT) {
return;
}
int fd = out_dev->gamepad_fd;
const uint32_t msg_flags = msg->data.event.ev_flags;
if ((msg_flags & EV_MESSAGE_FLAGS_IMU) != 0) {
fd = out_dev->imu_fd;
} else if ((msg_flags & EV_MESSAGE_FLAGS_MOUSE) != 0) {
fd = out_dev->mouse_fd;
} else {
fd = out_dev->gamepad_fd;
}
for (uint32_t i = 0; i < msg->data.event.ev_count; ++i) {
struct input_event ev = {
.code = msg->data.event.ev[i].code,
.type = msg->data.event.ev[i].type,
.value = msg->data.event.ev[i].value,
.time = msg->data.event.ev[i].time,
};
if ((msg_flags & EV_MESSAGE_FLAGS_PRESERVE_TIME) == 0) {
gettimeofday(&ev.time, NULL);
}
#if defined(INCLUDE_OUTPUT_DEBUG)
printf(
"Output: Received event %s (%s): %d\n",
libevdev_event_type_get_name(ev.type),
libevdev_event_code_get_name(ev.type, ev.code),
ev.value
);
#endif
const ssize_t written = write(fd, (void*)&ev, sizeof(ev));
if (written != sizeof(ev)) {
fprintf(
stderr,
"Error writing event %s %s %d: written %ld bytes out of %ld\n",
libevdev_event_type_get_name(ev.type),
libevdev_event_code_get_name(ev.type, ev.code),
ev.value,
written,
sizeof(ev)
);
}
}
#if defined(INCLUDE_TIMESTAMP)
gettimeofday(&now, NULL);
const struct input_event timestamp_ev = {
.code = MSC_TIMESTAMP,
.type = EV_MSC,
.value = (now.tv_sec - secAtInit)*1000000 + (now.tv_usec - usecAtInit),
.time = now,
};
const ssize_t timestamp_written = write(fd, (void*)&timestamp_ev, sizeof(timestamp_ev));
if (timestamp_written != sizeof(timestamp_ev)) {
fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", timestamp_written, sizeof(timestamp_ev));
}
#endif
struct timeval now = {0};
gettimeofday(&now, NULL);
const struct input_event syn_ev = {
.code = SYN_REPORT,
.type = EV_SYN,
.value = 0,
.time = now,
};
const ssize_t sync_written = write(fd, (void*)&syn_ev, sizeof(syn_ev));
if (sync_written != sizeof(syn_ev)) {
fprintf(stderr, "Error in sync: written %ld bytes out of %ld\n", sync_written, sizeof(syn_ev));
}
}
*/

47
virt_evdev.h Normal file
View file

@ -0,0 +1,47 @@
#pragma once
#include "logic.h"
#undef VIRT_EVDEV_DEBUG
// Emulates a "Generic" controller:
#define OUTPUT_DEV_NAME "ROGueENEMY"
#define OUTPUT_DEV_VENDOR_ID 0x108c
#define OUTPUT_DEV_PRODUCT_ID 0x0323
#define OUTPUT_DEV_VERSION 0x0111
/*
// Emulates a steam controller
#define OUTPUT_DEV_NAME "Steam Controller"
#define OUTPUT_DEV_VENDOR_ID 0x28de
#define OUTPUT_DEV_PRODUCT_ID 0x1102
#define OUTPUT_DEV_VERSION 0x0111
#define OUTPUT_DEV_BUS_TYPE BUS_USB
*/
/*
//Emulates an Xbox one wireless controller:
#define OUTPUT_DEV_NAME "Xbox Wireless Controller"
#define OUTPUT_DEV_VENDOR_ID 0x045e
#define OUTPUT_DEV_PRODUCT_ID 0x028e
#define OUTPUT_DEV_BUS_TYPE BUS_BLUETOOTH
*/
/*
// Emulates a DualShock controller
#define OUTPUT_DEV_NAME "Sony Interactive Entertainment DualSense Wireless Controller"
#define OUTPUT_DEV_VENDOR_ID 0x054c
#define OUTPUT_DEV_PRODUCT_ID 0x0ce6
#define OUTPUT_DEV_VERSION 0x8111
#define OUTPUT_DEV_BUS_TYPE BUS_USB
*/
#define PHYS_STR "00:11:22:33:44:55"
#define ACCEL_RANGE 512
#define GYRO_RANGE 2000 // max range is +/- 35 radian/s
#define GYRO_DEADZONE 1 // degrees/s to count as zero movement
void *virt_evdev_thread_func(void *ptr);

123
virt_mouse_kbd.c Normal file
View file

@ -0,0 +1,123 @@
#include "virt_mouse_kbd.h"
static const char* uinput_path = "/dev/uinput";
static int create_mouse_uinput() {
int fd = open(uinput_path, O_WRONLY | O_NONBLOCK);
if(fd < 0) {
fd = -1;
close(fd);
goto create_mouse_uinput_err;
}
struct uinput_setup dev = {0};
strncpy(dev.name, OUTPUT_DEV_NAME, UINPUT_MAX_NAME_SIZE-1);
#if defined(OUTPUT_DEV_BUS_TYPE)
dev.id.bustype = OUTPUT_DEV_BUS_TYPE;
#else
dev.id.bustype = BUS_VIRTUAL;
#endif
dev.id.vendor = OUTPUT_DEV_VENDOR_ID;
dev.id.product = OUTPUT_DEV_PRODUCT_ID;
#if defined(OUTPUT_DEV_VERSION)
dev.id.version = OUTPUT_DEV_VERSION;
#endif
//if (ioctl(fd, /*UI_SET_PHYS_STR*/ 18, PHYS_STR) != 0) {
// fprintf(stderr, "Controller and gyroscope will NOT be recognized as a single device!\n");
//}
if (ioctl(fd, UI_SET_PHYS, PHYS_STR) != 0) {
fprintf(stderr, "Error setting the phys of the virtual controller.\n");
}
#if !defined(UI_SET_PHYS_STR)
#warning Will not change the PHYS
#endif
#if !defined(UI_SET_UNIQ_STR)
#warning Will not change the UNIQ
#endif
ioctl(fd, UI_SET_EVBIT, EV_REL);
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_EVBIT, EV_MSC);
ioctl(fd, UI_SET_EVBIT, EV_SYN);
#if defined(INCLUDE_TIMESTAMP)
ioctl(fd, UI_SET_MSCBIT, MSC_TIMESTAMP);
#endif
ioctl(fd, UI_SET_KEYBIT, BTN_LEFT);
ioctl(fd, UI_SET_KEYBIT, BTN_MIDDLE);
ioctl(fd, UI_SET_KEYBIT, BTN_RIGHT);
//ioctl(fd, UI_SET_KEYBIT, BTN_SIDE);
//ioctl(fd, UI_SET_KEYBIT, BTN_EXTRA);
ioctl(fd, UI_SET_RELBIT, REL_X);
ioctl(fd, UI_SET_RELBIT, REL_Y);
ioctl(fd, UI_SET_RELBIT, REL_WHEEL);
ioctl(fd, UI_SET_RELBIT, REL_WHEEL_HI_RES);
if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) {
fd = -1;
close(fd);
goto create_mouse_uinput_err;
}
if(ioctl(fd, UI_DEV_CREATE) < 0) {
fd = -1;
close(fd);
goto create_mouse_uinput_err;
}
create_mouse_uinput_err:
return fd;
}
static int create_kb_uinput() {
return -EINVAL;
}
void *virt_mouse_kbd_thread_func(void *ptr) {
devices_status_t *const stats = (devices_status_t*)ptr;
const int mouse_fd = create_mouse_uinput();
if (mouse_fd != 0) {
fprintf(stderr, "Unable to create the mouse virtual evdev: %d\n", mouse_fd);
return NULL;
}
keyboard_status_t prev_kbd_stats;
kbd_status_init(&prev_kbd_stats);
for (;;) {
usleep(1250);
const int lock_res = pthread_mutex_lock(&stats->mutex);
if (lock_res != 0) {
printf("Unable to lock gamepad mutex: %d\n", lock_res);
continue;
}
// main virtual device logic
{
//event(fd, &stats->gamepad);
if (stats->kbd.connected) {
// TODO: do whatever it takes...
} else {
printf("kbd&mouse has been terminated: closing the device.\n");
break;
}
}
pthread_mutex_unlock(&stats->mutex);
}
return NULL;
}

16
virt_mouse_kbd.h Normal file
View file

@ -0,0 +1,16 @@
#pragma once
#include "logic.h"
#undef VIRT_EVDEV_DEBUG
// Emulates a "Generic" controller:
#define OUTPUT_DEV_NAME "ROGueENEMY"
#define OUTPUT_DEV_VENDOR_ID 0x108c
#define OUTPUT_DEV_PRODUCT_ID 0x0323
#define OUTPUT_DEV_VERSION 0x0111
#define PHYS_STR "00:11:22:33:44:55"
void *virt_mouse_kbd_thread_func(void *ptr);