The bbcr broke off my compilation help plz
This commit is contained in:
parent
91a639852f
commit
37c23e1b62
28 changed files with 1042 additions and 1256 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
2
Makefile
2
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)
|
||||
|
|
|
|||
130
dev_evdev.c
Normal file
130
dev_evdev.c
Normal 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
10
dev_evdev.h
Normal 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);
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
327
dev_in.c
Normal 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], ¤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;
|
||||
}
|
||||
26
dev_in.h
Normal file
26
dev_in.h
Normal 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
23
dev_out.c
Normal 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
26
dev_out.h
Normal 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);
|
||||
|
|
@ -26,4 +26,4 @@ typedef struct imu_message {
|
|||
|
||||
uint32_t flags;
|
||||
|
||||
} imu_message_t;
|
||||
} imu_in_message_t;
|
||||
|
|
|
|||
688
input_dev.c
688
input_dev.c
|
|
@ -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, ¤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;
|
||||
}
|
||||
17
input_dev.h
17
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);
|
||||
|
|
|
|||
14
logic.c
14
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) {
|
||||
|
|
|
|||
7
logic.h
7
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;
|
||||
|
|
|
|||
139
main.c
139
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 *);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
97
message.h
97
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;
|
||||
#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;
|
||||
|
|
|
|||
20
output_dev.c
20
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
|
||||
|
|
|
|||
|
|
@ -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
102
queue.c
|
|
@ -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
26
queue.h
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
|
|
|
|||
530
virt_ds4.c
530
virt_ds4.c
|
|
@ -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,59 +510,279 @@ 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];
|
||||
virt_dualshock_init_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
usleep(1250);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
int virt_dualshock_get_fd(virt_dualshock_t *const in_gamepad) {
|
||||
return in_gamepad->fd;
|
||||
}
|
||||
|
||||
const int lock_res = pthread_mutex_lock(&stats->mutex);
|
||||
if (lock_res != 0) {
|
||||
printf("Unable to lock gamepad mutex: %d\n", lock_res);
|
||||
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;
|
||||
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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");
|
||||
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");
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&stats->mutex);
|
||||
// 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 = {
|
||||
|
|
@ -797,12 +792,7 @@ void *virt_ds4_thread_func(void *ptr) {
|
|||
}
|
||||
};
|
||||
|
||||
memcpy(&l.u.input2.data[0], &buf[0], l.u.input2.size);
|
||||
memcpy(&l.u.input2.data[0], &out_buf[0], l.u.input2.size);
|
||||
|
||||
uhid_write(fd, &l);
|
||||
}
|
||||
|
||||
destroy(fd);
|
||||
|
||||
return NULL;
|
||||
return uhid_write(gamepad->fd, &l);
|
||||
}
|
||||
|
|
|
|||
26
virt_ds4.h
26
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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue