From 9f2e1e9de5d66e931d40f347d82b30c214fd73af Mon Sep 17 00:00:00 2001 From: Denis Date: Sat, 25 Nov 2023 21:03:05 +0100 Subject: [PATCH] threading rework to support rumble --- input_dev.c | 9 ++---- input_dev.h | 4 --- logic.c | 8 +++++ logic.h | 11 +++++-- main.c | 29 +++++------------- output_dev.c | 85 +++++++++++++++++++++++++++++++++++++++------------- output_dev.h | 5 ---- 7 files changed, 91 insertions(+), 60 deletions(-) diff --git a/input_dev.c b/input_dev.c index e3f9728..b7ed041 100644 --- a/input_dev.c +++ b/input_dev.c @@ -1,4 +1,5 @@ #include "input_dev.h" +#include "logic.h" #include "message.h" #include "queue.h" #include "dev_iio.h" @@ -326,9 +327,7 @@ static void input_iio( int open_sysfs_idx = -1; for (;;) { - const uint32_t flags = in_dev->crtl_flags; - if (flags & INPUT_DEV_CTRL_FLAG_EXIT) { - in_dev->crtl_flags &= ~INPUT_DEV_CTRL_FLAG_EXIT; + if (logic_termination_requested(in_dev->logic)) { break; } @@ -435,9 +434,7 @@ static void input_udev( int open_sysfs_idx = -1; for (;;) { - const uint32_t flags = in_dev->crtl_flags; - if (flags & INPUT_DEV_CTRL_FLAG_EXIT) { - in_dev->crtl_flags &= ~INPUT_DEV_CTRL_FLAG_EXIT; + if (logic_termination_requested(in_dev->logic)) { break; } diff --git a/input_dev.h b/input_dev.h index 2ec642e..e3ef31f 100644 --- a/input_dev.h +++ b/input_dev.h @@ -7,8 +7,6 @@ #undef INCLUDE_INPUT_DEBUG #undef IGNORE_INPUT_SCAN -#define INPUT_DEV_CTRL_FLAG_EXIT 0x00000001U - typedef uint32_t (*ev_input_filter_t)(struct input_event*, size_t*, uint32_t*, uint32_t*); typedef enum input_dev_type { @@ -30,8 +28,6 @@ typedef struct input_dev { const uinput_filters_t* ev_filters; const iio_filters_t* iio_filters; - volatile uint32_t crtl_flags; - ev_input_filter_t ev_input_filter_fn; logic_t *logic; diff --git a/logic.c b/logic.c index 587ce05..1a5b75f 100644 --- a/logic.c +++ b/logic.c @@ -131,3 +131,11 @@ 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; +} + +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 12bf8eb..a1d8d79 100644 --- a/logic.h +++ b/logic.h @@ -46,8 +46,9 @@ typedef struct gamepad_status { } gamepad_status_t; -#define LOGIC_FLAGS_VIRT_DS4_ENABLE 0x00000001U -#define LOGIC_FLAGS_PLATFORM_ENABLE 0x00000002U +#define LOGIC_FLAGS_VIRT_DS4_ENABLE 0x00000001U +#define LOGIC_FLAGS_PLATFORM_ENABLE 0x00000002U +#define LOGIC_FLAGS_TERMINATION_REQUESTED 0x80000000U typedef enum gamepad_output { GAMEPAD_OUTPUT_EVDEV = 0, @@ -71,7 +72,7 @@ typedef struct logic { pthread_t virt_ds4_thread; - uint32_t flags; + 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; @@ -92,3 +93,7 @@ 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 9ff974a..3b43ee3 100644 --- a/main.c +++ b/main.c @@ -10,7 +10,6 @@ logic_t global_logic; static output_dev_t out_gamepadd_dev = { .gamepad_fd = -1, .imu_fd = -1, - .crtl_flags = 0x00000000U, .logic = &global_logic, }; @@ -20,7 +19,6 @@ static iio_filters_t in_iio_filters = { static input_dev_t in_iio_dev = { .dev_type = input_dev_type_iio, - .crtl_flags = 0x00000000U, .iio_filters = &in_iio_filters, .logic = &global_logic, //.input_filter_fn = input_filter_imu_identity, @@ -32,7 +30,6 @@ static uinput_filters_t in_asus_kb_1_filters = { static input_dev_t in_asus_kb_1_dev = { .dev_type = input_dev_type_uinput, - .crtl_flags = 0x00000000U, .ev_filters = &in_asus_kb_1_filters, .logic = &global_logic, .ev_input_filter_fn = input_filter_asus_kb, @@ -44,7 +41,6 @@ static uinput_filters_t in_asus_kb_2_filters = { static input_dev_t in_asus_kb_2_dev = { .dev_type = input_dev_type_uinput, - .crtl_flags = 0x00000000U, .ev_filters = &in_asus_kb_2_filters, .logic = &global_logic, .ev_input_filter_fn = input_filter_asus_kb, @@ -56,7 +52,6 @@ static uinput_filters_t in_asus_kb_3_filters = { static input_dev_t in_asus_kb_3_dev = { .dev_type = input_dev_type_uinput, - .crtl_flags = 0x00000000U, .ev_filters = &in_asus_kb_3_filters, .logic = &global_logic, .ev_input_filter_fn = input_filter_asus_kb, @@ -68,25 +63,15 @@ static uinput_filters_t in_xbox_filters = { static input_dev_t in_xbox_dev = { .dev_type = input_dev_type_uinput, - .crtl_flags = 0x00000000U, .ev_filters = &in_xbox_filters, .logic = &global_logic, .ev_input_filter_fn = input_filter_identity, }; -void request_termination(void) { - out_gamepadd_dev.crtl_flags |= OUTPUT_DEV_CTRL_FLAG_EXIT; - - in_xbox_dev.crtl_flags |= INPUT_DEV_CTRL_FLAG_EXIT; - in_asus_kb_3_dev.crtl_flags |= INPUT_DEV_CTRL_FLAG_EXIT; - in_asus_kb_2_dev.crtl_flags |= INPUT_DEV_CTRL_FLAG_EXIT; - in_asus_kb_1_dev.crtl_flags |= INPUT_DEV_CTRL_FLAG_EXIT; -} - void sig_handler(int signo) { if (signo == SIGINT) { - request_termination(); + logic_request_termination(&global_logic); printf("received SIGINT\n"); } } @@ -140,7 +125,7 @@ int main(int argc, char ** argv) { if (gamepad_thread_creation != 0) { fprintf(stderr, "Error creating gamepad output thread: %d\n", gamepad_thread_creation); ret = -1; - request_termination(); + logic_request_termination(&global_logic); goto gamepad_thread_err; } @@ -148,7 +133,7 @@ int main(int argc, char ** argv) { if (xbox_thread_creation != 0) { fprintf(stderr, "Error creating xbox input thread: %d\n", xbox_thread_creation); ret = -1; - request_termination(); + logic_request_termination(&global_logic); goto xbox_drv_thread_err; } @@ -156,7 +141,7 @@ int main(int argc, char ** argv) { 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; - request_termination(); + logic_request_termination(&global_logic); goto asus_kb_1_thread_err; } @@ -164,7 +149,7 @@ int main(int argc, char ** argv) { 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; - request_termination(); + logic_request_termination(&global_logic); goto asus_kb_2_thread_err; } @@ -172,7 +157,7 @@ int main(int argc, char ** argv) { 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; - request_termination(); + logic_request_termination(&global_logic); goto asus_kb_3_thread_err; } @@ -180,7 +165,7 @@ int main(int argc, char ** argv) { if (iio_thread_creation != 0) { fprintf(stderr, "Error creating iio input thread: %d\n", asus_kb_3_thread_creation); ret = -1; - request_termination(); + logic_request_termination(&global_logic); goto iio_thread_err; } diff --git a/output_dev.c b/output_dev.c index 1c0f4a2..d64cad4 100644 --- a/output_dev.c +++ b/output_dev.c @@ -806,7 +806,7 @@ static void handle_msg(output_dev_t *const out_dev, message_t *const msg) { } } -void *output_dev_thread_func(void *ptr) { +void *output_dev_rumble_thread_func(void* ptr) { output_dev_t *const out_dev = (output_dev_t*)ptr; struct timeval now = {0}; @@ -820,6 +820,67 @@ void *output_dev_thread_func(void *ptr) { pthread_mutex_lock(&out_dev->logic->gamepad_mutex); uint64_t rumble_events_count = out_dev->logic->gamepad.rumble_events_count; pthread_mutex_unlock(&out_dev->logic->gamepad_mutex); + + // maximum number of ms that the gamepad can remain in a blocked status + const int timeout_ms = 10; + + for (;;) { + // sleep for about 4ms: this is an aggressive polling for rumble. + usleep(4000); + + // here transmit the rumble request to the input-device-handling components + pthread_mutex_lock(&out_dev->logic->gamepad_mutex); + + // check if the gamepad has notified the presence of a rumble event + if (out_dev->logic->gamepad.rumble_events_count != rumble_events_count) { + + struct timespec timeout; + if (clock_gettime(CLOCK_MONOTONIC, &timeout) == 0) { + timeout.tv_sec += timeout_ms / 1000; + timeout.tv_nsec += (timeout_ms % 1000) * 1000000; + + int result = sem_timedwait(&out_dev->logic->rumble.sem_empty, &timeout); + + if (result == 0) { + // translate the rumble to evdev + out_dev->logic->rumble.value = out_dev->logic->gamepad.motors_intensity[0] * 255; + + // wake up the input thread that will propagate the rumble to raw devices. + sem_post(&out_dev->logic->rumble.sem_full); + + // update the rumble events counter: this rumble event was handled + rumble_events_count = out_dev->logic->gamepad.rumble_events_count; + } + } + } + + pthread_mutex_unlock(&out_dev->logic->gamepad_mutex); + + if (logic_termination_requested(out_dev->logic)) { + break; + } + } + + 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) + 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, 1000); @@ -835,29 +896,13 @@ void *output_dev_thread_func(void *ptr) { fprintf(stderr, "Cannot read from input queue: %d\n", pop_res); continue; } - /* - // here transmit the rumble request to the input-device-handling components - pthread_mutex_lock(&out_dev->logic->gamepad_mutex); - - // check if the gamepad has notified the presence of a rumble event - if (out_dev->logic->gamepad.rumble_events_count != rumble_events_count) { - sem_wait(&out_dev->logic->rumble.sem_empty); - - // translate the rumble to evdev - out_dev->logic->rumble.value = out_dev->logic->gamepad.motors_intensity[0]; - sem_post(&out_dev->logic->rumble.sem_full); - } - - pthread_mutex_unlock(&out_dev->logic->gamepad_mutex); - - const uint32_t flags = out_dev->crtl_flags; - if (flags & OUTPUT_DEV_CTRL_FLAG_EXIT) { - out_dev->crtl_flags &= ~OUTPUT_DEV_CTRL_FLAG_EXIT; + if (logic_termination_requested(out_dev->logic)) { break; } - */ } + pthread_join(rumble_thread, NULL); + return NULL; } diff --git a/output_dev.h b/output_dev.h index 249fb9c..4cf536e 100644 --- a/output_dev.h +++ b/output_dev.h @@ -37,9 +37,6 @@ #define PHYS_STR "00:11:22:33:44:55" -#define OUTPUT_DEV_CTRL_FLAG_EXIT 0x00000001U -#define OUTPUT_DEV_CTRL_FLAG_DATA 0x00000002U - #define ACCEL_RANGE 512 #define GYRO_RANGE 2000 // max range is +/- 35 radian/s @@ -59,8 +56,6 @@ typedef struct output_dev { int imu_fd; int mouse_fd; - volatile uint32_t crtl_flags; - logic_t *logic; } output_dev_t;