diff --git a/CMakeLists.txt b/CMakeLists.txt index b27cb58..ef8738b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,9 @@ 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 virt_mouse_kbd.c virt_evdev.c devices_status.c) +add_executable(${EXECUTABLE_NAME} dev_iio.c dev_in.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) +target_link_libraries(${EXECUTABLE_NAME} PRIVATE Threads::Threads -levdev -ludev -lconfig -lm) set_target_properties(${EXECUTABLE_NAME} PROPERTIES LINKER_LANGUAGE C) diff --git a/Makefile b/Makefile index 82df7f0..a552b1b 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 virt_mouse_kbd.o virt_evdev.o devices_status.o +OBJECTS=main.o dev_in.o dev_out.o dev_iio.o dev_evdev.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/dev_evdev.c b/dev_evdev.c new file mode 100644 index 0000000..b708291 --- /dev/null +++ b/dev_evdev.c @@ -0,0 +1,130 @@ +#include "dev_evdev.h" +#include + +static const char *input_path = "/dev/input/"; + +static pthread_mutex_t input_acquire_mutex = PTHREAD_MUTEX_INITIALIZER; + +static int open_fds[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +static bool ev_matches(struct libevdev *dev, const uinput_filters_t* const filters) { + + const char* name = libevdev_get_name(dev); + if ((name != NULL) && (strcmp(name, filters->name) != 0)) { + return false; + } + + // TODO: if more filters are implemented write them here + + return true; +} + +void dev_evdev_close(struct libevdev* out_evdev) { + if (out_evdev == NULL) { + return; + } + + const int input_acquire_lock_result = pthread_mutex_lock(&input_acquire_mutex); + if (input_acquire_lock_result != 0) { + fprintf(stderr, "Cannot lock input mutex: %d -- this will probably have no consequences\n", input_acquire_lock_result); + return; + } + + int fd = libevdev_get_fd(out_evdev); + for (int i = 0; i < sizeof(open_fds) / sizeof(int); ++i) { + open_fds[i] = (open_fds[i] == fd) ? -1 : open_fds[i]; + } + + // free the memory + libevdev_free(out_evdev); + + pthread_mutex_unlock(&input_acquire_mutex); +} + +int dev_evdev_open( + const uinput_filters_t *const in_filters, + struct libevdev* out_evdev +) { + int res = -ENOENT; + + int open_sysfs_idx = -1; + + res = pthread_mutex_lock(&input_acquire_mutex); + if (res != 0) { + fprintf(stderr, "Cannot lock input mutex: %d\n", res); + goto dev_evdev_open_err; + } + + char path[512] = "\0"; + + DIR *d; + struct dirent *dir; + d = opendir(input_path); + if (d) { + while ((dir = readdir(d)) != NULL) { + if (dir->d_name[0] == '.') { + continue; + } else if (dir->d_name[0] == 'b') { // by-id + continue; + } else if (dir->d_name[0] == 'j') { // js-0 + continue; + } + + sprintf(path, "%s%s", input_path, dir->d_name); + + // try to open the device, if it cannot be opened to go the next + int fd = open(path, O_RDWR); + if (fd < 0) { + //fprintf(stderr, "Cannot open %s, device skipped.\n", sysfs_entry); + continue; + } + + // check if that has been already opened + // open_sysfs + int skip = 0; + for (int o = 0; o < (sizeof(open_fds) / sizeof(int)); ++o) { + if ((open_fds[o] != -1) && (open_fds[o] == fd)) { + close(fd); + skip = 1; + break; + } else if (open_fds[o] == -1) { + open_sysfs_idx = o; + } + } + + if ((skip) || (open_sysfs_idx == -1)) { + continue; + } else { + open_fds[open_sysfs_idx] = fd; + } + + if (libevdev_new_from_fd(fd, &out_evdev) != 0) { + //fprintf(stderr, "Cannot initialize libevdev from this device (%s): skipping.\n", sysfs_entry); + close(fd); + continue; + } + + // try to open the device + if (!ev_matches(out_evdev, in_filters)) { + libevdev_free(out_evdev); + close(fd); + continue; + } + + // the device has been found + break; + } + closedir(d); + } + + pthread_mutex_unlock(&input_acquire_mutex); + +dev_evdev_open_err: + return res; +} diff --git a/dev_evdev.h b/dev_evdev.h new file mode 100644 index 0000000..628c1e2 --- /dev/null +++ b/dev_evdev.h @@ -0,0 +1,10 @@ +#pragma once + +#include "input_dev.h" + +int dev_evdev_open( + const uinput_filters_t *const in_filters, + struct libevdev* out_evdev +); + +void dev_evdev_close(struct libevdev* out_evdev); diff --git a/dev_iio.c b/dev_iio.c index 5ca96c0..e14c19b 100644 --- a/dev_iio.c +++ b/dev_iio.c @@ -498,7 +498,7 @@ static void multiplyMatrixVector(const double matrix[3][3], const double vector[ result[2] = matrix[0][2] * vector[0] + matrix[1][2] * vector[1] + matrix[2][2] * vector[2]; } -int dev_iio_read_imu(const dev_iio_t *const iio, imu_message_t *const out) { +int dev_iio_read_imu(const dev_iio_t *const iio, imu_in_message_t *const out) { struct timeval read_time; gettimeofday(&read_time, NULL); diff --git a/dev_iio.h b/dev_iio.h index ca263b5..d8023d5 100644 --- a/dev_iio.h +++ b/dev_iio.h @@ -73,5 +73,5 @@ int dev_iio_read( int dev_iio_read_imu( const dev_iio_t *const iio, - imu_message_t *const out + imu_in_message_t *const out ); diff --git a/dev_in.c b/dev_in.c new file mode 100644 index 0000000..79a81e8 --- /dev/null +++ b/dev_in.c @@ -0,0 +1,327 @@ +#include "dev_in.h" +#include "message.h" +#include "dev_evdev.h" +#include + +typedef enum dev_in_type { + DEV_IN_TYPE_NONE, + DEV_IN_TYPE_HIDRAW, + DEV_IN_TYPE_IIO, + DEV_IN_TYPE_EV, +} dev_in_type_t; + +typedef struct dev_in_iio { + + int fd; + +} dev_in_iio_t; + +typedef struct dev_in_ev { + bool ignore_msc_scan; + + bool ignore_timestamp; + + struct libevdev* evdev; + + bool grabbed; + + bool has_syn_report; + + bool has_rumble_support; + + struct ff_effect ff_effect; + +} dev_in_ev_t; + +typedef union dev_in_aggr { + dev_in_ev_t evdev; + dev_in_iio_t iio; +} dev_in_aggr_t; + +typedef struct dev_in { + + dev_in_type_t type; + + dev_in_aggr_t dev; + +} dev_in_t; + +static int fill_message_from_iio(dev_in_iio_t *const in_evdev, in_message_t *const out_msg) { + return -EINVAL; +} + +static int fill_message_from_evdev(dev_in_ev_t *const in_evdev, in_message_t *const out_msg) { + int res = 0; + + struct input_event read_ev; + + // reset the events count + out_msg->data.event.ev_count = 0; + + // if the device does not support syn reports read just one event and return + if (!in_evdev->has_syn_report) { + res = libevdev_next_event(in_evdev->evdev, LIBEVDEV_READ_FLAG_NORMAL, &read_ev); + if (res < 0) { + goto fill_message_from_evdev_err; + } + + // just copy the input event + out_msg->data.event.ev[out_msg->data.event.ev_count++] = read_ev; + + goto fill_message_from_evdev_err_completed; + } + + // the device does support syn reports so read every event until one is found + while (out_msg->data.event.ev_count < MAX_EVDEV_EVENTS_IN_MESSAGE) { + res = libevdev_next_event(in_evdev->evdev, LIBEVDEV_READ_FLAG_BLOCKING, &read_ev); + if (res < 0) { + goto fill_message_from_evdev_err; + } + + // if the message is a syn_report exit the while as there are no more events + if ((read_ev.type == EV_SYN) && (read_ev.code == SYN_REPORT)) { + break; + } else if ((in_evdev->ignore_msc_scan) && (read_ev.type == EV_MSC) && (read_ev.code == MSC_SCAN)) { + continue; + } else if ((in_evdev->ignore_timestamp) && (read_ev.type == EV_MSC) && (read_ev.code == MSC_TIMESTAMP)) { + continue; + } + + // just copy the input event + out_msg->data.event.ev[out_msg->data.event.ev_count++] = read_ev; + } + +fill_message_from_evdev_err: +fill_message_from_evdev_err_completed: + return res; +} + +static int fill_message_from_device(dev_in_t *const in_dev, in_message_t *const out_msg) { + if (in_dev->type == DEV_IN_TYPE_EV) { + return fill_message_from_evdev(&in_dev->dev.evdev, out_msg); + } else if (in_dev->type == DEV_IN_TYPE_EV) { + return fill_message_from_iio(&in_dev->dev.iio, out_msg); + } + + fprintf(stderr, "Unable to recognise device type\n"); + return -EINVAL; +} + +int open_device( + const uinput_filters_t *const in_filters, + dev_in_ev_t *const out_dev +) { + int res = dev_evdev_open(in_filters, out_dev->evdev); + if (res != 0) { + fprintf(stderr, "Unable to open the specified device: %d\n", res); + goto open_device_err; + } + + out_dev->has_rumble_support = libevdev_has_event_type(out_dev->evdev, EV_FF) && libevdev_has_event_code(out_dev->evdev, EV_FF, FF_RUMBLE); + + const int grab_res = libevdev_grab(out_dev->evdev, LIBEVDEV_GRAB); + out_dev->grabbed = grab_res == 0; + if (!out_dev->grabbed) { + fprintf(stderr, "Unable to grab the device (%s): %d.\n", libevdev_get_name(out_dev->evdev), grab_res); + } + + if (out_dev->has_rumble_support) { + printf("Opened device\n name: %s\n rumble: %s\n", + libevdev_get_name(out_dev->evdev), + libevdev_has_event_code(out_dev->evdev, EV_FF, FF_RUMBLE) ? "true" : "false" + ); + + // prepare the rumble effect + out_dev->ff_effect.type = FF_RUMBLE; + out_dev->ff_effect.id = -1; + out_dev->ff_effect.replay.delay = 0; + out_dev->ff_effect.replay.length = 5000; + out_dev->ff_effect.u.rumble.strong_magnitude = 0x0000; + out_dev->ff_effect.u.rumble.weak_magnitude = 0x0000; + + const struct input_event gain = { + .type = EV_FF, + .code = FF_GAIN, + .value = 0xFFFF, // TODO: give the user the ability to change this + }; + + const int gain_set_res = write(libevdev_get_fd(out_dev->evdev), (const void*)&gain, sizeof(gain)); + if (gain_set_res != sizeof(gain)) { + fprintf(stderr, "Unable to adjust gain for force-feedback: %d\n", gain_set_res); + } else { + printf("Gain for force-feedback set to %u for device %s\n", gain.value, libevdev_get_name(out_dev->evdev)); + } + + } else { + printf("Opened device\n name: %s\n rumble: no force-feedback\n", + libevdev_get_name(out_dev->evdev) + ); + } + +open_device_err: + return res; +} + +static void handle_rumble_device(dev_in_ev_t *const in_dev, const out_message_rumble_t *const in_rumble_msg) { + if (!in_dev->has_rumble_support) { + return; + } + + int fd = libevdev_get_fd(in_dev->evdev); + + // here stop the previous rumble + if (in_dev->ff_effect.id != -1) { + struct input_event rumble_stop = { + .type = EV_FF, + .code = in_dev->ff_effect.id, + .value = 0, + }; + + const int rumble_stop_res = write(fd, (const void*) &rumble_stop, sizeof(rumble_stop)); + if (rumble_stop_res != sizeof(rumble_stop)) { + fprintf(stderr, "Unable to stop the previous rumble: %d\n", rumble_stop_res); + } + } + + // load the new effect data + in_dev->ff_effect.u.rumble.strong_magnitude = in_rumble_msg->strong_magnitude; + in_dev->ff_effect.u.rumble.weak_magnitude = in_rumble_msg->weak_magnitude; + + // upload the new effect to the device + const int effect_upload_res = ioctl(fd, EVIOCSFF, &in_dev->ff_effect); + if (effect_upload_res == 0) { + const struct input_event rumble_play = { + .type = EV_FF, + .code = in_dev->ff_effect.id, + .value = 1, + }; + + const int effect_start_res = write(fd, (const void*)&rumble_play, sizeof(rumble_play)); + if (effect_start_res == sizeof(rumble_play)) { +#if defined(INCLUDE_INPUT_DEBUG) + printf("Rumble effect play requested to driver\n"); +#endif + } else { + fprintf(stderr, "Unable to write input event starting the rumble: %d\n", effect_start_res); + } + } else { + fprintf(stderr, "Unable to update force-feedback effect: %d\n", effect_upload_res); + + in_dev->ff_effect.id = -1; + } +} + +static void handle_rumble(dev_in_t *const in_devs, size_t in_devs_count, const out_message_rumble_t *const in_rumble_msg) { + for (size_t i = 0; i < in_devs_count; ++i) { + if (in_devs[i].type == DEV_IN_TYPE_EV) { + handle_rumble_device(&in_devs[i].dev.evdev, in_rumble_msg); + } + } +} + +void* dev_in_thread_func(void *ptr) { + dev_in_data_t *const devs = (dev_in_data_t*)ptr; + + struct timeval timeout = { + .tv_sec = (__time_t)devs->timeout_ms / (__time_t)1000, + .tv_usec = ((__suseconds_t)devs->timeout_ms % (__suseconds_t)1000) * (__suseconds_t)1000000, + }; + + fd_set read_fds; + + dev_in_t* const devices = malloc(sizeof(dev_in_t) * devs->input_dev_cnt); + if (devices == NULL) { + fprintf(stderr, "Unable to allocate memory to hold devices -- aborting input thread\n"); + return NULL; + } + + // flag every device as disconnected + for (size_t i = 0; i < devs->input_dev_cnt; ++i) { + devices[i].type = DEV_IN_TYPE_NONE; + } + + in_message_t current_message; + + for (;;) { + FD_ZERO(&read_fds); + FD_SET(devs->in_message_pipe_fd, &read_fds); + for (size_t i = 0; i < devs->input_dev_cnt; ++i) { + int fd = -1; + if (devices[i].type == DEV_IN_TYPE_EV) { + fd = libevdev_get_fd(devices[i].dev.evdev.evdev); + } else if (devices[i].type == DEV_IN_TYPE_IIO) { + + } else if (devices[i].type == DEV_IN_TYPE_NONE) { + fprintf(stderr, "Device %zu not found -- Attempt reconnection", i); + + if (devs->input_dev_decl[i].dev_type == input_dev_type_uinput) { + const int open_res = open_device(&devs->input_dev_decl[i].filters.ev, &devices[i].dev.evdev); + if (open_res == 0) { + devices[i].type = DEV_IN_TYPE_EV; + fd = libevdev_get_fd(devices[i].dev.evdev.evdev); + } + } + + } + + FD_SET(fd, &read_fds); + } + + int ready_fds = select(FD_SETSIZE, &read_fds, NULL, NULL, &timeout); + + if (ready_fds == -1) { + const int err = errno; + fprintf(stderr, "Error reading devices: %d\n", err); + continue; + } else if (ready_fds == 0) { + // Timeout... simply retry + continue; + } + + // check for messages incoming like set leds or activate rumble + if (FD_ISSET(devs->out_message_pipe_fd, &read_fds)) { + out_message_t out_msg; + const ssize_t out_message_pipe_read_res = read(devs->out_message_pipe_fd, (void*)&out_msg, sizeof(out_message_t)); + if (out_message_pipe_read_res == sizeof(out_message_t)) { + if (out_msg.type == OUT_MSG_TYPE_RUMBLE) { + handle_rumble(devices, devs->input_dev_cnt, &out_msg.data.rumble); + } else if (out_msg.type == OUT_MSG_TYPE_LEDS) { + // TODO: handle LEDs + } + } else { + fprintf(stderr, "Unable to read message: %zd\n", out_message_pipe_read_res); + } + } + + // the following is only executed when there is actual data in at least one of the fd + for (size_t i = 0; i < devs->input_dev_cnt; ++i) { + int fd = -1; + if (devices[i].type == DEV_IN_TYPE_EV) { + fd = libevdev_get_fd(devices[i].dev.evdev.evdev); + } else if (devices[i].type == DEV_IN_TYPE_IIO) { + + } + + if (!FD_ISSET(fd, &read_fds)) { + continue; + } + + const int fill_msg_res = fill_message_from_device(&devices[i], ¤t_message); + if (!fill_msg_res) { + fprintf(stderr, "Error reading from selected input device (%zu): %d\n", i, fill_msg_res); + continue; + } + + const ssize_t in_message_pipe_write_res = write(devs->in_message_pipe_fd, (void*)¤t_message, sizeof(in_message_t)); + if (in_message_pipe_write_res != sizeof(in_message_t)) { + fprintf(stderr, "Unable to write data to the in_message pipe: %zu\n", in_message_pipe_write_res); + continue; + } + } + } + + // TODO: free every fd + free(devices); + + return NULL; +} \ No newline at end of file diff --git a/dev_in.h b/dev_in.h new file mode 100644 index 0000000..6172b8f --- /dev/null +++ b/dev_in.h @@ -0,0 +1,26 @@ +#pragma once + +#include "message.h" +#include "input_dev.h" + +typedef struct dev_in_data { + size_t max_messages_in_flight; + + // the timeout (in missileconds) + uint64_t timeout_ms; + + // declarations of devices to monitor + input_dev_t *input_dev_decl; + + // number of devices to monitor + size_t input_dev_cnt; + + // this pipe is reserved for reporting in_message_t + int in_message_pipe_fd; + + // this messages is reserved for receiving out_message_t + int out_message_pipe_fd; + +} dev_in_data_t; + +void *dev_in_thread_func(void *ptr); diff --git a/dev_out.c b/dev_out.c new file mode 100644 index 0000000..dff978a --- /dev/null +++ b/dev_out.c @@ -0,0 +1,23 @@ +#include "dev_out.h" + +void *dev_out_thread_func(void *ptr) { + dev_out_data_t *const dev_out = (dev_out_data_t*)ptr; + + dev_out_gamepad_device_t current_gamepad = dev_out->gamepad; + + int current_gamepad_fd = -1; + int current_keyboard_fd = -1; + int current_mouse_fd = -1; + + for (;;) { + + + if (current_gamepad == GAMEPAD_DUALSENSE) { + + } else if (current_gamepad == GAMEPAD_DUALSHOCK) { + + } + } + + return NULL; +} diff --git a/dev_out.h b/dev_out.h new file mode 100644 index 0000000..1db237e --- /dev/null +++ b/dev_out.h @@ -0,0 +1,26 @@ +#pragma once + +#include "message.h" +#include "devices_status.h" + +typedef enum dev_out_gamepad_device { + GAMEPAD_DUALSENSE, + GAMEPAD_DUALSHOCK, + GAMEPAD_XBOX, +} dev_out_gamepad_device_t; + +typedef struct dev_out_data { + + // this pipe is reserved for reporting in_message_t + int in_message_pipe_fd; + + // this messages is reserved for receiving out_message_t + int out_message_pipe_fd; + + dev_out_gamepad_device_t gamepad; + + devices_status_t dev_stats; + +} dev_out_data_t; + +void *dev_out_thread_func(void *ptr); diff --git a/devices_status.c b/devices_status.c index 297d021..429737d 100644 --- a/devices_status.c +++ b/devices_status.c @@ -100,4 +100,4 @@ void gamepad_status_qam_quirk(gamepad_status_t *const gamepad_stats) { gettimeofday(&press_time, NULL); } } -} \ No newline at end of file +} diff --git a/devices_status.h b/devices_status.h index da7559d..eca4bd7 100644 --- a/devices_status.h +++ b/devices_status.h @@ -80,4 +80,4 @@ void gamepad_status_init(gamepad_status_t *const stats); void devices_status_init(devices_status_t *const stats); -void gamepad_status_qam_quirk(gamepad_status_t *const gamepad_stats); \ No newline at end of file +void gamepad_status_qam_quirk(gamepad_status_t *const gamepad_stats); diff --git a/imu_message.h b/imu_message.h index 6fa45ad..be70ccc 100644 --- a/imu_message.h +++ b/imu_message.h @@ -26,4 +26,4 @@ typedef struct imu_message { uint32_t flags; -} imu_message_t; +} imu_in_message_t; diff --git a/input_dev.c b/input_dev.c deleted file mode 100644 index 2ac65bb..0000000 --- a/input_dev.c +++ /dev/null @@ -1,688 +0,0 @@ -#include "input_dev.h" -#include "logic.h" -#include "message.h" -#include "queue.h" -#include "dev_iio.h" -#include "platform.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char *input_path = "/dev/input/"; -static const char *iio_path = "/sys/bus/iio/devices/"; - -uint32_t input_filter_imu_identity(struct input_event* events, size_t* size, uint32_t* count, uint32_t* flags) { -/* - int32_t gyro_x = 0, gyro_y = 0, gyro_z = 0, accel_x = 0, accel_y = 0, accel_z = 0; - if (gyroscope_mouse_translation > 0) { - for (uint32_t i = 0; i < *count; ++i) { - if (events[i].type != EV_ABS) { - continue; - } - - if (events[i].code == ABS_X) { - accel_x = events[i].value; - } else if (events[i].code == ABS_Y) { - accel_y = events[i].value; - } else if (events[i].code == ABS_Z) { - accel_z = events[i].value; - } else if (events[i].code == ABS_RX) { - gyro_x = events[i].value; - } else if (events[i].code == ABS_RY) { - gyro_y = events[i].value; - } else if (events[i].code == ABS_RZ) { - gyro_z = events[i].value; - } - } - - uint32_t w = 0; - - if (gyro_x != 0) { - events[w].type = EV_REL; - events[w].code = REL_Y; - events[w].value = (float)gyro_x * -1.0; - ++w; - } - - if (gyro_y != 0) { - events[w].type = EV_REL; - events[w].code = REL_X; - events[w].value = (float)gyro_y * +1.0; - ++w; - } - - *count = w; - - flags |= EV_MESSAGE_FLAGS_PRESERVE_TIME | INPUT_FILTER_FLAGS_MOUSE; - } -*/ - return INPUT_FILTER_FLAGS_DO_NOT_EMIT; -} - -uint32_t input_filter_identity(struct input_event* events, size_t* size, uint32_t* count, uint32_t* flags) { - return INPUT_FILTER_FLAGS_NONE; -} - -uint32_t input_filter_asus_kb(struct input_event* events, size_t* size, uint32_t* count, uint32_t* flags) { - - - return INPUT_FILTER_FLAGS_NONE; -} - -static struct libevdev* ev_matches(const char* sysfs_entry, const uinput_filters_t* const filters) { - struct libevdev *dev = NULL; - - int fd = open(sysfs_entry, O_RDWR); - if (fd < 0) { - //fprintf(stderr, "Cannot open %s, device skipped.\n", sysfs_entry); - return NULL; - } - - if (libevdev_new_from_fd(fd, &dev) != 0) { - //fprintf(stderr, "Cannot initialize libevdev from this device (%s): skipping.\n", sysfs_entry); - close(fd); - return NULL; - } - - const char* name = libevdev_get_name(dev); - if ((name != NULL) && (strcmp(name, filters->name) != 0)) { - //fprintf(stderr, "The device name (%s) for device %s does not matches the expected one %s.\n", name, sysfs_entry, filters->name); - libevdev_free(dev); - close(fd); - return NULL; - } - - const int grab_res = libevdev_grab(dev, LIBEVDEV_GRAB); - if (grab_res != 0) { - fprintf(stderr, "Unable to grab the device (%s): %d.\n", sysfs_entry, grab_res); - //libevdev_free(dev); - //close(fd); - return dev; - } - - return dev; -} - -static dev_iio_t* iio_matches(const char* sysfs_entry, const iio_filters_t* const filters) { - dev_iio_t *const dev_iio = dev_iio_create(sysfs_entry); - if (dev_iio == NULL) { - fprintf(stderr, "Could not create iio device.\n"); - return NULL; - } - - const char* const iio_name = dev_iio_get_name(dev_iio); - if (abs(strcmp(iio_name, filters->name)) != 0) { - fprintf(stderr, "Error: iio device name does not match, expected %s got %s.\n", filters->name, iio_name); - dev_iio_destroy(dev_iio); - return NULL; - } - - return dev_iio; -} - -static pthread_mutex_t input_acquire_mutex = PTHREAD_MUTEX_INITIALIZER; - -static char* open_sysfs[] = { - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -}; - -#define MAX_MESSAGES_IN_FLIGHT 32 -#define DEFAULT_EVENTS_IN_REPORT 8 - -#define INPUT_CTX_FLAGS_READ_TERMINATED 0x00000001U - -struct input_ctx { - struct libevdev* dev; - dev_iio_t *iio_dev; - queue_t* queue; - queue_t* rumble_queue; - controller_settings_t* settings; - uint32_t flags; - message_t messages[MAX_MESSAGES_IN_FLIGHT]; - ev_input_filter_t input_filter_fn; -}; - -static void* iio_read_thread_func(void* ptr) { - struct input_ctx* ctx = (struct input_ctx*)ptr; - - message_t* msg = NULL; - - int rc = -1; - - do { - if (msg == NULL) { - for (int h = 0; h < MAX_MESSAGES_IN_FLIGHT; ++h) { - if ((ctx->messages[h].flags & MESSAGE_FLAGS_HANDLE_DONE) != 0) { - msg = &ctx->messages[h]; - //TODO: msg->ev_count = 0; - break; - } - } - } - - if (msg == NULL) { - fprintf(stderr, "iio: Events are stalled.\n"); - continue; - } - - rc = dev_iio_read_imu(ctx->iio_dev, &msg->data.imu); - if (rc == 0) { - // OK: good read. go on.... - } else if (rc == -ENOMEM) { - fprintf(stderr, "Error: out-of-memory will skip the current frame.\n"); - continue; - } else { - fprintf(stderr, "Error: reading %s: %d\n", dev_iio_get_name(ctx->iio_dev), rc); - break; - } - - // clear out flags - msg->flags = 0x00000000U; - - if (queue_push(ctx->queue, (void*)msg) != 0) { - fprintf(stderr, "Error pushing iio event.\n"); - - // flag the memory to be safe to reuse - msg->flags |= MESSAGE_FLAGS_HANDLE_DONE; - } - - // TODO: configure equal as sampling rate - usleep(625); - - // either way.... fill a new buffer on the next cycle - msg = NULL; - } while (rc == 1 || rc == 0 || rc == -EAGAIN); - - return NULL; -} - -static void* input_read_thread_func(void* ptr) { - struct input_ctx* ctx = (struct input_ctx*)ptr; - struct libevdev* dev = ctx->dev; - - int has_syn = libevdev_has_event_type(ctx->dev, EV_SYN); - - int rc = 1; - - message_t* msg = NULL; - - do { - if (msg == NULL) { - for (int h = 0; h < MAX_MESSAGES_IN_FLIGHT; ++h) { - if ((ctx->messages[h].flags & MESSAGE_FLAGS_HANDLE_DONE) != 0) { - msg = &ctx->messages[h]; - msg->data.event.ev_count = 0; - break; - } - } - } - - if (msg == NULL) { - fprintf(stderr, "udev: Events are stalled.\n"); - continue; - } - - struct input_event read_ev; - rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_BLOCKING, &read_ev); - if (rc == 0) { - const int is_syn = (read_ev.type == EV_SYN) && (read_ev.code == SYN_REPORT); - - if (read_ev.type == EV_MSC) { - if (read_ev.code == MSC_SCAN) { -#if defined(IGNORE_INPUT_SCAN) - continue; -#endif // IGNORE_INPUT_SCAN - } else if (read_ev.code == MSC_TIMESTAMP) { - // the output device will handle that - //printf("MSC_TIMESTAMP found. Ignoring...\n"); - } - } - - if ((!has_syn) || ((has_syn) && (!is_syn))) { -#if defined(INCLUDE_INPUT_DEBUG) - printf( - "Input: %s %s %d\n", - libevdev_event_type_get_name(read_ev.type), - libevdev_event_code_get_name(read_ev.type, read_ev.code), - read_ev.value - ); -#endif - - if ((msg->data.event.ev_count+1) == msg->data.event.ev_size) { - printf("maximum number of events reached, buffer enlarged.\n"); - - const size_t new_size = msg->data.event.ev_size * 2; - struct input_event* new_buf = malloc(sizeof(struct input_event) * new_size); - if (new_buf != NULL) { - void* old_buf = (void*)msg->data.event.ev; - - // copy events already in the buffer - memcpy((void*)new_buf, (const void*)old_buf, sizeof(struct input_event) * msg->data.event.ev_size); - - // copy the new event - memcpy((void*)(&new_buf[msg->data.event.ev_count]), (const void*)&read_ev, sizeof(struct input_event)); - ++msg->data.event.ev_count; - - msg->data.event.ev = new_buf; - msg->data.event.ev_size = new_size; - - free(old_buf); - } else { - fprintf(stderr, "Unable to allocate data for incoming events."); - } - } else { - // just copy the input event - msg->data.event.ev[msg->data.event.ev_count] = read_ev; - ++msg->data.event.ev_count; - } - } - - if ((!has_syn) || ((has_syn) && (is_syn))) { -#if defined(INCLUDE_INPUT_DEBUG) - printf("Sync ---------------------------------------\n"); -#endif - - // clear out flags - msg->flags = 0x00000000U; - msg->data.event.ev_flags = 0x00000000U; - - const uint32_t input_filter_res = ctx->input_filter_fn(msg->data.event.ev, &msg->data.event.ev_size, &msg->data.event.ev_count, &msg->data.event.ev_flags); - - if (((input_filter_res & INPUT_FILTER_FLAGS_DO_NOT_EMIT) == 0) && (msg->data.event.ev_count > 0)) { - if (queue_push(ctx->queue, (void*)msg) != 0) { - fprintf(stderr, "Error pushing event.\n"); - - // flag the memory to be safe to reuse - msg->flags |= MESSAGE_FLAGS_HANDLE_DONE; - } - } else { - // flag the memory to be safe to reuse - msg->flags |= MESSAGE_FLAGS_HANDLE_DONE; - } - - // either way.... fill a new buffer on the next cycle - msg = NULL; - } - } - } while (rc == 1 || rc == 0 || rc == -EAGAIN); - - ctx->flags |= INPUT_CTX_FLAGS_READ_TERMINATED; - - return NULL; -} - -static void input_iio( - input_dev_t *const in_dev, - struct input_ctx *const ctx -) { - int open_sysfs_idx = -1; - - for (;;) { - if (logic_termination_requested(in_dev->logic)) { - break; - } - - // clean up from previous iteration - if (ctx->iio_dev != NULL) { - dev_iio_destroy(ctx->iio_dev); - ctx->dev = NULL; - } - - const int input_acquire_lock_result = pthread_mutex_lock(&input_acquire_mutex); - if (input_acquire_lock_result != 0) { - fprintf(stderr, "Cannot lock input mutex: %d, will retry later...\n", input_acquire_lock_result); - usleep(150000); - continue; - } - - // clean up leftover from previous opening - if (open_sysfs_idx >= 0) { - free(open_sysfs[open_sysfs_idx]); - open_sysfs[open_sysfs_idx] = NULL; - } - - char path[512] = "\0"; - - DIR *d; - struct dirent *dir; - d = opendir(iio_path); - if (d) { - while ((dir = readdir(d)) != NULL) { - if (dir->d_name[0] == '.') { - continue; - } else if (dir->d_name[0] == 'b') { // by-id - continue; - } else if (dir->d_name[0] == 'j') { // js-0 - continue; - } - - sprintf(path, "%s%s", iio_path, dir->d_name); - - // check if that has been already opened - // open_sysfs - int skip = 0; - for (int o = 0; o < (sizeof(open_sysfs) / sizeof(const char*)); ++o) { - if ((open_sysfs[o] != NULL) && (strcmp(open_sysfs[o], path) == 0)) { - fprintf(stderr, "already opened iio device %s: skip.\n", path); - skip = 1; - break; - } - } - - if (skip) { - continue; - } - - // try to open the device - ctx->iio_dev = iio_matches(path, in_dev->iio_filters); - if (ctx->iio_dev != NULL) { - open_sysfs_idx = 0; - while (open_sysfs[open_sysfs_idx] != NULL) { - ++open_sysfs_idx; - } - open_sysfs[open_sysfs_idx] = malloc(sizeof(path)); - memcpy(open_sysfs[open_sysfs_idx], path, 512); - - printf("Opened iio %s\n name: %s\n", - path, - dev_iio_get_name(ctx->iio_dev) - ); - - break; - } else { - fprintf(stderr, "iio device in %s does NOT matches\n", path); - ctx->iio_dev = NULL; - } - } - closedir(d); - } - - pthread_mutex_unlock(&input_acquire_mutex); - - // if device was not open "continue" - if (ctx->iio_dev == NULL) { - usleep(250000); - continue; - } - - pthread_t incoming_events_thread; - - const int incoming_events_thread_creation = pthread_create(&incoming_events_thread, NULL, iio_read_thread_func, (void*)ctx); - if (incoming_events_thread_creation != 0) { - fprintf(stderr, "Error creating the input thread for device %s: %d\n", dev_iio_get_name(ctx->iio_dev), incoming_events_thread_creation); - } - - if (incoming_events_thread_creation == 0) { - pthread_join(incoming_events_thread, NULL); - } - } -} - -static void input_udev( - input_dev_t *const in_dev, - struct input_ctx *const ctx -) { - int open_sysfs_idx = -1; - - for (;;) { - if (logic_termination_requested(in_dev->logic)) { - break; - } - - // clean up from previous iteration - if (ctx->dev != NULL) { - libevdev_free(ctx->dev); - ctx->dev = NULL; - } - - const int input_acquire_lock_result = pthread_mutex_lock(&input_acquire_mutex); - if (input_acquire_lock_result != 0) { - fprintf(stderr, "Cannot lock input mutex: %d, will retry later...\n", input_acquire_lock_result); - usleep(250000); - continue; - } - - // clean up leftover from previous opening - if (open_sysfs_idx >= 0) { - free(open_sysfs[open_sysfs_idx]); - open_sysfs[open_sysfs_idx] = NULL; - } - - char path[512] = "\0"; - - DIR *d; - struct dirent *dir; - d = opendir(input_path); - if (d) { - while ((dir = readdir(d)) != NULL) { - if (dir->d_name[0] == '.') { - continue; - } else if (dir->d_name[0] == 'b') { // by-id - continue; - } else if (dir->d_name[0] == 'j') { // js-0 - continue; - } - - sprintf(path, "%s%s", input_path, dir->d_name); - - // check if that has been already opened - // open_sysfs - int skip = 0; - for (int o = 0; o < (sizeof(open_sysfs) / sizeof(const char*)); ++o) { - if ((open_sysfs[o] != NULL) && (strcmp(open_sysfs[o], path) == 0)) { - skip = 1; - break; - } - } - - if (skip) { - continue; - } - - // try to open the device - ctx->dev = ev_matches(path, in_dev->ev_filters); - if (ctx->dev != NULL) { - open_sysfs_idx = 0; - while (open_sysfs[open_sysfs_idx] != NULL) { - ++open_sysfs_idx; - } - open_sysfs[open_sysfs_idx] = malloc(sizeof(path)); - memcpy(open_sysfs[open_sysfs_idx], path, 512); - - if (libevdev_has_event_type(ctx->dev, EV_FF)) { - printf("Opened device %s\n name: %s\n rumble: %s\n", - path, - libevdev_get_name(ctx->dev), - libevdev_has_event_code(ctx->dev, EV_FF, FF_RUMBLE) ? "true" : "false" - ); - } else { - printf("Opened device %s\n name: %s\n rumble: no EV_FF\n", - path, - libevdev_get_name(ctx->dev) - ); - } - - break; - } - } - closedir(d); - } - - pthread_mutex_unlock(&input_acquire_mutex); - - if (ctx->dev == NULL) { - usleep(250000); - continue; - } - - const int fd = libevdev_get_fd(ctx->dev); - - struct ff_effect current_effect = { - .type = FF_RUMBLE, - .id = -1, - .replay = { - .delay = 0, - .length = 5000, - }, - .u = { - .rumble = { - .strong_magnitude = 0x0000, - .weak_magnitude = 0x0000, - } - } - }; - - // start the incoming events read thread - pthread_t incoming_events_thread; - const int incoming_events_thread_creation = pthread_create(&incoming_events_thread, NULL, input_read_thread_func, (void*)ctx); - if (incoming_events_thread_creation != 0) { - fprintf(stderr, "Error creating the input thread for device %s: %d\n", libevdev_get_name(ctx->dev), incoming_events_thread_creation); - continue; - } - - const int has_ff = libevdev_has_event_type(ctx->dev, EV_FF); - - if (has_ff) { - const struct input_event gain = { - .type = EV_FF, - .code = FF_GAIN, - .value = ctx->settings->ff_gain, - }; - - const int gain_set_res = write(fd, (const void*)&gain, sizeof(gain)); - if (gain_set_res != sizeof(gain)) { - fprintf(stderr, "Unable to adjust gain for force-feedback: %d\n", gain_set_res); - } else { - printf("Gain for force-feedback set to %u\n", gain.value); - } - } - - // const int timeout_ms = 1200; 40% usage - const int timeout_ms = 5000; //Reduced from 1200 same functionality but cpu usage reduced by ~20%, any higher yield no sig results - - // while the incoming events thread run... - while ((ctx->flags & INPUT_CTX_FLAGS_READ_TERMINATED) == 0) { - if (has_ff) { - void* rmsg = NULL; - - const int rumble_msg_recv_res = queue_pop_timeout(ctx->rumble_queue, &rmsg, timeout_ms); - if (rumble_msg_recv_res == 0) { - rumble_message_t *const rumble_msg = (rumble_message_t*)rmsg; - - // here stop the previous rumble - if (current_effect.id != -1) { - struct input_event rumble_stop = { - .type = EV_FF, - .code = current_effect.id, - .value = 0, - }; - - const int rumble_stop_res = write(fd, (const void*) &rumble_stop, sizeof(rumble_stop)); - if (rumble_stop_res != sizeof(rumble_stop)) { - fprintf(stderr, "Unable to stop the previous rumble: %d\n", rumble_stop_res); - } - } - - current_effect.u.rumble.strong_magnitude = rumble_msg->strong_magnitude; - current_effect.u.rumble.weak_magnitude = rumble_msg->weak_magnitude; - -#if defined(INCLUDE_INPUT_DEBUG) - printf("Rumble event received -- strong_magnitude: %u, weak_magnitude: %u\n", (unsigned)current_effect.u.rumble.strong_magnitude, (unsigned)current_effect.u.rumble.weak_magnitude); -#endif - - const int effect_upload_res = ioctl(fd, EVIOCSFF, ¤t_effect); - if (effect_upload_res == 0) { - const struct input_event rumble_play = { - .type = EV_FF, - .code = current_effect.id, - .value = 1, - }; - - const int effect_start_res = write(fd, (const void*)&rumble_play, sizeof(rumble_play)); - if (effect_start_res == sizeof(rumble_play)) { -#if defined(INCLUDE_INPUT_DEBUG) - printf("Rumble effect play requested to driver\n"); -#endif - } else { - fprintf(stderr, "Unable to write input event starting the rumble: %d\n", effect_start_res); - } - } else { - fprintf(stderr, "Unable to update force-feedback effect: %d\n", effect_upload_res); - - current_effect.id = -1; - } - - // this message was allocated by output_dev so I have to free it - free(rumble_msg); - } - } else { - usleep(timeout_ms * 1000); - } - } - - // stop any effect - if ((has_ff) && (current_effect.id != -1)) { - const int effect_removal_res = ioctl(fd, EVIOCRMFF, current_effect.id); - if (effect_removal_res == 0) { - printf("\n"); - } else { - fprintf(stderr, "Error removing rumble effect: %d\n", effect_removal_res); - } - } - - // wait for incoming events thread to totally stop - pthread_join(incoming_events_thread, NULL); - } -} - -void *input_dev_thread_func(void *ptr) { - input_dev_t *in_dev = (input_dev_t*)ptr; - - struct input_ctx ctx = { - .dev = NULL, - .queue = &in_dev->logic->input_queue, - .rumble_queue = &in_dev->logic->rumble_events_queue, - .settings = &in_dev->logic->controller_settings, - .input_filter_fn = in_dev->ev_input_filter_fn, - .flags = 0x00000000U - }; - - if (in_dev->dev_type == input_dev_type_uinput) { - // prepare space and empty messages - for (int h = 0; h < MAX_MESSAGES_IN_FLIGHT; ++h) { - ctx.messages[h].flags = MESSAGE_FLAGS_HANDLE_DONE; - ctx.messages[h].type = MSG_TYPE_EV; - ctx.messages[h].data.event.ev_size = DEFAULT_EVENTS_IN_REPORT; - ctx.messages[h].data.event.ev = malloc(sizeof(struct input_event) * ctx.messages[h].data.event.ev_size); - } - - input_udev(in_dev, &ctx); - - // free memory - for (int h = 0; h < MAX_MESSAGES_IN_FLIGHT; ++h) { - free(ctx.messages[h].data.event.ev); - ctx.messages[h].data.event.ev_size = 0; - } - } else if (in_dev->dev_type == input_dev_type_iio) { - // prepare space and empty messages - for (int h = 0; h < MAX_MESSAGES_IN_FLIGHT; ++h) { - ctx.messages[h].flags = MESSAGE_FLAGS_HANDLE_DONE; - ctx.messages[h].type = MSG_TYPE_IMU; - } - - input_iio(in_dev, &ctx); - } - - return NULL; -} diff --git a/input_dev.h b/input_dev.h index e3ef31f..61c227c 100644 --- a/input_dev.h +++ b/input_dev.h @@ -1,6 +1,5 @@ #pragma once -#include "queue.h" #include "message.h" #include "logic.h" @@ -15,29 +14,25 @@ typedef enum input_dev_type { } input_dev_type_t; typedef struct uinput_filters { - const char* name; + const char name[256]; } uinput_filters_t; typedef struct iio_filters { - const char* name; + const char name[256]; } iio_filters_t; typedef struct input_dev { input_dev_type_t dev_type; - const uinput_filters_t* ev_filters; - const iio_filters_t* iio_filters; + union { + uinput_filters_t ev; + iio_filters_t iio; + } filters; ev_input_filter_t ev_input_filter_fn; - logic_t *logic; - } input_dev_t; -void *input_dev_thread_func(void *ptr); - -int open_and_hide_input(void); - uint32_t input_filter_imu_identity(struct input_event* events, size_t* size, uint32_t* count, uint32_t* flags); uint32_t input_filter_identity(struct input_event* events, size_t* size, uint32_t* count, uint32_t* flags); diff --git a/logic.c b/logic.c index 20e891a..23efe53 100644 --- a/logic.c +++ b/logic.c @@ -1,6 +1,5 @@ #include "logic.h" #include "platform.h" -#include "queue.h" #include "virt_ds4.h" #include "virt_ds5.h" #include "virt_evdev.h" @@ -21,25 +20,12 @@ int logic_create(logic_t *const logic) { 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); - - 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) { diff --git a/logic.h b/logic.h index 02b86e6..aa76644 100644 --- a/logic.h +++ b/logic.h @@ -1,7 +1,6 @@ #pragma once #include "platform.h" -#include "queue.h" #include "settings.h" #include "devices_status.h" @@ -11,7 +10,7 @@ typedef struct rumble_message { uint16_t strong_magnitude; uint16_t weak_magnitude; -} rumble_message_t; +} rumble_in_message_t; typedef struct logic { @@ -19,15 +18,11 @@ typedef struct logic { devices_status_t dev_stats; - queue_t input_queue; - pthread_t virt_dev_thread; bool virt_dev_thread_running; volatile uint32_t flags; - queue_t rumble_events_queue; - controller_settings_t controller_settings; } logic_t; diff --git a/main.c b/main.c index 75895f2..9dbec69 100644 --- a/main.c +++ b/main.c @@ -3,6 +3,8 @@ #include "input_dev.h" #include "output_dev.h" +#include "dev_in.h" +#include "dev_out.h" #include "logic.h" logic_t global_logic; @@ -11,48 +13,48 @@ static output_dev_t out_gamepadd_dev = { .logic = &global_logic, }; -static iio_filters_t in_iio_filters = { - .name = "bmi323", -}; - static input_dev_t in_iio_dev = { .dev_type = input_dev_type_iio, - .iio_filters = &in_iio_filters, - .logic = &global_logic, + .filters = { + .iio = { + .name = "bmi323", + } + }, + //.logic = &global_logic, //.input_filter_fn = input_filter_imu_identity, }; -static uinput_filters_t in_asus_kb_1_filters = { - .name = "Asus Keyboard", -}; - static input_dev_t in_asus_kb_1_dev = { .dev_type = input_dev_type_uinput, - .ev_filters = &in_asus_kb_1_filters, - .logic = &global_logic, - .ev_input_filter_fn = input_filter_asus_kb, -}; - -static uinput_filters_t in_asus_kb_2_filters = { - .name = "Asus Keyboard", + .filters = { + .ev = { + .name = "Asus Keyboard" + } + }, + //.logic = &global_logic, + //.ev_input_filter_fn = input_filter_asus_kb, }; static input_dev_t in_asus_kb_2_dev = { .dev_type = input_dev_type_uinput, - .ev_filters = &in_asus_kb_2_filters, - .logic = &global_logic, - .ev_input_filter_fn = input_filter_asus_kb, -}; - -static uinput_filters_t in_asus_kb_3_filters = { - .name = "Asus Keyboard", + .filters = { + .ev = { + .name = "Asus Keyboard" + } + }, + //.logic = &global_logic, + //.ev_input_filter_fn = input_filter_asus_kb, }; static input_dev_t in_asus_kb_3_dev = { .dev_type = input_dev_type_uinput, - .ev_filters = &in_asus_kb_3_filters, - .logic = &global_logic, - .ev_input_filter_fn = input_filter_asus_kb, + .filters = { + .ev = { + .name = "Asus Keyboard" + } + }, + //.logic = &global_logic, + //.ev_input_filter_fn = input_filter_asus_kb, }; static uinput_filters_t in_xbox_filters = { @@ -61,34 +63,23 @@ static uinput_filters_t in_xbox_filters = { static input_dev_t in_xbox_dev = { .dev_type = input_dev_type_uinput, - .ev_filters = &in_xbox_filters, - .logic = &global_logic, - .ev_input_filter_fn = input_filter_identity, + .filters = { + .ev = { + .name = "Microsoft X-Box 360 pad" + } + }, + //.logic = &global_logic, + //.ev_input_filter_fn = input_filter_asus_kb, }; -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; -} +dev_in_data_t dev_in_thread_data; +dev_out_data_t dev_out_thread_data; void sig_handler(int signo) { if (signo == SIGINT) { logic_request_termination(&global_logic); - printf("received SIGINT\n"); + printf("Received SIGINT\n"); } } @@ -101,7 +92,6 @@ int main(int argc, char ** argv) { return EXIT_FAILURE; } - input_dev_t *in_devs[] = { &in_xbox_dev, &in_iio_dev, @@ -110,24 +100,40 @@ int main(int argc, char ** argv) { &in_asus_kb_3_dev, }; - const uint16_t input_dev_count = (sizeof(in_devs) / sizeof(input_dev_t *)); + int out_message_pipes[2]; + pipe(out_message_pipes); - pthread_t *out_threads = malloc(sizeof(pthread_t) * input_dev_count); + int in_message_pipes[2]; + pipe(in_message_pipes); - 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; - } + // populate the input device thread data + dev_in_thread_data.timeout_ms = 400; + dev_in_thread_data.in_message_pipe_fd = in_message_pipes[1]; + dev_in_thread_data.out_message_pipe_fd = out_message_pipes[0]; + dev_in_thread_data.input_dev_decl = *in_devs; + dev_in_thread_data.input_dev_cnt = sizeof(in_devs) / sizeof(input_dev_t *); + + // populate the output device thread data + //dev_out_thread_data.timeout_ms = 400; + dev_out_thread_data.in_message_pipe_fd = in_message_pipes[0]; + dev_out_thread_data.out_message_pipe_fd = out_message_pipes[1]; - pthread_t gamepad_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); + pthread_t dev_in_thread; + const int dev_in_thread_creation = pthread_create(&dev_in_thread, NULL, dev_in_thread_func, (void*)(&dev_in_thread_data)); + if (dev_in_thread_creation != 0) { + fprintf(stderr, "Error creating dev_in thread: %d\n", dev_in_thread_creation); ret = -1; logic_request_termination(&global_logic); - goto gamepad_thread_err; + goto main_err; + } + + pthread_t dev_out_thread; + const int dev_out_thread_creation = pthread_create(&dev_out_thread, NULL, dev_out_thread_func, (void*)(&dev_out_thread_data)); + if (dev_out_thread_creation != 0) { + fprintf(stderr, "Error creating dev_out thread: %d\n", dev_out_thread_creation); + ret = -1; + logic_request_termination(&global_logic); + goto main_err; } /* @@ -139,13 +145,14 @@ int main(int argc, char ** argv) { } */ - for (uint16_t j = 0; j < input_dev_count; ++j) { - pthread_join(out_threads[j], NULL); +main_err: + if (dev_in_thread_creation == 0) { + pthread_join(dev_in_thread, NULL); } - pthread_join(gamepad_thread, NULL); + if (dev_out_thread_creation == 0) { + pthread_join(dev_out_thread, NULL); + } -gamepad_thread_err: -input_threads_err: return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/message.h b/message.h index 9290d09..cc2daba 100644 --- a/message.h +++ b/message.h @@ -2,37 +2,84 @@ #include "imu_message.h" -#define MESSAGE_FLAGS_HANDLE_DONE 0x00000001U -#define EV_MESSAGE_FLAGS_PRESERVE_TIME 0x00000002U -#define EV_MESSAGE_FLAGS_IMU 0x00000004U -#define EV_MESSAGE_FLAGS_MOUSE 0x00000008U +#define EV_MESSAGE_FLAGS_PRESERVE_TIME 0x00000001U +#define EV_MESSAGE_FLAGS_IMU 0x00000002U +#define EV_MESSAGE_FLAGS_MOUSE 0x00000004U -typedef struct ev_message { - struct input_event* ev; - - uint32_t ev_flags; +#define MAX_EVDEV_EVENTS_IN_MESSAGE 16 - uint32_t ev_count; - - size_t ev_size; +typedef enum in_message_gamepad_btn { + GAMEPAD_BTN_A, + GAMEPAD_BTN_B, + GAMEPAD_BTN_X, + GAMEPAD_BTN_Y, + GAMEPAD_BTN_START, + GAMEPAD_BTN_SELECT, + GAMEPAD_BTN_L1, + GAMEPAD_BTN_R1, + GAMEPAD_BTN_L2, + GAMEPAD_BTN_R2, + GAMEPAD_BTN_L3, + GAMEPAD_BTN_R3, +} __packed in_message_gamepad_btn_t; -} ev_message_t; +typedef struct in_message_gamepad_btn_change { + in_message_gamepad_btn_t button; + uint8_t status; +} __packed in_message_gamepad_btn_change_t; -typedef enum message_type { - MSG_TYPE_EV = 0, - MSG_TYPE_IMU, -} message_type_t; +typedef enum in_message_gamepad_delta_type { + GAMEPAD_DELTA_BTN, +} __packed in_message_gamepad_delta_type_t; -#define INPUT_FILTER_FLAGS_NONE 0x00000000U -#define INPUT_FILTER_FLAGS_DO_NOT_EMIT 0x00000001U - -typedef struct message { - message_type_t type; +typedef struct in_message_gamepad_delta { + in_message_gamepad_delta_type_t type; union { - imu_message_t imu; - ev_message_t event; + in_message_gamepad_btn_change_t btn; + } data; +} __packed in_message_gamepad_delta_t; + +typedef enum in_in_message_type { + IN_MSG_TYPE_BTN, + IN_MSG_TYPE_SENSOR, + IN_MSG_TYPE_MACRO, +} __packed in_message_type_t; + +typedef struct in_message { + in_message_type_t type; + + union { + //imu_in_message_t imu; + + in_message_gamepad_delta_t gamepad_delta; } data; - volatile uint32_t flags; -} message_t; +} __packed in_message_t; + + +typedef struct out_message_rumble { + uint16_t strong_magnitude; + uint16_t weak_magnitude; +} __packed out_message_rumble_t; + +typedef struct out_message_leds { + uint8_t r; + uint8_t g; + uint8_t b; +} __packed out_message_leds_t; + +typedef enum out_message_type { + OUT_MSG_TYPE_RUMBLE = 0, + OUT_MSG_TYPE_LEDS, +} __packed out_message_type_t; + +typedef struct out_message { + out_message_type_t type; + + union { + out_message_rumble_t rumble; + out_message_leds_t leds; + } data; + +} __packed out_message_t; diff --git a/output_dev.c b/output_dev.c index c0717de..3281107 100644 --- a/output_dev.c +++ b/output_dev.c @@ -1,7 +1,6 @@ #include "output_dev.h" #include "logic.h" #include "platform.h" -#include "queue.h" #include "message.h" #include "settings.h" #include "virt_ds4.h" @@ -11,7 +10,7 @@ #define DECODE_EV_FLAG_MODE_MAIN_MENU_REQUESTED 0x00000002U #define DECODE_EV_FLAG_MODE_QAM_REQUESTED 0x00000004U -static uint32_t decode_ev(output_dev_t *const out_dev, message_t *const msg) { +static uint32_t decode_ev(output_dev_t *const out_dev, in_message_t *const msg) { uint32_t flags = 0x00000000U; // scan for mouse mode and emit events in the virtual mouse if required @@ -76,7 +75,6 @@ static uint32_t 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_count == 2) && - (msg->data.event.ev_size >= 3) && (msg->data.event.ev[0].value == -13565896) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_PROG1) @@ -88,7 +86,7 @@ static uint32_t decode_ev(output_dev_t *const out_dev, message_t *const msg) { return flags; } -static void update_gs_from_ev(devices_status_t *const stats, message_t *const msg, controller_settings_t *const settings) { +static void update_gs_from_ev(devices_status_t *const stats, in_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) && @@ -216,7 +214,7 @@ static void update_gs_from_ev(devices_status_t *const stats, message_t *const ms } } -static void update_gs_from_imu(devices_status_t *const stats, message_t *const msg, controller_settings_t *const settings) { +static void update_gs_from_imu(devices_status_t *const stats, in_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; @@ -238,8 +236,8 @@ static void update_gs_from_imu(devices_status_t *const stats, message_t *const m } } -static void handle_msg(output_dev_t *const out_dev, message_t *const msg) { - if (msg->type == MSG_TYPE_EV) { +static void handle_msg(output_dev_t *const out_dev, in_message_t *const msg) { + if (msg->type == IN_MSG_TYPE_EV) { const uint32_t decode_ev_res_flags = decode_ev(out_dev, msg); if (decode_ev_res_flags & DECODE_EV_FLAG_MODE_SWITCH_REQUESTED) { @@ -275,9 +273,9 @@ static void handle_msg(output_dev_t *const out_dev, message_t *const msg) { return; } - if (msg->type == MSG_TYPE_EV) { + if (msg->type == IN_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) { + } else if (msg->type == IN_MSG_TYPE_IMU) { update_gs_from_imu(&out_dev->logic->dev_stats, msg, &out_dev->logic->controller_settings); } @@ -294,7 +292,7 @@ int handle_rumble(output_dev_t *const out_dev, uint64_t *rumble_events_count) { // check if the gamepad has notified the presence of a rumble event if (tmp_ev_count != *rumble_events_count) { - rumble_message_t *const rumble_msg = malloc(sizeof(rumble_message_t)); + rumble_in_message_t *const rumble_msg = malloc(sizeof(rumble_in_message_t)); if(rumble_msg != NULL) { rumble_msg->strong_magnitude = (uint16_t)left_motor << (uint16_t)8; rumble_msg->weak_magnitude = (uint16_t)right_motor << (uint16_t)8; @@ -385,7 +383,7 @@ void *output_dev_thread_func(void *ptr) { void *raw_ev; const int pop_res = queue_pop_timeout(&out_dev->logic->input_queue, &raw_ev, 1); if (pop_res == 0) { - message_t *const msg = (message_t*)raw_ev; + in_message_t *const msg = (in_message_t*)raw_ev; handle_msg(out_dev, msg); // from now on it's forbidden to use this memory diff --git a/output_dev.h b/output_dev.h index dfae899..f1494bf 100644 --- a/output_dev.h +++ b/output_dev.h @@ -1,6 +1,5 @@ #pragma once -#include "queue.h" #include "logic.h" #undef INCLUDE_TIMESTAMP @@ -8,6 +7,13 @@ typedef struct output_dev { logic_t *logic; + + // this pipe is reserved for reporting in_message_t + int in_message_pipe_fd; + + // this messages is reserved for receiving out_message_t + int out_message_pipe_fd; + } output_dev_t; void *output_dev_thread_func(void *ptr); diff --git a/queue.c b/queue.c deleted file mode 100644 index 31abd2d..0000000 --- a/queue.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "queue.h" - -int queue_init(queue_t* const q, size_t max_elements) { - q->front = q->rear = -1; - q->array_size = max_elements; - if (pthread_mutex_init(&q->mutex, NULL) != 0) { - perror("mutex"); - return -1; - } - - q->array = calloc(sizeof(void*), max_elements); - if (q->array == NULL) { - perror("calloc"); - return -1; - } - - sem_init(&q->empty, 0, q->array_size); - sem_init(&q->full, 0, 0); - - return 0; -} - -void queue_destroy(queue_t* q) { - free(q->array); - q->array = NULL; -} - -int queue_push(queue_t* const q, void *in_item) { - sem_wait(&q->empty); - pthread_mutex_lock(&q->mutex); - - q->rear = (q->rear + 1) % q->array_size; - q->array[q->rear] = in_item; - - pthread_mutex_unlock(&q->mutex); - sem_post(&q->full); - - return 0; -} - -int queue_pop(queue_t* const q, void **out_item) { - sem_wait(&q->full); - pthread_mutex_lock(&q->mutex); - - q->front = (q->front + 1) % q->array_size; - *out_item = q->array[q->front]; - - pthread_mutex_unlock(&q->mutex); - sem_post(&q->empty); - - return 0; -} - -int queue_push_timeout(queue_t* const q, void *in_item, int timeout_ms) { - struct timespec timeout; - if (clock_gettime(CLOCK_REALTIME, &timeout) == -1) { - // Handle clock_gettime error - return -1; - } - - timeout.tv_sec += timeout_ms / 1000; - timeout.tv_nsec += (timeout_ms % 1000) * 1000000; - - int result = sem_timedwait(&q->empty, &timeout); - - if (result == 0) { - pthread_mutex_lock(&q->mutex); - - q->rear = (q->rear + 1) % q->array_size; - q->array[q->rear] = in_item; - - pthread_mutex_unlock(&q->mutex); - sem_post(&q->full); - } - - return result; -} - -int queue_pop_timeout(queue_t* const q, void **out_item, int timeout_ms) { - struct timespec timeout; - if (clock_gettime(CLOCK_MONOTONIC, &timeout) == -1) { - // Handle clock_gettime error - return -1; - } - - timeout.tv_sec += timeout_ms / 1000; - timeout.tv_nsec += (timeout_ms % 1000) * 1000000; - - int result = sem_timedwait(&q->full, &timeout); - - if (result == 0) { - pthread_mutex_lock(&q->mutex); - - q->front = (q->front + 1) % q->array_size; - *out_item = q->array[q->front]; - - pthread_mutex_unlock(&q->mutex); - sem_post(&q->empty); - } - - return result; -} \ No newline at end of file diff --git a/queue.h b/queue.h deleted file mode 100644 index d8373f5..0000000 --- a/queue.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "rogue_enemy.h" - -typedef struct queue { - sem_t empty, full; - pthread_mutex_t mutex; - - ssize_t front; - ssize_t rear; - - size_t array_size; - void** array; -} queue_t; - -int queue_init(queue_t* queue, size_t max_elements); - -void queue_destroy(queue_t* queue); - -int queue_push(queue_t* queue, void *in_item); - -int queue_push_timeout(queue_t* const q, void *in_item, int timeout_ms); - -int queue_pop(queue_t* queue, void **out_item); - -int queue_pop_timeout(queue_t* const q, void **out_item, int timeout_ms); diff --git a/rogue_enemy.h b/rogue_enemy.h index 03fa521..391d357 100644 --- a/rogue_enemy.h +++ b/rogue_enemy.h @@ -10,14 +10,19 @@ #include #include #include +#include +#include + #include #include #include #include #include #include -#include +#include +#include +#include #include #include @@ -27,8 +32,15 @@ #include +#include + #define LSB_PER_RAD_S_2000_DEG_S ((double)0.001064724) #define LSB_PER_RAD_S_2000_DEG_S_STR "0.001064724" #define LSB_PER_16G ((double)0.004785) #define LSB_PER_16G_STR "0.004785" + +// courtesy of linux kernel +#ifndef __packed +#define __packed __attribute__((packed)) +#endif diff --git a/virt_ds4.c b/virt_ds4.c index 29b1b6c..2a2b29c 100644 --- a/virt_ds4.c +++ b/virt_ds4.c @@ -1,4 +1,5 @@ #include "virt_ds4.h" +#include "message.h" #include #include @@ -335,232 +336,6 @@ static void destroy(int fd) uhid_write(fd, &ev); } -/* This parses raw output reports sent by the kernel to the device. A normal - * 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, 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) - return; - - /* - if (ds4->update_rumble) { - * Select classic rumble style haptics and enable it. * - common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_MOTOR; - common->motor_left = ds4->motor_left; - common->motor_right = ds4->motor_right; - ds4->update_rumble = false; - } - - if (ds4->update_lightbar) { - common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED; - * Comptabile behavior with hid-sony, which used a dummy global LED to - * allow enabling/disabling the lightbar. The global LED maps to - * lightbar_enabled. - * - common->lightbar_red = ds4->lightbar_enabled ? ds4->lightbar_red : 0; - common->lightbar_green = ds4->lightbar_enabled ? ds4->lightbar_green : 0; - common->lightbar_blue = ds4->lightbar_enabled ? ds4->lightbar_blue : 0; - ds4->update_lightbar = false; - } - - if (ds4->update_lightbar_blink) { - common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED_BLINK; - common->lightbar_blink_on = ds4->lightbar_blink_on; - common->lightbar_blink_off = ds4->lightbar_blink_off; - ds4->update_lightbar_blink = false; - } - */ - - if (ev->u.output.size != 32) { - fprintf(stderr, "Invalid data length: got %d, expected 32\n", (int)ev->u.output.size); - - return; - } - - // first byte is report-id which is 0x01 - if (ev->u.output.data[0] != 0x05) { - fprintf(stderr, "Unrecognised report-id: %d\n", (int)ev->u.output.data[0]); - return; - } - - const uint8_t valid_flag0 = ev->u.output.data[1]; - const uint8_t valid_flag1 = ev->u.output.data[2]; - const uint8_t reserved = ev->u.output.data[3]; - const uint8_t motor_right = ev->u.output.data[4]; - const uint8_t motor_left = ev->u.output.data[5]; - const uint8_t lightbar_red = ev->u.output.data[6]; - const uint8_t lightbar_green = ev->u.output.data[7]; - const uint8_t lightbar_blue = ev->u.output.data[8]; - 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)) { - 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( - "Updated rumble -- motor_left: %d, motor_right: %d, valid_flag0; %d, valid_flag1: %d\n", - motor_left, - motor_right, - valid_flag0, - valid_flag1 - ); -#endif - } -} - -static int event(int fd, gamepad_status_t *const gamepad_stats) -{ - struct uhid_event ev; - ssize_t ret; - - memset(&ev, 0, sizeof(ev)); - ret = read(fd, &ev, sizeof(ev)); - if (ret == 0) { - fprintf(stderr, "Read HUP on uhid-cdev\n"); - return -EFAULT; - } else if (ret == -1) { - return 0; - } else if (ret < 0) { - fprintf(stderr, "Cannot read uhid-cdev: %d\n", (int)ret); - return -errno; - } else if (ret != sizeof(ev)) { - fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n", - ret, sizeof(ev)); - return -EFAULT; - } - - switch (ev.type) { - case UHID_START: -#if defined(VIRT_DS4_DEBUG) - printf("UHID_START from uhid-dev\n"); -#endif - break; - case UHID_STOP: -#if defined(VIRT_DS4_DEBUG) - printf("UHID_STOP from uhid-dev\n"); -#endif - break; - case UHID_OPEN: -#if defined(VIRT_DS4_DEBUG) - printf("UHID_OPEN from uhid-dev\n"); -#endif - break; - case UHID_CLOSE: -#if defined(VIRT_DS4_DEBUG) - printf(stderr, "UHID_CLOSE from uhid-dev\n"); -#endif - break; - case UHID_OUTPUT: -#if defined(VIRT_DS4_DEBUG) - printf("UHID_OUTPUT from uhid-dev\n"); -#endif - handle_output(&ev, gamepad_stats); - break; - case UHID_OUTPUT_EV: -#if defined(VIRT_DS4_DEBUG) - printf("UHID_OUTPUT_EV from uhid-dev\n"); -#endif - break; - case UHID_GET_REPORT: - //fprintf(stderr, "UHID_GET_REPORT from uhid-dev, report=%d\n", ev.u.get_report.rnum); - if (ev.u.get_report.rnum == 18) { - const struct uhid_event mac_addr_response = { - .type = UHID_GET_REPORT_REPLY, - .u = { - .get_report_reply = { - .size = 16, - .id = ev.u.get_report.id, - .err = 0, - .data = { - 0x12, 0xf2, 0xa5, 0x71, 0x68, 0xaf, 0xdc, 0x08, - 0x25, 0x00, 0x4c, 0x46, 0x49, 0x0e, 0x41, 0x00 - } - } - } - }; - - uhid_write(fd, &mac_addr_response); - } else if (ev.u.get_report.rnum == 0xa3) { - const struct uhid_event firmware_info_response = { - .type = UHID_GET_REPORT_REPLY, - .u = { - .get_report_reply = { - .size = 49, - .id = ev.u.get_report.id, - .err = 0, - .data = { - 0xa3, 0x53, 0x65, 0x70, 0x20, 0x32, 0x31, 0x20, - 0x32, 0x30, 0x31, 0x38, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x30, 0x34, 0x3a, 0x35, 0x30, 0x3a, 0x35, - 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x0c, 0xb4, 0x01, 0x00, 0x00, - 0x00, 0x0a, 0xa0, 0x10, 0x20, 0x00, 0xa0, 0x02, - 0x00 - } - } - } - }; - - uhid_write(fd, &firmware_info_response); - } else if (ev.u.get_report.rnum == 0x02) { // dualshock4_get_calibration_data - struct uhid_event firmware_info_response = { - .type = UHID_GET_REPORT_REPLY, - .u = { - .get_report_reply = { - .size = 37, - .id = ev.u.get_report.id, - .err = 0, - .data = { - 0x02, 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, 0x06, 0x00, - - } - } - } - }; - - // bias in kernel is 0 (embedded constant) - // speed_2x = speed_2x*DS4_GYRO_RES_PER_DEG_S; calculated by the kernel will be 1080. - // As a consequence sens_numer (for every axis) is 1080*1024. - // that number will be 1105920 - memcpy((void*)&firmware_info_response.u.get_report_reply.data[1], (const void*)&gyro_pitch_bias, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[3], (const void*)&gyro_yaw_bias, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[5], (const void*)&gyro_roll_bias, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[7], (const void*)&gyro_pitch_plus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[9], (const void*)&gyro_pitch_minus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[11], (const void*)&gyro_yaw_plus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[13], (const void*)&gyro_yaw_minus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[15], (const void*)&gyro_roll_plus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[17], (const void*)&gyro_roll_minus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[19], (const void*)&gyro_speed_plus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[21], (const void*)&gyro_speed_minus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[23], (const void*)&acc_x_plus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[25], (const void*)&acc_x_minus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[27], (const void*)&acc_y_plus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[29], (const void*)&acc_y_minus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[31], (const void*)&acc_z_plus, sizeof(int16_t)); - memcpy((void*)&firmware_info_response.u.get_report_reply.data[33], (const void*)&acc_z_minus, sizeof(int16_t)); - - uhid_write(fd, &firmware_info_response); - } - - break; - default: - fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); - } - - return 0; -} - typedef enum ds4_dpad_status { DPAD_N = 0, DPAD_NE = 1, @@ -735,74 +510,289 @@ static void compose_hid_report_buffer(int fd, gamepad_status_t *const gamepad_st buf[44] = 0x80; // IDK... it seems constant... } -/** - * Thread function emulating the DualShock4 controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs. - * - * See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3 - */ -void *virt_ds4_thread_func(void *ptr) { - devices_status_t *const stats = (devices_status_t*)ptr; +int virt_dualshock_init(virt_dualshock_t *const out_gamepad) { + int ret = 0; - 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; + out_gamepad->debug = false; + + out_gamepad->fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK); + if (out_gamepad->fd < 0) { + fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, out_gamepad->fd); + ret = out_gamepad->fd; + goto virt_dualshock_init_err; } - fprintf(stderr, "Create uhid device\n"); - int ret = create(fd); + ret = create(out_gamepad->fd); if (ret) { - close(fd); - return NULL; + fprintf(stderr, "Error creating uhid device: %d\n", ret); + close(out_gamepad->fd); + goto virt_dualshock_init_err; } - 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); - - gamepad_status_qam_quirk(&stats->gamepad); - - if (stats->gamepad.connected) { - compose_hid_report_buffer(fd, &stats->gamepad, buf); - } else { - pthread_mutex_unlock(&stats->mutex); - - printf("DualShock has been terminated: closing the device.\n"); - break; - } - } - - 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; +virt_dualshock_init_err: + return ret; +} + +int virt_dualshock_get_fd(virt_dualshock_t *const in_gamepad) { + return in_gamepad->fd; +} + +int virt_dualshock_event(virt_dualshock_t *const gamepad, gamepad_status_t *const out_device_status, int out_message_pipe_fd) { + struct uhid_event ev; + ssize_t ret; + + memset(&ev, 0, sizeof(ev)); + ret = read(gamepad->fd, &ev, sizeof(ev)); + if (ret == 0) { + fprintf(stderr, "Read HUP on uhid-cdev\n"); + return -EFAULT; + } else if (ret == -1) { + return 0; + } else if (ret < 0) { + fprintf(stderr, "Cannot read uhid-cdev: %d\n", (int)ret); + return -errno; + } else if (ret != sizeof(ev)) { + fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n", + ret, sizeof(ev)); + return -EFAULT; + } + + switch (ev.type) { + case UHID_START: + if (gamepad->debug) { + printf("UHID_START from uhid-dev\n"); + } + break; + case UHID_STOP: + if (gamepad->debug) { + printf("UHID_STOP from uhid-dev\n"); + } + break; + case UHID_OPEN: + if (gamepad->debug) { + printf("UHID_OPEN from uhid-dev\n"); + } + break; + case UHID_CLOSE: + if (gamepad->debug) { + printf(stderr, "UHID_CLOSE from uhid-dev\n"); + } + break; + case UHID_OUTPUT: + if (gamepad->debug) { + printf("UHID_OUTPUT from uhid-dev\n"); + } + + // Rumble and LED messages are adverised via OUTPUT reports; ignore the rest + if (ev.u.output.rtype != UHID_OUTPUT_REPORT) + return 0; + + /* + if (ds4->update_rumble) { + * Select classic rumble style haptics and enable it. * + common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_MOTOR; + common->motor_left = ds4->motor_left; + common->motor_right = ds4->motor_right; + ds4->update_rumble = false; + } + + if (ds4->update_lightbar) { + common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED; + * Comptabile behavior with hid-sony, which used a dummy global LED to + * allow enabling/disabling the lightbar. The global LED maps to + * lightbar_enabled. + * + common->lightbar_red = ds4->lightbar_enabled ? ds4->lightbar_red : 0; + common->lightbar_green = ds4->lightbar_enabled ? ds4->lightbar_green : 0; + common->lightbar_blue = ds4->lightbar_enabled ? ds4->lightbar_blue : 0; + ds4->update_lightbar = false; + } + + if (ds4->update_lightbar_blink) { + common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED_BLINK; + common->lightbar_blink_on = ds4->lightbar_blink_on; + common->lightbar_blink_off = ds4->lightbar_blink_off; + ds4->update_lightbar_blink = false; + } + */ + + if (ev.u.output.size != 32) { + fprintf(stderr, "Invalid data length: got %d, expected 32\n", (int)ev.u.output.size); + + return 0; + } + + // first byte is report-id which is 0x01 + if (ev.u.output.data[0] != 0x05) { + fprintf(stderr, "Unrecognised report-id: %d\n", (int)ev.u.output.data[0]); + return 0; + } + + const uint8_t valid_flag0 = ev.u.output.data[1]; + const uint8_t valid_flag1 = ev.u.output.data[2]; + const uint8_t reserved = ev.u.output.data[3]; + const uint8_t motor_right = ev.u.output.data[4]; + const uint8_t motor_left = ev.u.output.data[5]; + const uint8_t lightbar_red = ev.u.output.data[6]; + const uint8_t lightbar_green = ev.u.output.data[7]; + const uint8_t lightbar_blue = ev.u.output.data[8]; + 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)) { + out_device_status->motors_intensity[0] = motor_left; + out_device_status->motors_intensity[1] = motor_right; + ++out_device_status->rumble_events_count; + + out_message_t msg = { + .type = OUT_MSG_TYPE_RUMBLE, + .data = { + .rumble = { + .strong_magnitude = motor_left == 0 ? 0 : (uint16_t)0x00FF | ((uint16_t)motor_left << (uint16_t)8), + .weak_magnitude = motor_right == 0 ? 0 : (uint16_t)0x00FF | ((uint16_t)motor_right << (uint16_t)8), + } + } + }; + + const int write_res = write(out_message_pipe_fd, (void*)&msg, sizeof(msg)); + if (write_res != 0) { + return write_res; + } + + if (gamepad->debug) { + printf( + "Updated rumble -- motor_left: %d, motor_right: %d, valid_flag0; %d, valid_flag1: %d\n", + motor_left, + motor_right, + valid_flag0, + valid_flag1 + ); + } + } + break; + case UHID_OUTPUT_EV: + if (gamepad->debug) { + printf("UHID_OUTPUT_EV from uhid-dev\n"); + } + break; + case UHID_GET_REPORT: + if (gamepad->debug) { + printf("UHID_GET_REPORT from uhid-dev, report=%d\n", ev.u.get_report.rnum); + } + + if (ev.u.get_report.rnum == 18) { + const struct uhid_event mac_addr_response = { + .type = UHID_GET_REPORT_REPLY, + .u = { + .get_report_reply = { + .size = 16, + .id = ev.u.get_report.id, + .err = 0, + .data = { + 0x12, 0xf2, 0xa5, 0x71, 0x68, 0xaf, 0xdc, 0x08, + 0x25, 0x00, 0x4c, 0x46, 0x49, 0x0e, 0x41, 0x00 + } + } + } + }; + + uhid_write(gamepad->fd, &mac_addr_response); + } else if (ev.u.get_report.rnum == 0xa3) { + const struct uhid_event firmware_info_response = { + .type = UHID_GET_REPORT_REPLY, + .u = { + .get_report_reply = { + .size = 49, + .id = ev.u.get_report.id, + .err = 0, + .data = { + 0xa3, 0x53, 0x65, 0x70, 0x20, 0x32, 0x31, 0x20, + 0x32, 0x30, 0x31, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x34, 0x3a, 0x35, 0x30, 0x3a, 0x35, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x0c, 0xb4, 0x01, 0x00, 0x00, + 0x00, 0x0a, 0xa0, 0x10, 0x20, 0x00, 0xa0, 0x02, + 0x00 + } + } + } + }; + + uhid_write(gamepad->fd, &firmware_info_response); + } else if (ev.u.get_report.rnum == 0x02) { // dualshock4_get_calibration_data + struct uhid_event firmware_info_response = { + .type = UHID_GET_REPORT_REPLY, + .u = { + .get_report_reply = { + .size = 37, + .id = ev.u.get_report.id, + .err = 0, + .data = { + 0x02, 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, 0x06, 0x00, + + } + } + } + }; + + // bias in kernel is 0 (embedded constant) + // speed_2x = speed_2x*DS4_GYRO_RES_PER_DEG_S; calculated by the kernel will be 1080. + // As a consequence sens_numer (for every axis) is 1080*1024. + // that number will be 1105920 + memcpy((void*)&firmware_info_response.u.get_report_reply.data[1], (const void*)&gyro_pitch_bias, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[3], (const void*)&gyro_yaw_bias, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[5], (const void*)&gyro_roll_bias, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[7], (const void*)&gyro_pitch_plus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[9], (const void*)&gyro_pitch_minus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[11], (const void*)&gyro_yaw_plus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[13], (const void*)&gyro_yaw_minus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[15], (const void*)&gyro_roll_plus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[17], (const void*)&gyro_roll_minus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[19], (const void*)&gyro_speed_plus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[21], (const void*)&gyro_speed_minus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[23], (const void*)&acc_x_plus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[25], (const void*)&acc_x_minus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[27], (const void*)&acc_y_plus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[29], (const void*)&acc_y_minus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[31], (const void*)&acc_z_plus, sizeof(int16_t)); + memcpy((void*)&firmware_info_response.u.get_report_reply.data[33], (const void*)&acc_z_minus, sizeof(int16_t)); + + uhid_write(gamepad->fd, &firmware_info_response); + } + + break; + default: + fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type); + } + + return 0; +} + +void virt_dualshock_close(virt_dualshock_t *const out_gamepad) { + destroy(out_gamepad->fd); +} + +void virt_dualshock_compose(virt_dualshock_t *const gamepad, gamepad_status_t *const in_device_status, uint8_t *const out_buf) { + gamepad_status_qam_quirk(in_device_status); + + compose_hid_report_buffer(gamepad->fd, in_device_status, out_buf); +} + +int virt_dualshock_send(virt_dualshock_t *const gamepad, uint8_t *const out_buf) { + struct uhid_event l = { + .type = UHID_INPUT2, + .u = { + .input2 = { + .size = 64, + } + } + }; + + memcpy(&l.u.input2.data[0], &out_buf[0], l.u.input2.size); + + return uhid_write(gamepad->fd, &l); } diff --git a/virt_ds4.h b/virt_ds4.h index 95de9a0..85940f3 100644 --- a/virt_ds4.h +++ b/virt_ds4.h @@ -1,7 +1,31 @@ #pragma once -#include "logic.h" +#include "message.h" +#include "devices_status.h" #undef VIRT_DS4_DEBUG +/** + * Emulator of the DualShock4 controller at USB level using USB UHID ( https://www.kernel.org/doc/html/latest/hid/uhid.html ) kernel APIs. + * + * See https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/hid/uhid.txt?id=refs/tags/v4.10-rc3 + */ +typedef struct virt_dualshock { + int fd; + + bool debug; +} virt_dualshock_t; + +int virt_dualshock_init(virt_dualshock_t *const gamepad); + +int virt_dualshock_get_fd(virt_dualshock_t *const gamepad); + +int virt_dualshock_event(virt_dualshock_t *const gamepad, gamepad_status_t *const out_device_status, int out_message_pipe_fd); + +void virt_dualshock_compose(virt_dualshock_t *const gamepad, gamepad_status_t *const in_device_status, uint8_t *const out_buf); + +int virt_dualshock_send(virt_dualshock_t *const gamepad, uint8_t *const out_buf); + +void virt_dualshock_close(virt_dualshock_t *const gamepad); + void *virt_ds4_thread_func(void *ptr); diff --git a/virt_evdev.c b/virt_evdev.c index ca69f65..25aacfc 100644 --- a/virt_evdev.c +++ b/virt_evdev.c @@ -4,7 +4,7 @@ static const char* uinput_path = "/dev/uinput"; -static int create_gamepad_uinput() { +static int create_gamepad_uinput(void) { int fd = open(uinput_path, O_WRONLY | O_NONBLOCK); if(fd < 0) { fd = -1; @@ -491,7 +491,7 @@ void *virt_evdev_thread_func(void *ptr) { } /* -static void emit_ev(output_dev_t *const out_dev, const message_t *const msg) { +static void emit_ev(output_dev_t *const out_dev, const in_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; @@ -569,4 +569,4 @@ static void emit_ev(output_dev_t *const out_dev, const message_t *const msg) { 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_mouse_kbd.c b/virt_mouse_kbd.c index 5aa7f13..c72c237 100644 --- a/virt_mouse_kbd.c +++ b/virt_mouse_kbd.c @@ -2,7 +2,7 @@ static const char* uinput_path = "/dev/uinput"; -static int create_mouse_uinput() { +static int create_mouse_uinput(void) { int fd = open(uinput_path, O_WRONLY | O_NONBLOCK); if(fd < 0) { fd = -1; @@ -76,7 +76,7 @@ create_mouse_uinput_err: return fd; } -static int create_kb_uinput() { +static int create_kb_uinput(void) { return -EINVAL; }