The bbcr broke off my compilation help plz

This commit is contained in:
Denis 2023-12-06 21:05:02 +01:00
parent 91a639852f
commit 37c23e1b62
No known key found for this signature in database
GPG key ID: DD9B63F805CF5C03
28 changed files with 1042 additions and 1256 deletions

View file

@ -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)

View file

@ -2,7 +2,7 @@
CFLAGS= -O3 -march=znver4 -D _DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112L -std=c11 -fPIE -pedantic -Wall -flto=full # -Werror
LDFLAGS=-lpthread -levdev -ludev -lconfig -lrt -lm -flto=full
CC=clang
OBJECTS=main.o input_dev.o dev_iio.o output_dev.o queue.o logic.o platform.o settings.o virt_ds4.o virt_ds5.o 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)

130
dev_evdev.c Normal file
View file

@ -0,0 +1,130 @@
#include "dev_evdev.h"
#include <libevdev-1.0/libevdev/libevdev.h>
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;
}

10
dev_evdev.h Normal file
View file

@ -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);

View file

@ -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);

View file

@ -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
);

327
dev_in.c Normal file
View file

@ -0,0 +1,327 @@
#include "dev_in.h"
#include "message.h"
#include "dev_evdev.h"
#include <libevdev-1.0/libevdev/libevdev.h>
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], &current_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*)&current_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;
}

26
dev_in.h Normal file
View file

@ -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);

23
dev_out.c Normal file
View file

@ -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;
}

26
dev_out.h Normal file
View file

@ -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);

View file

@ -26,4 +26,4 @@ typedef struct imu_message {
uint32_t flags;
} imu_message_t;
} imu_in_message_t;

View file

@ -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 <stdlib.h>
#include <libevdev-1.0/libevdev/libevdev.h>
#include <linux/input-event-codes.h>
#include <linux/input.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <dirent.h>
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, &current_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;
}

View file

@ -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);

14
logic.c
View file

@ -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) {

View file

@ -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;

139
main.c
View file

@ -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 *);
pthread_t gamepad_thread;
// 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];
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;
}

View file

@ -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;
#define MAX_EVDEV_EVENTS_IN_MESSAGE 16
uint32_t ev_flags;
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;
uint32_t ev_count;
typedef struct in_message_gamepad_btn_change {
in_message_gamepad_btn_t button;
uint8_t status;
} __packed in_message_gamepad_btn_change_t;
size_t ev_size;
typedef enum in_message_gamepad_delta_type {
GAMEPAD_DELTA_BTN,
} __packed in_message_gamepad_delta_type_t;
} ev_message_t;
typedef enum message_type {
MSG_TYPE_EV = 0,
MSG_TYPE_IMU,
} message_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;

View file

@ -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

View file

@ -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);

102
queue.c
View file

@ -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;
}

26
queue.h
View file

@ -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);

View file

@ -10,14 +10,19 @@
#include <unistd.h>
#include <string.h>
#include <math.h>
#include <termios.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/hidraw.h>
#include <sys/select.h>
#include <linux/hidraw.h>
#include <linux/input-event-codes.h>
#include <linux/uinput.h>
#include <linux/input.h>
@ -27,8 +32,15 @@
#include <libevdev-1.0/libevdev/libevdev.h>
#include <libevdev-1.0/libevdev/libevdev.h>
#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

View file

@ -1,4 +1,5 @@
#include "virt_ds4.h"
#include "message.h"
#include <bits/types/time_t.h>
#include <linux/uhid.h>
@ -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);
}

View file

@ -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);

View file

@ -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;

View file

@ -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;
}