From d6e6162db5707ea4ddb2fba9a34cae9ea4719983 Mon Sep 17 00:00:00 2001 From: Denis Date: Wed, 6 Dec 2023 00:32:51 +0100 Subject: [PATCH] Code refactor --- CMakeLists.txt | 2 +- Makefile | 2 +- TODO.md | 5 + devices_status.c | 45 +++ devices_status.h | 73 ++++ logic.c | 142 ++++---- logic.h | 99 ++---- main.c | 146 +++----- output_dev.c | 848 ++++++++--------------------------------------- output_dev.h | 51 --- platform.c | 24 +- platform.h | 5 - rogue_enemy.h | 2 +- settings.c | 10 +- settings.h | 10 +- virt_ds4.c | 189 +++++------ virt_ds5.c | 184 ++++------ virt_evdev.c | 570 +++++++++++++++++++++++++++++++ virt_evdev.h | 47 +++ virt_mouse_kbd.c | 123 +++++++ virt_mouse_kbd.h | 16 + 21 files changed, 1342 insertions(+), 1251 deletions(-) create mode 100644 TODO.md create mode 100644 devices_status.c create mode 100644 devices_status.h create mode 100644 virt_evdev.c create mode 100644 virt_evdev.h create mode 100644 virt_mouse_kbd.c create mode 100644 virt_mouse_kbd.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a307515..b27cb58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/Makefile b/Makefile index 4112201..82df7f0 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..757f273 --- /dev/null +++ b/TODO.md @@ -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 \ No newline at end of file diff --git a/devices_status.c b/devices_status.c new file mode 100644 index 0000000..c3d0e82 --- /dev/null +++ b/devices_status.c @@ -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 +} \ No newline at end of file diff --git a/devices_status.h b/devices_status.h new file mode 100644 index 0000000..b3a95e6 --- /dev/null +++ b/devices_status.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include + +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); \ No newline at end of file diff --git a/logic.c b/logic.c index 97749d7..c526960 100644 --- a/logic.c +++ b/logic.c @@ -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; @@ -195,4 +211,4 @@ void logic_request_termination(logic_t *const logic) { int logic_termination_requested(logic_t *const logic) { return (logic->flags & LOGIC_FLAGS_TERMINATION_REQUESTED) != 0; -} \ No newline at end of file +} diff --git a/logic.h b/logic.h index 428794b..4ac30f3 100644 --- a/logic.h +++ b/logic.h @@ -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); \ No newline at end of file diff --git a/main.c b/main.c index 3b43ee3..75895f2 100644 --- a/main.c +++ b/main.c @@ -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,50 +93,35 @@ 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) { fprintf(stderr, "Error creating gamepad output thread: %d\n", gamepad_thread_creation); @@ -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; } diff --git a/output_dev.c b/output_dev.c index 0b6afb6..347c9ed 100644 --- a/output_dev.c +++ b/output_dev.c @@ -5,537 +5,14 @@ #include "message.h" #include "settings.h" #include "virt_ds4.h" +#include -int create_output_dev(const char* uinput_path, output_dev_type_t type) { - int fd = open(uinput_path, O_WRONLY | O_NONBLOCK); - if(fd < 0) { - fd = -1; - close(fd); - goto create_output_dev_err; - } - - struct uinput_setup dev = {0}; - strncpy(dev.name, OUTPUT_DEV_NAME, UINPUT_MAX_NAME_SIZE-1); +#define DECODE_EV_FLAG_MODE_SWITCH_REQUESTED 0x00000001U +#define DECODE_EV_FLAG_MODE_MAIN_MENU_REQUESTED 0x00000002U +#define DECODE_EV_FLAG_MODE_QAM_REQUESTED 0x00000004U -#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 - - switch (type) { - case output_dev_imu: { -#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_output_dev_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_output_dev_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_output_dev_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_output_dev_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_output_dev_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_output_dev_err; - } - - if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) { - fd = -1; - close(fd); - goto create_output_dev_err; - } - - if(ioctl(fd, UI_DEV_CREATE) < 0) { - fd = -1; - close(fd); - goto create_output_dev_err; - } - - break; - } - - case output_dev_gamepad: { -#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_output_dev_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_output_dev_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_output_dev_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_output_dev_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_output_dev_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_output_dev_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_output_dev_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_output_dev_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_output_dev_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_output_dev_err; - } - - if(ioctl(fd, UI_DEV_SETUP, &dev) < 0) { - fd = -1; - close(fd); - goto create_output_dev_err; - } - - if(ioctl(fd, UI_DEV_CREATE) < 0) { - fd = -1; - close(fd); - goto create_output_dev_err; - } - - break; - } - - case output_dev_mouse: { - 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_output_dev_err; - } - - if(ioctl(fd, UI_DEV_CREATE) < 0) { - fd = -1; - close(fd); - goto create_output_dev_err; - } - - break; - } - - default: - // error - close(fd); - fd = -1; - goto create_output_dev_err; - } - -create_output_dev_err: - return fd; -} - -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*)×tamp_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)); - } -} - -static void decode_ev(output_dev_t *const out_dev, message_t *const msg) { - static int F15_status = 0; - static int gyroscope_mouse_translation = 0; +static uint32_t decode_ev(output_dev_t *const out_dev, message_t *const msg) { + uint32_t flags = 0x00000000U; // scan for mouse mode and emit events in the virtual mouse if required if ((is_rc71l_ready(out_dev->logic)) && (is_mouse_mode(&out_dev->logic->platform))) { @@ -544,11 +21,11 @@ static void decode_ev(output_dev_t *const out_dev, message_t *const msg) { if (msg->data.event.ev[a].type == EV_KEY) { if ((msg->data.event.ev[a].code == BTN_MIDDLE) || (msg->data.event.ev[a].code == BTN_LEFT) || (msg->data.event.ev[a].code == BTN_RIGHT)) { msg->data.event.ev_flags |= EV_MESSAGE_FLAGS_PRESERVE_TIME | EV_MESSAGE_FLAGS_MOUSE; - return; + return flags; } } else if (msg->data.event.ev[a].type == EV_REL) { msg->data.event.ev_flags |= EV_MESSAGE_FLAGS_PRESERVE_TIME | EV_MESSAGE_FLAGS_MOUSE; - return; + return flags; } } } @@ -558,26 +35,7 @@ static void decode_ev(output_dev_t *const out_dev, message_t *const msg) { } else if ((msg->data.event.ev_count >= 2) && (msg->data.event.ev[0].type == EV_MSC) && (msg->data.event.ev[0].code == MSC_SCAN)) { if ((msg->data.event.ev[0].value == -13565784) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F18)) { if (msg->data.event.ev[1].value == 1) { - printf("Detected mode switch command, switching mode...\n"); - - const int new_mode = cycle_mode(&out_dev->logic->platform); - - if (new_mode < 0) { - fprintf(stderr, "Error in mode switching: %d\n", new_mode); - } else { - printf("Mode correctly switched to %d\n", new_mode); - - if (new_mode == 0) { - printf("Mode switched to virtual DualSense for game mode.\n"); - out_dev->logic->gamepad_output = (out_dev->logic->flags & LOGIC_FLAGS_VIRT_DS5_ENABLE) ? GAMEPAD_OUTPUT_DS5 : ((out_dev->logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) ? GAMEPAD_OUTPUT_DS4: GAMEPAD_OUTPUT_EVDEV); - } else if (new_mode == 1) { - printf("Mode switched to virtual evdev for lizard mode.\n"); - out_dev->logic->gamepad_output = GAMEPAD_OUTPUT_EVDEV; - } else if (new_mode == 2) { - printf("Mode switched to virtual DualShock for macro mode.\n"); - out_dev->logic->gamepad_output = (out_dev->logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) ? GAMEPAD_OUTPUT_DS4 : GAMEPAD_OUTPUT_EVDEV; - } - } + flags |= DECODE_EV_FLAG_MODE_SWITCH_REQUESTED; } else { // Do nothing effectively discarding the input } @@ -585,69 +43,35 @@ static void decode_ev(output_dev_t *const out_dev, message_t *const msg) { msg->flags |= INPUT_FILTER_FLAGS_DO_NOT_EMIT; } else if (msg->data.event.ev[0].value == -13565784) { msg->flags |= INPUT_FILTER_FLAGS_DO_NOT_EMIT; - } else if ((msg->data.event.ev_count == 2) && (msg->data.event.ev[0].value == 458860) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F17)) { - if (is_rc71l_ready(out_dev->logic)) { - if (is_mouse_mode(&out_dev->logic->platform)) { - if (msg->data.event.ev[1].value < 2) { - msg->data.event.ev_count = 1; - msg->data.event.ev[0].type = EV_KEY; - msg->data.event.ev[0].code = BTN_GEAR_DOWN; - msg->data.event.ev[0].value = msg->data.event.ev[1].value; - return; - } - } else if (is_gamepad_mode(&out_dev->logic->platform)) { - if (msg->data.event.ev[1].value == 0) { - --gyroscope_mouse_translation; - } else if (msg->data.event.ev[1].value == 1) { - ++gyroscope_mouse_translation; - } - } - - msg->flags |= INPUT_FILTER_FLAGS_DO_NOT_EMIT; - } - } else if ((msg->data.event.ev_count == 2) && (msg->data.event.ev[0].value == 458861) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F18)) { - if (is_rc71l_ready(out_dev->logic)) { - if (is_mouse_mode(&out_dev->logic->platform)) { - if (msg->data.event.ev[1].value < 2) { - msg->data.event.ev_count = 1; - msg->data.event.ev[0].type = EV_KEY; - msg->data.event.ev[0].code = BTN_GEAR_UP; - msg->data.event.ev[0].value = msg->data.event.ev[1].value; - } - } else if (is_gamepad_mode(&out_dev->logic->platform)) { - if (msg->data.event.ev[1].value == 0) { - --gyroscope_mouse_translation; - } else if (msg->data.event.ev[1].value == 1) { - ++gyroscope_mouse_translation; - } - - msg->flags |= INPUT_FILTER_FLAGS_DO_NOT_EMIT; - } - } + } else if ( + (msg->data.event.ev_count == 2) && + (msg->data.event.ev[0].value == 458860) && + (msg->data.event.ev[1].type == EV_KEY) && + (msg->data.event.ev[1].code == KEY_F17) + ) { + // this is the left back paddle while in game mode or mouse mode + } else if ( + (msg->data.event.ev_count == 2) && + (msg->data.event.ev[0].value == 458861) && + (msg->data.event.ev[1].type == EV_KEY) && + (msg->data.event.ev[1].code == KEY_F18) + ) { + // this is the left back paddle while in game mode or mouse mode } else if ((msg->data.event.ev_count == 2) && (msg->data.event.ev[0].value == -13565786) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F16)) { printf("Converted AC short-press button to BTN_MODE\n"); msg->data.event.ev_count = 1; msg->data.event.ev[0].type = EV_KEY; msg->data.event.ev[0].code = BTN_MODE; msg->data.event.ev[0].value = msg->data.event.ev[1].value; - } else if ((msg->data.event.ev_count == 2) && (msg->data.event.ev[0].value == -13565787) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F15)) { - if (msg->data.event.ev[1].value == 0) { - if (F15_status > 0) { - --F15_status; - } - if (F15_status == 0) { - printf("Exiting gyro mode.\n"); - } - } else if (msg->data.event.ev[1].value == 1) { - if (F15_status <= 2) { - ++F15_status; - } - - if (F15_status == 1) { - printf("Entering gyro mode.\n"); - } - } + flags |= DECODE_EV_FLAG_MODE_MAIN_MENU_REQUESTED; + } else if ( + (msg->data.event.ev_count == 2) && + (msg->data.event.ev[0].value == -13565787) && + (msg->data.event.ev[1].type == EV_KEY) && + (msg->data.event.ev[1].code == KEY_F15) + ) { + // this is default mode (deprecated) M15 for both back buttons and one was pressed. good luck with that! msg->flags |= INPUT_FILTER_FLAGS_DO_NOT_EMIT; } else if ( @@ -657,38 +81,14 @@ static void decode_ev(output_dev_t *const out_dev, message_t *const msg) { (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_PROG1) ) { - // do nothing to match the DS4 joystick - /* - msg->data.event.ev_count = 3; - - const int32_t val = msg->data.event.ev[1].value; - //struct timeval time = msg->data.event.ev[1].time; - - msg->data.event.ev[0].type = EV_KEY; - msg->data.event.ev[0].code = BTN_MODE; - msg->data.event.ev[0].value = val; - - msg->data.event.ev[1].type = SYN_REPORT; - msg->data.event.ev[1].code = EV_SYN; - msg->data.event.ev[1].value = 0; - - msg->data.event.ev[2].type = EV_KEY; - msg->data.event.ev[2].code = BTN_SOUTH; - msg->data.event.ev[2].value = val; - - events[3].type = SYN_REPORT; - events[3].code = EV_SYN; - events[3].value = 0; - - events[4].type = EV_KEY; - events[4].code = BTN_SOUTH; - events[4].value = 0; - */ + flags |= DECODE_EV_FLAG_MODE_QAM_REQUESTED; } } + + return flags; } -static void update_gs_from_ev(gamepad_status_t *const gs, message_t *const msg, controller_settings_t *const settings) { +static void update_gs_from_ev(devices_status_t *const stats, message_t *const msg, controller_settings_t *const settings) { if ( // this is what happens at release of the left-screen button of the ROG Ally (msg->data.event.ev_count == 2) && (msg->data.event.ev[0].type == EV_MSC) && @@ -701,7 +101,7 @@ static void update_gs_from_ev(gamepad_status_t *const gs, message_t *const msg, #if defined(INCLUDE_OUTPUT_DEBUG) printf("RC71L CC button short-press detected\n"); #endif - gs->flags |= GAMEPAD_STATUS_FLAGS_PRESS_AND_REALEASE_CENTER; + stats->gamepad.flags |= GAMEPAD_STATUS_FLAGS_PRESS_AND_REALEASE_CENTER; } else if ( // this is what happens at release of the right-screen button of the ROG Ally (msg->data.event.ev_count == 2) && (msg->data.event.ev[0].type == EV_MSC) && @@ -715,7 +115,7 @@ static void update_gs_from_ev(gamepad_status_t *const gs, message_t *const msg, printf("RC71L AC button short-press detected\n"); #endif if (settings->enable_qam) { - gs->flags |= GAMEPAD_STATUS_FLAGS_OPEN_STEAM_QAM; + stats->gamepad.flags |= GAMEPAD_STATUS_FLAGS_OPEN_STEAM_QAM; } } else if ( (msg->data.event.ev_count == 2) && @@ -725,7 +125,7 @@ static void update_gs_from_ev(gamepad_status_t *const gs, message_t *const msg, (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F17) ) { - gs->l4 = msg->data.event.ev[1].value; + stats->gamepad.l4 = msg->data.event.ev[1].value; } else if ( (msg->data.event.ev_count == 2) && (msg->data.event.ev[0].type == EV_MSC) && @@ -734,140 +134,161 @@ static void update_gs_from_ev(gamepad_status_t *const gs, message_t *const msg, (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F18) ) { - gs->r4 = msg->data.event.ev[1].value; + stats->gamepad.r4 = msg->data.event.ev[1].value; } for (uint32_t i = 0; i < msg->data.event.ev_count; ++i) { if (msg->data.event.ev[i].type == EV_KEY) { if (msg->data.event.ev[i].code == BTN_EAST) { if (settings->nintendo_layout) { - gs->cross = msg->data.event.ev[i].value; + stats->gamepad.cross = msg->data.event.ev[i].value; } else { - gs->circle = msg->data.event.ev[i].value; + stats->gamepad.circle = msg->data.event.ev[i].value; } } else if (msg->data.event.ev[i].code == BTN_NORTH) { if (settings->nintendo_layout) { - gs->triangle = msg->data.event.ev[i].value; + stats->gamepad.triangle = msg->data.event.ev[i].value; } else { - gs->square = msg->data.event.ev[i].value; + stats->gamepad.square = msg->data.event.ev[i].value; } } else if (msg->data.event.ev[i].code == BTN_SOUTH) { if (settings->nintendo_layout) { - gs->circle = msg->data.event.ev[i].value; + stats->gamepad.circle = msg->data.event.ev[i].value; } else { - gs->cross = msg->data.event.ev[i].value; + stats->gamepad.cross = msg->data.event.ev[i].value; } } else if (msg->data.event.ev[i].code == BTN_WEST) { if (settings->nintendo_layout) { - gs->square = msg->data.event.ev[i].value; + stats->gamepad.square = msg->data.event.ev[i].value; } else { - gs->triangle = msg->data.event.ev[i].value; + stats->gamepad.triangle = msg->data.event.ev[i].value; } } else if (msg->data.event.ev[i].code == BTN_SELECT) { - gs->option = msg->data.event.ev[i].value; + stats->gamepad.option = msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == BTN_START) { - gs->share = msg->data.event.ev[i].value; + stats->gamepad.share = msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == BTN_TR) { - gs->r1 = msg->data.event.ev[i].value; + stats->gamepad.r1 = msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == BTN_TL) { - gs->l1 = msg->data.event.ev[i].value; + stats->gamepad.l1 = msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == BTN_THUMBR) { - gs->r3 = msg->data.event.ev[i].value; + stats->gamepad.r3 = msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == BTN_THUMBL) { - gs->l3 = msg->data.event.ev[i].value; + stats->gamepad.l3 = msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == BTN_MODE) { - gs->flags |= GAMEPAD_STATUS_FLAGS_PRESS_AND_REALEASE_CENTER; + stats->gamepad.flags |= GAMEPAD_STATUS_FLAGS_PRESS_AND_REALEASE_CENTER; } } else if (msg->data.event.ev[i].type == EV_ABS) { if (msg->data.event.ev[i].code == ABS_X) { - gs->joystick_positions[0][0] = (int32_t)msg->data.event.ev[i].value; + stats->gamepad.joystick_positions[0][0] = (int32_t)msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == ABS_Y) { - gs->joystick_positions[0][1] = (int32_t)msg->data.event.ev[i].value; + stats->gamepad.joystick_positions[0][1] = (int32_t)msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == ABS_RX) { - gs->joystick_positions[1][0] = (int32_t)msg->data.event.ev[i].value; + stats->gamepad.joystick_positions[1][0] = (int32_t)msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == ABS_RY) { - gs->joystick_positions[1][1] = (int32_t)msg->data.event.ev[i].value; + stats->gamepad.joystick_positions[1][1] = (int32_t)msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == ABS_Z) { - gs->l2_trigger = (int32_t)msg->data.event.ev[i].value; + stats->gamepad.l2_trigger = (int32_t)msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == ABS_RZ) { - gs->r2_trigger = (int32_t)msg->data.event.ev[i].value; + stats->gamepad.r2_trigger = (int32_t)msg->data.event.ev[i].value; } else if (msg->data.event.ev[i].code == ABS_HAT0X) { const int v = msg->data.event.ev[i].value; - gs->dpad &= 0xF0; + stats->gamepad.dpad &= 0xF0; if (v == 0) { - gs->dpad |= 0x00; + stats->gamepad.dpad |= 0x00; } else if (v == 1) { - gs->dpad |= 0x01; + stats->gamepad.dpad |= 0x01; } else if (v == -1) { - gs->dpad |= 0x02; + stats->gamepad.dpad |= 0x02; } } else if (msg->data.event.ev[i].code == ABS_HAT0Y) { const int v = msg->data.event.ev[i].value; - gs->dpad &= 0x0F; + stats->gamepad.dpad &= 0x0F; if (v == 0) { - gs->dpad |= 0x00; + stats->gamepad.dpad |= 0x00; } else if (v == 1) { - gs->dpad |= 0x20; + stats->gamepad.dpad |= 0x20; } else if (v == -1) { - gs->dpad |= 0x10; + stats->gamepad.dpad |= 0x10; } } } } } +static void update_gs_from_imu(devices_status_t *const stats, message_t *const msg, controller_settings_t *const settings) { + if (msg->data.imu.flags & IMU_MESSAGE_FLAGS_ANGLVEL) { + stats->gamepad.last_gyro_motion_time = msg->data.imu.gyro_read_time; + + memcpy(stats->gamepad.gyro, msg->data.imu.gyro_rad_s, sizeof(double[3])); + + stats->gamepad.raw_gyro[0] = msg->data.imu.gyro_x_raw; + stats->gamepad.raw_gyro[1] = msg->data.imu.gyro_y_raw; + stats->gamepad.raw_gyro[2] = msg->data.imu.gyro_z_raw; + } + + if (msg->data.imu.flags & IMU_MESSAGE_FLAGS_ACCEL) { + stats->gamepad.last_accel_motion_time = msg->data.imu.accel_read_time; + + memcpy(stats->gamepad.accel, msg->data.imu.accel_m2s, sizeof(double[3])); + + stats->gamepad.raw_accel[0] = msg->data.imu.accel_x_raw; + stats->gamepad.raw_accel[1] = msg->data.imu.accel_y_raw; + stats->gamepad.raw_accel[2] = msg->data.imu.accel_z_raw; + } +} + static void handle_msg(output_dev_t *const out_dev, message_t *const msg) { if (msg->type == MSG_TYPE_EV) { - decode_ev(out_dev, msg); + const uint32_t decode_ev_res_flags = decode_ev(out_dev, msg); - // TODO: the mode could have been switched by decode_ev so change the output device too - - const int upd_beg_res = logic_begin_status_update(out_dev->logic); - if (upd_beg_res == 0) { - update_gs_from_ev(&out_dev->logic->gamepad, msg, &out_dev->logic->controller_settings); + if (decode_ev_res_flags & DECODE_EV_FLAG_MODE_SWITCH_REQUESTED) { + printf("Detected mode switch command, switching mode...\n"); - logic_end_status_update(out_dev->logic); - } else { - fprintf(stderr, "[ev] Unable to begin the gamepad status update: %d\n", upd_beg_res); - } + // lock the mutex to flag every device as disconnected + pthread_mutex_lock(&out_dev->logic->dev_stats.mutex); + out_dev->logic->dev_stats.gamepad.connected = false; + out_dev->logic->dev_stats.kbd.connected = false; + pthread_mutex_unlock(&out_dev->logic->dev_stats.mutex); - if (out_dev->logic->gamepad_output == GAMEPAD_OUTPUT_EVDEV) { - emit_ev(out_dev, msg); - } - } else if (msg->type == MSG_TYPE_IMU) { - const int upd_beg_res = logic_begin_status_update(out_dev->logic); - if (upd_beg_res == 0) { - if (msg->data.imu.flags & IMU_MESSAGE_FLAGS_ANGLVEL) { - out_dev->logic->gamepad.last_gyro_motion_time = msg->data.imu.gyro_read_time; + // wait for the thread to terminate itself... + logic_terminate_output_thread(out_dev->logic); - memcpy(out_dev->logic->gamepad.gyro, msg->data.imu.gyro_rad_s, sizeof(double[3])); + // re-lock the mutex and flag the controller as connected so that the new thread won't exit immediatly + // as the other gamepad-accessing thread is now joined (has terminated) it's safe to update the state without locking the mutex + out_dev->logic->dev_stats.gamepad.connected = true; - out_dev->logic->gamepad.raw_gyro[0] = msg->data.imu.gyro_x_raw; - out_dev->logic->gamepad.raw_gyro[1] = msg->data.imu.gyro_y_raw; - out_dev->logic->gamepad.raw_gyro[2] = msg->data.imu.gyro_z_raw; + const int cycle_mode_res = cycle_mode(&out_dev->logic->platform); + if (cycle_mode_res == 0) { + const int output_thread_start_res = (is_mouse_mode(&out_dev->logic->platform)) ? + logic_start_output_mouse_kbd_thread(out_dev->logic) : + logic_start_output_dev_thread(out_dev->logic); + + if (output_thread_start_res != 0) { + fprintf(stderr, "Unable to start output thread: %d -- don't panic, another mode switch will retry.", output_thread_start_res); + } + } else { + fprintf(stderr, "Error in mode switching: %d\n", cycle_mode_res); } - - if (msg->data.imu.flags & IMU_MESSAGE_FLAGS_ACCEL) { - out_dev->logic->gamepad.last_accel_motion_time = msg->data.imu.accel_read_time; - - memcpy(out_dev->logic->gamepad.accel, msg->data.imu.accel_m2s, sizeof(double[3])); - - out_dev->logic->gamepad.raw_accel[0] = msg->data.imu.accel_x_raw; - out_dev->logic->gamepad.raw_accel[1] = msg->data.imu.accel_y_raw; - out_dev->logic->gamepad.raw_accel[2] = msg->data.imu.accel_z_raw; - - logic_end_status_update(out_dev->logic); - } - -#if defined(INCLUDE_OUTPUT_DEBUG) - printf("gyro_x: %d\t\t| gyro_y: %d\t\t| gyro_z: %d\t\t\n", (int)msg->data.imu.gyro_x_raw, (int)msg->data.imu.gyro_y_raw, (int)msg->data.imu.gyro_z_raw); -#endif - } else { - fprintf(stderr, "[imu] Unable to begin the gamepad status update: %d\n", upd_beg_res); } } + + const int upd_beg_res = pthread_mutex_lock(&out_dev->logic->dev_stats.mutex); + if (upd_beg_res != 0) { + fprintf(stderr, "Unable to begin the gamepad status update (can't lock mutex): %d\n", upd_beg_res); + return; + } + + if (msg->type == MSG_TYPE_EV) { + update_gs_from_ev(&out_dev->logic->dev_stats, msg, &out_dev->logic->controller_settings); + } else if (msg->type == MSG_TYPE_IMU) { + update_gs_from_imu(&out_dev->logic->dev_stats, msg, &out_dev->logic->controller_settings); + } + + pthread_mutex_unlock(&out_dev->logic->dev_stats.mutex); } +/* void *output_dev_rumble_thread_func(void* ptr) { output_dev_t *const out_dev = (output_dev_t*)ptr; @@ -931,24 +352,25 @@ void *output_dev_rumble_thread_func(void* ptr) { return NULL; } +*/ void *output_dev_thread_func(void *ptr) { output_dev_t *const out_dev = (output_dev_t*)ptr; - struct timeval now = {0}; - #if defined(INCLUDE_TIMESTAMP) + struct timeval now = {0}; gettimeofday(&now, NULL); __time_t secAtInit = now.tv_sec; __time_t usecAtInit = now.tv_usec; #endif +/* pthread_t rumble_thread; const int rumble_thread_creation = pthread_create(&rumble_thread, NULL, output_dev_rumble_thread_func, ptr); if (rumble_thread_creation != 0) { fprintf(stderr, "Error creating the rumble thread: %d -- rumble will not work.\n", rumble_thread_creation); } - +*/ for (;;) { void *raw_ev; const int pop_res = queue_pop_timeout(&out_dev->logic->input_queue, &raw_ev, 5000); @@ -969,8 +391,8 @@ void *output_dev_thread_func(void *ptr) { break; } } - +/* pthread_join(rumble_thread, NULL); - +*/ return NULL; } diff --git a/output_dev.h b/output_dev.h index 4cf536e..dfae899 100644 --- a/output_dev.h +++ b/output_dev.h @@ -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); diff --git a/platform.c b/platform.c index bbee4c7..3322bb8 100644 --- a/platform.c +++ b/platform.c @@ -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; - } + free(dev_name); - 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; -} diff --git a/platform.h b/platform.h index 920a28f..26cbf0f 100644 --- a/platform.h +++ b/platform.h @@ -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); - diff --git a/rogue_enemy.h b/rogue_enemy.h index a700852..03fa521 100644 --- a/rogue_enemy.h +++ b/rogue_enemy.h @@ -1,11 +1,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include diff --git a/settings.c b/settings.c index a5d3568..9d5a02d 100644 --- a/settings.c +++ b/settings.c @@ -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,8 +47,15 @@ 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: return res; -} \ No newline at end of file +} diff --git a/settings.h b/settings.h index 119a279..52c941b 100644 --- a/settings.h +++ b/settings.h @@ -6,8 +6,16 @@ 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); -int fill_config(controller_settings_t *const conf, const char* file); \ No newline at end of file +int fill_config(controller_settings_t *const conf, const char* file); diff --git a/virt_ds4.c b/virt_ds4.c index ffccddd..d12476d 100644 --- a/virt_ds4.c +++ b/virt_ds4.c @@ -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,19 +733,6 @@ 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... - - struct uhid_event l = { - .type = UHID_INPUT2, - .u = { - .input2 = { - .size = 64, - } - } - }; - - memcpy(&l.u.input2.data[0], &buf[0], l.u.input2.size); - - return uhid_write(fd, &l); } /** @@ -798,48 +741,64 @@ static int send_data(int fd, logic_t *const logic) { * 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; + 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 (;;) { - if (logic->gamepad_output != GAMEPAD_OUTPUT_DS4) { - // sleep for 500ms before re-checking - usleep(500000); + 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; } - 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; - } + // main virtual device logic + { + event(fd, &stats->gamepad); - 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); - } + if (stats->gamepad.connected) { + compose_hid_report_buffer(fd, &stats->gamepad, buf); } else { printf("DualShock has been terminated: closing the device.\n"); - goto virt_ds4_thread_func_reset; + break; } } - -virt_ds4_thread_func_reset: - destroy(fd); + + pthread_mutex_unlock(&stats->mutex); + + struct uhid_event l = { + .type = UHID_INPUT2, + .u = { + .input2 = { + .size = 64, + } + } + }; + + memcpy(&l.u.input2.data[0], &buf[0], l.u.input2.size); + + uhid_write(fd, &l); } + + destroy(fd); return NULL; } diff --git a/virt_ds5.c b/virt_ds5.c index d1e91bc..95f8dc6 100644 --- a/virt_ds5.c +++ b/virt_ds5.c @@ -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; @@ -408,13 +357,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 @@ -472,19 +414,6 @@ 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... - - struct uhid_event l = { - .type = UHID_INPUT2, - .u = { - .input2 = { - .size = DS_INPUT_REPORT_USB_SIZE, - } - } - }; - - memcpy(&l.u.input2.data[0], &buf[0], l.u.input2.size); - - return uhid_write(fd, &l); } /** @@ -493,49 +422,64 @@ static int send_data(int fd, logic_t *const logic) { * 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; + devices_status_t *const stats = (devices_status_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); + 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, + .u = { + .input2 = { + .size = DS_INPUT_REPORT_USB_SIZE, + } + } + }; + + memcpy(&l.u.input2.data[0], &buf[0], l.u.input2.size); + + uhid_write(fd, &l); + } + + destroy(fd); return NULL; } diff --git a/virt_evdev.c b/virt_evdev.c new file mode 100644 index 0000000..a5a7294 --- /dev/null +++ b/virt_evdev.c @@ -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*)×tamp_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)); + } +} +*/ \ No newline at end of file diff --git a/virt_evdev.h b/virt_evdev.h new file mode 100644 index 0000000..0d99c29 --- /dev/null +++ b/virt_evdev.h @@ -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); diff --git a/virt_mouse_kbd.c b/virt_mouse_kbd.c new file mode 100644 index 0000000..4201440 --- /dev/null +++ b/virt_mouse_kbd.c @@ -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; +} \ No newline at end of file diff --git a/virt_mouse_kbd.h b/virt_mouse_kbd.h new file mode 100644 index 0000000..0b1b6c2 --- /dev/null +++ b/virt_mouse_kbd.h @@ -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);