test IMU output

This commit is contained in:
Denis 2023-11-17 23:07:16 +01:00
parent 720435b6e8
commit 9c33257a3b
No known key found for this signature in database
GPG key ID: DD9B63F805CF5C03
18 changed files with 1102 additions and 251 deletions

View file

@ -1,7 +1,7 @@
CFLAGS= -g -O0 -D _DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112L -std=c11 -fPIE -pedantic -Wall # -Werror
LDFLAGS=-lpthread -levdev -lrt -lm
CC=gcc
OBJECTS=main.o input_dev.o dev_iio.o output_dev.o queue.o platform.o
OBJECTS=main.o input_dev.o dev_iio.o output_dev.o queue.o logic.o platform.o virt_ds4.o
TARGET=rogue_enemy
all: $(TARGET)

View file

@ -354,3 +354,87 @@ int dev_iio_read(
return 0;
}
int dev_iio_read_imu(const dev_iio_t *const iio, imu_message_t *const out) {
char tmp[128];
if (iio->accel_x_fd != NULL) {
rewind(iio->accel_x_fd);
memset((void*)&tmp[0], 0, sizeof(tmp));
const int tmp_read = fread((void*)&tmp[0], 1, sizeof(tmp), iio->accel_x_fd);
if (tmp_read >= 0) {
out->accel_x_raw = strtol(&tmp[0], NULL, 10);
out->accel_x_in_m2s = (double)out->accel_x_raw * iio->accel_scale_x;
} else {
fprintf(stderr, "While reading accel(x): %d\n", tmp_read);
return tmp_read;
}
}
if (iio->accel_y_fd != NULL) {
rewind(iio->accel_y_fd);
memset((void*)&tmp[0], 0, sizeof(tmp));
const int tmp_read = fread((void*)&tmp[0], 1, sizeof(tmp), iio->accel_y_fd);
if (tmp_read >= 0) {
out->accel_y_raw = strtol(&tmp[0], NULL, 10);
out->accel_y_in_m2s = (double)out->accel_y_raw * iio->accel_scale_y;
} else {
fprintf(stderr, "While reading accel(y): %d\n", tmp_read);
return tmp_read;
}
}
if (iio->accel_z_fd != NULL) {
rewind(iio->accel_z_fd);
memset((void*)&tmp[0], 0, sizeof(tmp));
const int tmp_read = fread((void*)&tmp[0], 1, sizeof(tmp), iio->accel_z_fd);
if (tmp_read >= 0) {
out->accel_z_raw = strtol(&tmp[0], NULL, 10);
out->accel_z_in_m2s = (double)out->accel_z_raw * iio->accel_scale_z;
} else {
fprintf(stderr, "While reading accel(z): %d\n", tmp_read);
return tmp_read;
}
}
if (iio->anglvel_x_fd != NULL) {
rewind(iio->anglvel_x_fd);
memset((void*)&tmp[0], 0, sizeof(tmp));
const int tmp_read = fread((void*)&tmp[0], 1, sizeof(tmp), iio->anglvel_x_fd);
if (tmp_read >= 0) {
out->gyro_x_raw = strtol(&tmp[0], NULL, 10);
out->gyro_x_in_rad_s = (double)out->gyro_x_raw * iio->anglvel_scale_x;
} else {
fprintf(stderr, "While reading anglvel(x): %d\n", tmp_read);
return tmp_read;
}
}
if (iio->anglvel_y_fd != NULL) {
rewind(iio->anglvel_y_fd);
memset((void*)&tmp[0], 0, sizeof(tmp));
const int tmp_read = fread((void*)&tmp[0], 1, sizeof(tmp), iio->anglvel_y_fd);
if (tmp_read >= 0) {
out->gyro_y_raw = strtol(&tmp[0], NULL, 10);
out->gyro_y_in_rad_s = (double)out->gyro_y_raw *iio->anglvel_scale_y;
} else {
fprintf(stderr, "While reading anglvel(y): %d\n", tmp_read);
return tmp_read;
}
}
if (iio->anglvel_z_fd != NULL) {
rewind(iio->anglvel_z_fd);
memset((void*)&tmp[0], 0, sizeof(tmp));
const int tmp_read = fread((void*)&tmp[0], 1, sizeof(tmp), iio->anglvel_z_fd);
if (tmp_read >= 0) {
out->gyro_z_raw = strtol(&tmp[0], NULL, 10);
out->gyro_z_in_rad_s = (double)out->gyro_z_raw *iio->anglvel_scale_z;
} else {
fprintf(stderr, "While reading anglvel(z): %d\n", tmp_read);
return tmp_read;
}
}
return 0;
}

View file

@ -1,6 +1,6 @@
#pragma once
#include "rogue_enemy.h"
#include "imu_message.h"
#define DEV_IIO_HAS_ACCEL 0x00000001U
#define DEV_IIO_HAS_ANGLVEL 0x00000002U
@ -60,4 +60,9 @@ int dev_iio_read(
struct input_event *const buf,
size_t buf_sz,
uint32_t *const buf_out
);
int dev_iio_read_imu(
const dev_iio_t *const iio,
imu_message_t *const out
);

23
imu_message.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include "rogue_enemy.h"
typedef struct imu_message {
double gyro_x_in_rad_s;
long gyro_x_raw;
double gyro_y_in_rad_s;
int16_t gyro_y_raw;
double gyro_z_in_rad_s;
int16_t gyro_z_raw;
double accel_x_in_m2s;
int16_t accel_x_raw;
double accel_y_in_m2s;
int16_t accel_y_raw;
double accel_z_in_m2s;
int16_t accel_z_raw;
} imu_message_t;

View file

@ -4,6 +4,7 @@
#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>
@ -19,8 +20,6 @@
static const char *input_path = "/dev/input/";
static const char *iio_path = "/sys/bus/iio/devices/";
static uint32_t gyroscope_mouse_translation = 0;
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;
/*
@ -74,126 +73,7 @@ uint32_t input_filter_identity(struct input_event* events, size_t* size, uint32_
}
uint32_t input_filter_asus_kb(struct input_event* events, size_t* size, uint32_t* count, uint32_t* flags) {
static int F15_status = 0;
if (events[0].type == EV_REL) {
*flags |= EV_MESSAGE_FLAGS_MOUSE;
return 0;
} else if ((events[0].type == EV_KEY) || (events[1].type == EV_KEY)) {
if ((events[0].code == BTN_MIDDLE) || (events[0].code == BTN_LEFT) || (events[0].code == BTN_RIGHT)) {
*flags |= EV_MESSAGE_FLAGS_PRESERVE_TIME | EV_MESSAGE_FLAGS_MOUSE;
} else if ((events[1].code == BTN_MIDDLE) || (events[1].code == BTN_LEFT) || (events[1].code == BTN_RIGHT)) {
*flags |= EV_MESSAGE_FLAGS_PRESERVE_TIME | EV_MESSAGE_FLAGS_MOUSE;
}
return 0;
}
if ((*count >= 2) && (events[0].type == EV_MSC) && (events[0].code == MSC_SCAN)) {
if ((events[0].value == -13565784) && (events[1].type == EV_KEY) && (events[1].code == KEY_F18)) {
if (events[1].value == 1) {
printf("Detected mode switch command, switching mode...\n");
cycle_mode();
} else {
// Do nothing effectively discarding the input
}
return INPUT_FILTER_FLAGS_DO_NOT_EMIT;
} else if (events[0].value == -13565784) {
return INPUT_FILTER_FLAGS_DO_NOT_EMIT;
} else if ((*count == 2) && (events[0].value == 458860) && (events[1].type == EV_KEY) && (events[1].code == KEY_F17)) {
if (mouse_mode()) {
if (events[1].value < 2) {
*count = 1;
events[0].type = EV_KEY;
events[0].code = BTN_GEAR_DOWN;
events[0].value = events[1].value;
return INPUT_FILTER_FLAGS_NONE;
}
} else if (gamepad_mode()) {
if (events[1].value == 0) {
--gyroscope_mouse_translation;
} else if (events[1].value == 1) {
++gyroscope_mouse_translation;
}
}
return INPUT_FILTER_FLAGS_DO_NOT_EMIT;
} else if ((*count == 2) && (events[0].value == 458861) && (events[1].type == EV_KEY) && (events[1].code == KEY_F18)) {
if (mouse_mode()) {
if (events[1].value < 2) {
*count = 1;
events[0].type = EV_KEY;
events[0].code = BTN_GEAR_UP;
events[0].value = events[1].value;
return INPUT_FILTER_FLAGS_NONE;
}
} else if (gamepad_mode()) {
if (events[1].value == 0) {
--gyroscope_mouse_translation;
} else if (events[1].value == 1) {
++gyroscope_mouse_translation;
}
}
return INPUT_FILTER_FLAGS_DO_NOT_EMIT;
} else if ((*count == 2) && (events[0].value == -13565786) && (events[1].type == EV_KEY) && (events[1].code == KEY_F16)) {
*count = 1;
events[0].type = EV_KEY;
events[0].code = BTN_MODE;
events[0].value = events[1].value;
return INPUT_FILTER_FLAGS_NONE;
} else if ((*count == 2) && (events[0].value == -13565787) && (events[1].type == EV_KEY) && (events[1].code == KEY_F15)) {
if (events[1].value == 0) {
if (F15_status > 0) {
--F15_status;
}
if (F15_status == 0) {
printf("Exiting gyro mode.\n");
}
} else if (events[1].value == 1) {
if (F15_status <= 2) {
++F15_status;
}
if (F15_status == 1) {
printf("Entering gyro mode.\n");
}
}
return INPUT_FILTER_FLAGS_DO_NOT_EMIT;
} else if ((*count == 2) && (*size >= 3) && (events[0].value == -13565896) && (events[1].type == EV_KEY) && (events[1].code == KEY_PROG1)) {
*count = 3;
int32_t val = events[1].value;
struct timeval time = events[1].time;
events[0].type = EV_KEY;
events[0].code = BTN_MODE;
events[0].value = val;
events[1].type = SYN_REPORT;
events[1].code = EV_SYN;
events[1].value = 0;
events[2].type = EV_KEY;
events[2].code = BTN_SOUTH;
events[2].value = val;
/*
events[3].type = SYN_REPORT;
events[3].code = EV_SYN;
events[3].value = 0;
events[4].type = EV_KEY;
events[4].code = BTN_SOUTH;
events[4].value = 0;
*/
return INPUT_FILTER_FLAGS_NONE;
}
}
return INPUT_FILTER_FLAGS_NONE;
}
@ -294,8 +174,7 @@ static void* iio_read_thread_func(void* ptr) {
continue;
}
/*
rc = dev_iio_read(ctx->iio_dev, msg->ev, msg->ev_size, &msg->ev_count);
rc = dev_iio_read_imu(ctx->iio_dev, &msg->data.imu);
if (rc == 0) {
// OK: good read. go on....
} else if (rc == -ENOMEM) {
@ -309,21 +188,14 @@ static void* iio_read_thread_func(void* ptr) {
// clear out flags
msg->flags = 0x00000000U;
const uint32_t input_filter_res = ctx->input_filter_fn(msg->ev, &msg->ev_size, &msg->ev_count);
if (((input_filter_res & INPUT_FILTER_FLAGS_DO_NOT_EMIT) == 0) && (msg->ev_count > 0)) {
if (queue_push(ctx->queue, (void*)msg) != 0) {
fprintf(stderr, "Error pushing iio event.\n");
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;
}
} else {
// flag the memory to be safe to reuse
msg->flags |= MESSAGE_FLAGS_HANDLE_DONE;
}
*/
// TODO: configure equal as sampling rate
usleep(100);
// either way.... fill a new buffer on the next cycle
@ -376,19 +248,39 @@ static void* input_read_thread_func(void* ptr) {
}
if ((!has_syn) || ((has_syn) && (!is_syn))) {
if ((msg->data.event.ev_count+1) == msg->data.event.ev_size) {
// TODO: perform a memove
fprintf(stderr, "MEMMOVE NEEDED\n");
} else {
#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
);
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) {
// TODO: perform a memove
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;
@ -396,9 +288,9 @@ static void* input_read_thread_func(void* ptr) {
}
if ((!has_syn) || ((has_syn) && (is_syn))) {
/*
#if defined(INCLUDE_INPUT_DEBUG)
printf("Sync ---------------------------------------\n");
*/
#endif
// clear out flags
msg->flags = 0x00000000U;
@ -421,8 +313,6 @@ static void* input_read_thread_func(void* ptr) {
// either way.... fill a new buffer on the next cycle
msg = NULL;
}
}
} while (rc == 1 || rc == 0 || rc == -EAGAIN);
@ -655,12 +545,10 @@ void *input_dev_thread_func(void *ptr) {
struct input_ctx ctx = {
.dev = NULL,
.queue = in_dev->queue,
.queue = &in_dev->logic->input_queue,
.input_filter_fn = in_dev->ev_input_filter_fn,
};
if (in_dev->dev_type == input_dev_type_uinput) {
// prepare space and empty messages
for (int h = 0; h < MAX_MESSAGES_IN_FLIGHT; ++h) {

View file

@ -2,6 +2,7 @@
#include "queue.h"
#include "message.h"
#include "logic.h"
#undef INCLUDE_INPUT_DEBUG
#undef IGNORE_INPUT_SCAN
@ -31,10 +32,10 @@ typedef struct input_dev {
volatile uint32_t crtl_flags;
queue_t *queue;
ev_input_filter_t ev_input_filter_fn;
logic_t *logic;
} input_dev_t;
void *input_dev_thread_func(void *ptr);

87
logic.c Normal file
View file

@ -0,0 +1,87 @@
#include "logic.h"
#include "platform.h"
#include "virt_ds4.h"
int logic_create(logic_t *const logic) {
logic->flags = 0x00000000U;
memset(logic->gamepad.joystick_positions, 0, sizeof(logic->gamepad.joystick_positions));
logic->gamepad.dpad = DPAD_RELEASED;
logic->gamepad.l2_trigger = 0;
logic->gamepad.r2_trigger = 0;
logic->gamepad.triangle = 0;
logic->gamepad.circle = 0;
logic->gamepad.cross = 0;
logic->gamepad.square = 0;
logic->gamepad.gyro_x = 0;
logic->gamepad.gyro_y = 0;
logic->gamepad.gyro_z = 0;
logic->gamepad.accel_x = 0;
logic->gamepad.accel_y = 0;
logic->gamepad.accel_z = 0;
const int mutex_creation_res = pthread_mutex_init(&logic->gamepad_mutex, NULL);
if (mutex_creation_res != 0) {
fprintf(stderr, "Unable to create mutex: %d\n", mutex_creation_res);
return mutex_creation_res;
}
const int queue_init_res = queue_init(&logic->input_queue, 128);
const int virt_ds4_thread_creation = pthread_create(&logic->virt_ds4_thread, NULL, virt_ds4_thread_func, (void*)(logic));
if (virt_ds4_thread_creation != 0) {
fprintf(stderr, "Error creating virtual DualShock4 thread: %d\n", virt_ds4_thread_creation);
} else {
logic->flags |= LOGIC_FLAGS_VIRT_DS4_ENABLE;
}
if (queue_init_res < 0) {
fprintf(stderr, "Unable to create queue: %d\n", queue_init_res);
return queue_init_res;
}
const int init_platform_res = init_platform(&logic->platform);
if (init_platform_res == 0) {
logic->flags |= LOGIC_FLAGS_PLATFORM_ENABLE;
} else {
fprintf(stderr, "Unable to initialize Asus RC71L MCU: %d", init_platform_res);
}
return 0;
}
int is_rc71l_ready(const logic_t *const logic) {
return logic->flags & LOGIC_FLAGS_PLATFORM_ENABLE;
}
int logic_copy_gamepad_status(logic_t *const logic, gamepad_status_t *const out) {
int res = 0;
res = pthread_mutex_lock(&logic->gamepad_mutex);
if (res != 0) {
goto logic_copy_gamepad_status_err;
}
*out = logic->gamepad;
pthread_mutex_unlock(&logic->gamepad_mutex);
logic_copy_gamepad_status_err:
return res;
}
int logic_begin_status_update(logic_t *const logic) {
int res = 0;
res = pthread_mutex_lock(&logic->gamepad_mutex);
if (res != 0) {
goto logic_begin_status_update_err;
}
logic_begin_status_update_err:
return res;
}
void logic_end_status_update(logic_t *const logic) {
pthread_mutex_unlock(&logic->gamepad_mutex);
}

71
logic.h Normal file
View file

@ -0,0 +1,71 @@
#pragma once
#include "platform.h"
#include "queue.h"
typedef enum dpad_status {
DPAD_N = 0,
DPAD_NE = 1,
DPAD_E = 2,
DPAD_SE = 3,
DPAD_S = 4,
DPAD_SW = 5,
DPAD_W = 6,
DPAD_NW = 7,
DPAD_RELEASED = 0x08,
} dpad_status_t;
typedef struct gamepad_status {
uint8_t joystick_positions[2][2]; // [0 left | 1 right][x axis | y axis]
dpad_status_t dpad;
uint8_t l2_trigger;
uint8_t r2_trigger;
uint8_t triangle;
uint8_t circle;
uint8_t cross;
uint8_t square;
int16_t gyro_x; // follows right-hand-rules
int16_t gyro_y; // follows right-hand-rules
int16_t gyro_z; // follows right-hand-rules
int16_t accel_x; // positive: right
int16_t accel_y; // positive: up
int16_t accel_z; // positive: towards player
//uint8_t
} gamepad_status_t;
#define LOGIC_FLAGS_VIRT_DS4_ENABLE 0x00000001U
#define LOGIC_FLAGS_PLATFORM_ENABLE 0x00000002U
typedef struct logic {
rc71l_platform_t platform;
pthread_mutex_t gamepad_mutex;
gamepad_status_t gamepad;
queue_t input_queue;
pthread_t virt_ds4_thread;
volatile uint32_t flags;
} logic_t;
int logic_create(logic_t *const logic);
int is_rc71l_ready(const logic_t *const logic);
int logic_copy_gamepad_status(logic_t *const logic, gamepad_status_t *const out);
int logic_begin_status_update(logic_t *const logic);
void logic_end_status_update(logic_t *const logic);

25
main.c
View file

@ -1,16 +1,17 @@
#include <signal.h>
#include <stdlib.h>
#include "input_dev.h"
#include "output_dev.h"
#include "platform.h"
#include "logic.h"
queue_t global_ev_queue;
logic_t global_logic;
static output_dev_t out_gamepadd_dev = {
.gamepad_fd = -1,
.imu_fd = -1,
.crtl_flags = 0x00000000U,
.queue = &global_ev_queue,
.logic = &global_logic,
};
static iio_filters_t in_iio_filters = {
@ -21,7 +22,7 @@ static input_dev_t in_iio_dev = {
.dev_type = input_dev_type_iio,
.crtl_flags = 0x00000000U,
.iio_filters = &in_iio_filters,
.queue = &global_ev_queue,
.logic = &global_logic,
//.input_filter_fn = input_filter_imu_identity,
};
@ -33,7 +34,7 @@ static input_dev_t in_asus_kb_1_dev = {
.dev_type = input_dev_type_uinput,
.crtl_flags = 0x00000000U,
.ev_filters = &in_asus_kb_1_filters,
.queue = &global_ev_queue,
.logic = &global_logic,
.ev_input_filter_fn = input_filter_asus_kb,
};
@ -45,7 +46,7 @@ static input_dev_t in_asus_kb_2_dev = {
.dev_type = input_dev_type_uinput,
.crtl_flags = 0x00000000U,
.ev_filters = &in_asus_kb_2_filters,
.queue = &global_ev_queue,
.logic = &global_logic,
.ev_input_filter_fn = input_filter_asus_kb,
};
@ -57,7 +58,7 @@ static input_dev_t in_asus_kb_3_dev = {
.dev_type = input_dev_type_uinput,
.crtl_flags = 0x00000000U,
.ev_filters = &in_asus_kb_3_filters,
.queue = &global_ev_queue,
.logic = &global_logic,
.ev_input_filter_fn = input_filter_asus_kb,
};
@ -69,7 +70,7 @@ static input_dev_t in_xbox_dev = {
.dev_type = input_dev_type_uinput,
.crtl_flags = 0x00000000U,
.ev_filters = &in_xbox_filters,
.queue = &global_ev_queue,
.logic = &global_logic,
.ev_input_filter_fn = input_filter_identity,
};
@ -91,9 +92,11 @@ void sig_handler(int signo)
}
int main(int argc, char ** argv) {
init_global_mode();
queue_init(&global_ev_queue, 128);
const int logic_creation_res = logic_create(&global_logic);
if (logic_creation_res < 0) {
fprintf(stderr, "Unable to create logic: %d", logic_creation_res);
return EXIT_FAILURE;
}
int imu_fd = create_output_dev("/dev/uinput", output_dev_imu);
if (imu_fd < 0) {

View file

@ -1,8 +1,11 @@
#pragma once
#include "rogue_enemy.h"
#include "imu_message.h"
#define MESSAGE_FLAGS_HANDLE_DONE 0x00000001U
#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
typedef struct ev_message {
struct input_event* ev;
@ -15,21 +18,14 @@ typedef struct ev_message {
} ev_message_t;
typedef struct imu_message {
int32_t gyro_x;
int32_t gyro_y;
int32_t gyro_z;
int32_t accel_x;
int32_t accel_y;
int32_t accel_z;
} imu_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;
@ -40,9 +36,3 @@ typedef struct message {
volatile uint32_t flags;
} message_t;
#define INPUT_FILTER_FLAGS_NONE 0x00000000U
#define INPUT_FILTER_FLAGS_DO_NOT_EMIT 0x00000001U
#define EV_MESSAGE_FLAGS_PRESERVE_TIME 0x00000002U
#define EV_MESSAGE_FLAGS_IMU 0x00000004U
#define EV_MESSAGE_FLAGS_MOUSE 0x00000008U

View file

@ -1,9 +1,13 @@
#include "output_dev.h"
#include "logic.h"
#include "queue.h"
#include "message.h"
#include <linux/input-event-codes.h>
#include <stdio.h>
#include <unistd.h>
#include "virt_ds4.h"
int create_output_dev(const char* uinput_path, output_dev_type_t type) {
int fd = open(uinput_path, O_WRONLY | O_NONBLOCK);
if(fd < 0) {
@ -452,7 +456,12 @@ create_output_dev_err:
return fd;
}
static void handle_ev(output_dev_t *const out_dev, const message_t *const msg) {
static void emit_ev(output_dev_t *const out_dev, const message_t *const msg) {
// if events are flagged as do not emit... Do NOT emit!
if (msg->flags & INPUT_FILTER_FLAGS_DO_NOT_EMIT) {
return;
}
int fd = out_dev->gamepad_fd;
if ((msg->flags & EV_MESSAGE_FLAGS_IMU) != 0) {
fd = out_dev->imu_fd;
@ -525,14 +534,157 @@ static void handle_ev(output_dev_t *const out_dev, const message_t *const msg) {
}
}
static void handle_msg(output_dev_t *const out_dev, const message_t *const msg) {
static void decode_ev(output_dev_t *const out_dev, message_t *const msg) {
static int F15_status = 0;
static int gyroscope_mouse_translation = 0;
if (msg->data.event.ev[0].type == EV_REL) {
msg->data.event.ev_flags |= EV_MESSAGE_FLAGS_MOUSE;
} else if ((msg->data.event.ev[0].type == EV_KEY) || (msg->data.event.ev[1].type == EV_KEY)) {
if ((msg->data.event.ev[0].code == BTN_MIDDLE) || (msg->data.event.ev[0].code == BTN_LEFT) || (msg->data.event.ev[0].code == BTN_RIGHT)) {
msg->data.event.ev_flags |= EV_MESSAGE_FLAGS_PRESERVE_TIME | EV_MESSAGE_FLAGS_MOUSE;
} else if ((msg->data.event.ev[1].code == BTN_MIDDLE) || (msg->data.event.ev[1].code == BTN_LEFT) || (msg->data.event.ev[1].code == BTN_RIGHT)) {
msg->data.event.ev_flags |= EV_MESSAGE_FLAGS_PRESERVE_TIME | EV_MESSAGE_FLAGS_MOUSE;
}
} else if ((msg->data.event.ev_count >= 2) && (msg->data.event.ev[0].type == EV_MSC) && (msg->data.event.ev[0].code == MSC_SCAN)) {
if ((msg->data.event.ev[0].value == -13565784) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F18)) {
if (msg->data.event.ev[1].value == 1) {
printf("Detected mode switch command, switching mode...\n");
cycle_mode(&out_dev->logic->platform);
} else {
// Do nothing effectively discarding the input
}
msg->flags |= INPUT_FILTER_FLAGS_DO_NOT_EMIT;
} else if (msg->data.event.ev[0].value == -13565784) {
msg->flags |= INPUT_FILTER_FLAGS_DO_NOT_EMIT;
} else if ((msg->data.event.ev_count == 2) && (msg->data.event.ev[0].value == 458860) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F17)) {
if (is_rc71l_ready(out_dev->logic)) {
if (is_mouse_mode(&out_dev->logic->platform)) {
if (msg->data.event.ev[1].value < 2) {
msg->data.event.ev_count = 1;
msg->data.event.ev[0].type = EV_KEY;
msg->data.event.ev[0].code = BTN_GEAR_DOWN;
msg->data.event.ev[0].value = msg->data.event.ev[1].value;
return;
}
} else if (is_gamepad_mode(&out_dev->logic->platform)) {
if (msg->data.event.ev[1].value == 0) {
--gyroscope_mouse_translation;
} else if (msg->data.event.ev[1].value == 1) {
++gyroscope_mouse_translation;
}
}
msg->flags |= INPUT_FILTER_FLAGS_DO_NOT_EMIT;
}
} else if ((msg->data.event.ev_count == 2) && (msg->data.event.ev[0].value == 458861) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F18)) {
if (is_rc71l_ready(out_dev->logic)) {
if (is_mouse_mode(&out_dev->logic->platform)) {
if (msg->data.event.ev[1].value < 2) {
msg->data.event.ev_count = 1;
msg->data.event.ev[0].type = EV_KEY;
msg->data.event.ev[0].code = BTN_GEAR_UP;
msg->data.event.ev[0].value = msg->data.event.ev[1].value;
}
} else if (is_gamepad_mode(&out_dev->logic->platform)) {
if (msg->data.event.ev[1].value == 0) {
--gyroscope_mouse_translation;
} else if (msg->data.event.ev[1].value == 1) {
++gyroscope_mouse_translation;
}
msg->flags |= INPUT_FILTER_FLAGS_DO_NOT_EMIT;
}
}
} else if ((msg->data.event.ev_count == 2) && (msg->data.event.ev[0].value == -13565786) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F16)) {
msg->data.event.ev_count = 1;
msg->data.event.ev[0].type = EV_KEY;
msg->data.event.ev[0].code = BTN_MODE;
msg->data.event.ev[0].value = msg->data.event.ev[1].value;
} else if ((msg->data.event.ev_count == 2) && (msg->data.event.ev[0].value == -13565787) && (msg->data.event.ev[1].type == EV_KEY) && (msg->data.event.ev[1].code == KEY_F15)) {
if (msg->data.event.ev[1].value == 0) {
if (F15_status > 0) {
--F15_status;
}
if (F15_status == 0) {
printf("Exiting gyro mode.\n");
}
} else if (msg->data.event.ev[1].value == 1) {
if (F15_status <= 2) {
++F15_status;
}
if (F15_status == 1) {
printf("Entering gyro mode.\n");
}
}
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)
) {
msg->data.event.ev_count = 3;
const int32_t val = msg->data.event.ev[1].value;
//struct timeval time = msg->data.event.ev[1].time;
msg->data.event.ev[0].type = EV_KEY;
msg->data.event.ev[0].code = BTN_MODE;
msg->data.event.ev[0].value = val;
msg->data.event.ev[1].type = SYN_REPORT;
msg->data.event.ev[1].code = EV_SYN;
msg->data.event.ev[1].value = 0;
msg->data.event.ev[2].type = EV_KEY;
msg->data.event.ev[2].code = BTN_SOUTH;
msg->data.event.ev[2].value = val;
/*
events[3].type = SYN_REPORT;
events[3].code = EV_SYN;
events[3].value = 0;
events[4].type = EV_KEY;
events[4].code = BTN_SOUTH;
events[4].value = 0;
*/
}
}
}
static void handle_msg(output_dev_t *const out_dev, message_t *const msg) {
if (msg->type == MSG_TYPE_EV) {
handle_ev(out_dev, msg);
decode_ev(out_dev, msg);
if ((out_dev->logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) != 0) {
emit_ev(out_dev, msg);
}
} else if (msg->type == MSG_TYPE_IMU) {
const int upd_beg_res = logic_begin_status_update(out_dev->logic);
if (upd_beg_res == 0) {
out_dev->logic->gamepad.accel_x = msg->data.imu.accel_x_raw;
out_dev->logic->gamepad.accel_y = msg->data.imu.accel_y_raw;
out_dev->logic->gamepad.accel_z = msg->data.imu.accel_z_raw;
out_dev->logic->gamepad.gyro_x = msg->data.imu.gyro_x_raw;
out_dev->logic->gamepad.gyro_y = msg->data.imu.gyro_y_raw;
out_dev->logic->gamepad.gyro_z = msg->data.imu.gyro_z_raw;
logic_end_status_update(out_dev->logic);
} else {
fprintf(stderr, "Unable to begin the gamepad status update: %d\n", upd_beg_res);
}
}
}
void *output_dev_thread_func(void *ptr) {
output_dev_t *out_dev = (output_dev_t*)ptr;
output_dev_t *const out_dev = (output_dev_t*)ptr;
struct timeval now = {0};
#if defined(INCLUDE_TIMESTAMP)
@ -543,7 +695,7 @@ void *output_dev_thread_func(void *ptr) {
for (;;) {
void *raw_ev;
const int pop_res = queue_pop_timeout(out_dev->queue, &raw_ev, 1000);
const int pop_res = queue_pop_timeout(&out_dev->logic->input_queue, &raw_ev, 1000);
if (pop_res == 0) {
message_t *const msg = (message_t*)raw_ev;
handle_msg(out_dev, msg);

View file

@ -1,6 +1,7 @@
#pragma once
#include "queue.h"
#include "logic.h"
// Emulates a "Generic" controller:
#define OUTPUT_DEV_NAME "ROGueENEMY"
@ -60,7 +61,7 @@ typedef struct output_dev {
volatile uint32_t crtl_flags;
queue_t *queue;
logic_t *logic;
} output_dev_t;
int create_output_dev(const char* uinput_path, output_dev_type_t type);

View file

@ -5,49 +5,43 @@
static const char* const platform_input_path = "/sys/devices/platform/asus-mcu.0/input/mode";
static rc71l_platform_t static_platform;
void init_global_mode() {
global_platform = NULL;
if (access(platform_input_path, F_OK) == 0) {
FILE* mode_file = fopen(platform_input_path, "r");
if (mode_file == NULL) {
fprintf(stderr, "Unable to open the MCU platform mode file %s: modes cannot be switched.\n", platform_input_path);
return;
}
char mode_str[12];
unsigned long read_bytes = fread((void*)&mode_str[0], 1, sizeof(mode_str), mode_file);
if (read_bytes < 1) {
fprintf(stderr, "Unable to read the MCU platform mode file %s: no bytes.\n", platform_input_path);
fclose(mode_file);
}
static_platform.mode = strtoul(&mode_str[0], NULL, 10);
fclose(mode_file);
global_platform = &static_platform;
printf("Asus MCU platform found: current mode %lu\n", global_platform->mode);
global_platform->modes_count = 3;
} else {
int init_platform(rc71l_platform_t *const platform) {
if (access(platform_input_path, F_OK) != 0) {
fprintf(stderr, "Unable to find the MCU platform mode file %s: modes cannot be switched.\n", platform_input_path);
return -ENOENT;
}
FILE* mode_file = fopen(platform_input_path, "r");
if (mode_file == NULL) {
fprintf(stderr, "Unable to open the MCU platform mode file %s: modes cannot be switched.\n", platform_input_path);
return -EINVAL;
}
char mode_str[12];
unsigned long read_bytes = fread((void*)&mode_str[0], 1, sizeof(mode_str), mode_file);
if (read_bytes < 1) {
fprintf(stderr, "Unable to read the MCU platform mode file %s: no bytes.\n", platform_input_path);
fclose(mode_file);
}
platform->mode = strtoul(&mode_str[0], NULL, 10);
fclose(mode_file);
printf("Asus MCU platform found: current mode %lu\n", platform->mode);
platform->modes_count = 3;
return 0;
}
int cycle_mode() {
if (global_platform == NULL) {
fprintf(stderr, "Asus MCU not registered: aborting.\n");
}
int cycle_mode(rc71l_platform_t *const platform) {
char new_mode_str[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
unsigned long new_mode = (global_platform->mode + 1) % global_platform->modes_count;
unsigned long new_mode = (platform->mode + 1) % platform->modes_count;
sprintf(new_mode_str, "%lu\n", new_mode);
FILE* mode_file = fopen(platform_input_path, "w");
@ -63,21 +57,21 @@ int cycle_mode() {
return -2;
}
global_platform->mode = new_mode;
platform->mode = new_mode;
fclose(mode_file);
return 0;
}
int gamepad_mode() {
return global_platform != NULL ? global_platform->mode == 0 : 0;
int is_gamepad_mode(rc71l_platform_t *const platform) {
return platform != NULL ? platform->mode == 0 : 0;
}
int mouse_mode() {
return global_platform != NULL ? global_platform->mode == 1 : 0;
int is_mouse_mode(rc71l_platform_t *const platform) {
return platform != NULL ? platform->mode == 1 : 0;
}
int macro_mode() {
return global_platform != NULL ? global_platform->mode == 2 : 0;
int is_macro_mode(rc71l_platform_t *const platform) {
return platform != NULL ? platform->mode == 2 : 0;
}

View file

@ -7,18 +7,12 @@ typedef struct rc71l_platform {
unsigned int modes_count;
} rc71l_platform_t;
#ifdef PLATFORM_FILE
rc71l_platform_t* global_platform = NULL;
#else
extern rc71l_platform_t* global_platform;
#endif
int init_platform(rc71l_platform_t *const platform);
void init_global_mode();
int cycle_mode(rc71l_platform_t *const platform);
int cycle_mode();
int is_mouse_mode(rc71l_platform_t *const platform);
int mouse_mode();
int is_gamepad_mode(rc71l_platform_t *const platform);
int gamepad_mode();
int macro_mode();
int is_macro_mode(rc71l_platform_t *const platform);

22
queue.c
View file

@ -4,6 +4,11 @@
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");
@ -12,7 +17,6 @@ int queue_init(queue_t* const q, size_t max_elements) {
sem_init(&q->empty, 0, q->array_size);
sem_init(&q->full, 0, 0);
sem_init(&q->mutex, 0, 1);
return 0;
}
@ -24,12 +28,12 @@ void queue_destroy(queue_t* q) {
int queue_push(queue_t* const q, void *in_item) {
sem_wait(&q->empty);
sem_wait(&q->mutex);
pthread_mutex_lock(&q->mutex);
q->rear = (q->rear + 1) % q->array_size;
q->array[q->rear] = in_item;
sem_post(&q->mutex);
pthread_mutex_unlock(&q->mutex);
sem_post(&q->full);
return 0;
@ -37,12 +41,12 @@ int queue_push(queue_t* const q, void *in_item) {
int queue_pop(queue_t* const q, void **out_item) {
sem_wait(&q->full);
sem_wait(&q->mutex);
pthread_mutex_lock(&q->mutex);
q->front = (q->front + 1) % q->array_size;
*out_item = q->array[q->front];
sem_post(&q->mutex);
pthread_mutex_unlock(&q->mutex);
sem_post(&q->empty);
return 0;
@ -60,12 +64,12 @@ int queue_push_timeout(queue_t* const q, void *in_item, int timeout_ms) {
int result = sem_timedwait(&q->empty, &timeout);
if (result == 0) {
sem_wait(&q->mutex);
pthread_mutex_lock(&q->mutex);
q->rear = (q->rear + 1) % q->array_size;
q->array[q->rear] = in_item;
sem_post(&q->mutex);
pthread_mutex_unlock(&q->mutex);
sem_post(&q->full);
}
@ -84,12 +88,12 @@ int queue_pop_timeout(queue_t* const q, void **out_item, int timeout_ms) {
int result = sem_timedwait(&q->full, &timeout);
if (result == 0) {
sem_wait(&q->mutex);
pthread_mutex_lock(&q->mutex);
q->front = (q->front + 1) % q->array_size;
*out_item = q->array[q->front];
sem_post(&q->mutex);
pthread_mutex_unlock(&q->mutex);
sem_post(&q->empty);
}

View file

@ -3,7 +3,8 @@
#include "rogue_enemy.h"
typedef struct queue {
sem_t empty, full, mutex;
sem_t empty, full;
pthread_mutex_t mutex;
ssize_t front;
ssize_t rear;

548
virt_ds4.c Normal file
View file

@ -0,0 +1,548 @@
#include "virt_ds4.h"
#include <linux/uhid.h>
#include <fcntl.h>
#include <poll.h>
static const char* path = "/dev/dualshock4";
static unsigned char rdesc[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x05, /* Usage (Gamepad), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x01, /* Report ID (1), */
0x09, 0x30, /* Usage (X), */
0x09, 0x31, /* Usage (Y), */
0x09, 0x32, /* Usage (Z), */
0x09, 0x35, /* Usage (Rz), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x39, /* Usage (Hat Switch), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x07, /* Logical Maximum (7), */
0x35, 0x00, /* Physical Minimum (0), */
0x46, 0x3B, 0x01, /* Physical Maximum (315), */
0x65, 0x14, /* Unit (Degrees), */
0x75, 0x04, /* Report Size (4), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x42, /* Input (Variable, Null State), */
0x65, 0x00, /* Unit, */
0x05, 0x09, /* Usage Page (Button), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x0E, /* Usage Maximum (0Eh), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x0E, /* Report Count (14), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x20, /* Usage (20h), */
0x75, 0x06, /* Report Size (6), */
0x95, 0x01, /* Report Count (1), */
0x15, 0x00, /* Logical Minimum (0), */
0x25, 0x3F, /* Logical Maximum (63), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x33, /* Usage (Rx), */
0x09, 0x34, /* Usage (Ry), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x21, /* Usage (21h), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x19, 0x40, /* Usage Minimum (40h), */
0x29, 0x42, /* Usage Maximum (42h), */
0x16, 0x00, 0x80, /* Logical Minimum (-32768), */
0x26, 0x00, 0x7F, /* Logical Maximum (32767), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x19, 0x43, /* Usage Minimum (43h), */
0x29, 0x45, /* Usage Maximum (45h), */
0x16, 0x00, 0xE0, /* Logical Minimum (-8192), */
0x26, 0xFF, 0x1F, /* Logical Maximum (8191), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
0x09, 0x21, /* Usage (21h), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x27, /* Report Count (39), */
0x81, 0x02, /* Input (Variable), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x22, /* Usage (22h), */
0x95, 0x1F, /* Report Count (31), */
0x91, 0x02, /* Output (Variable), */
0x85, 0x04, /* Report ID (4), */
0x09, 0x23, /* Usage (23h), */
0x95, 0x24, /* Report Count (36), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x02, /* Report ID (2), */
0x09, 0x24, /* Usage (24h), */
0x95, 0x24, /* Report Count (36), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x08, /* Report ID (8), */
0x09, 0x25, /* Usage (25h), */
0x95, 0x03, /* Report Count (3), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x10, /* Report ID (16), */
0x09, 0x26, /* Usage (26h), */
0x95, 0x04, /* Report Count (4), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x11, /* Report ID (17), */
0x09, 0x27, /* Usage (27h), */
0x95, 0x02, /* Report Count (2), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x12, /* Report ID (18), */
0x06, 0x02, 0xFF, /* Usage Page (FF02h), */
0x09, 0x21, /* Usage (21h), */
0x95, 0x0F, /* Report Count (15), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x13, /* Report ID (19), */
0x09, 0x22, /* Usage (22h), */
0x95, 0x16, /* Report Count (22), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x14, /* Report ID (20), */
0x06, 0x05, 0xFF, /* Usage Page (FF05h), */
0x09, 0x20, /* Usage (20h), */
0x95, 0x10, /* Report Count (16), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x15, /* Report ID (21), */
0x09, 0x21, /* Usage (21h), */
0x95, 0x2C, /* Report Count (44), */
0xB1, 0x02, /* Feature (Variable), */
0x06, 0x80, 0xFF, /* Usage Page (FF80h), */
0x85, 0x80, /* Report ID (128), */
0x09, 0x20, /* Usage (20h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x81, /* Report ID (129), */
0x09, 0x21, /* Usage (21h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x82, /* Report ID (130), */
0x09, 0x22, /* Usage (22h), */
0x95, 0x05, /* Report Count (5), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x83, /* Report ID (131), */
0x09, 0x23, /* Usage (23h), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x84, /* Report ID (132), */
0x09, 0x24, /* Usage (24h), */
0x95, 0x04, /* Report Count (4), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x85, /* Report ID (133), */
0x09, 0x25, /* Usage (25h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x86, /* Report ID (134), */
0x09, 0x26, /* Usage (26h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x87, /* Report ID (135), */
0x09, 0x27, /* Usage (27h), */
0x95, 0x23, /* Report Count (35), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x88, /* Report ID (136), */
0x09, 0x28, /* Usage (28h), */
0x95, 0x22, /* Report Count (34), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x89, /* Report ID (137), */
0x09, 0x29, /* Usage (29h), */
0x95, 0x02, /* Report Count (2), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x90, /* Report ID (144), */
0x09, 0x30, /* Usage (30h), */
0x95, 0x05, /* Report Count (5), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x91, /* Report ID (145), */
0x09, 0x31, /* Usage (31h), */
0x95, 0x03, /* Report Count (3), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x92, /* Report ID (146), */
0x09, 0x32, /* Usage (32h), */
0x95, 0x03, /* Report Count (3), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0x93, /* Report ID (147), */
0x09, 0x33, /* Usage (33h), */
0x95, 0x0C, /* Report Count (12), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA0, /* Report ID (160), */
0x09, 0x40, /* Usage (40h), */
0x95, 0x06, /* Report Count (6), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA1, /* Report ID (161), */
0x09, 0x41, /* Usage (41h), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA2, /* Report ID (162), */
0x09, 0x42, /* Usage (42h), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA3, /* Report ID (163), */
0x09, 0x43, /* Usage (43h), */
0x95, 0x30, /* Report Count (48), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA4, /* Report ID (164), */
0x09, 0x44, /* Usage (44h), */
0x95, 0x0D, /* Report Count (13), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA5, /* Report ID (165), */
0x09, 0x45, /* Usage (45h), */
0x95, 0x15, /* Report Count (21), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA6, /* Report ID (166), */
0x09, 0x46, /* Usage (46h), */
0x95, 0x15, /* Report Count (21), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xF0, /* Report ID (240), */
0x09, 0x47, /* Usage (47h), */
0x95, 0x3F, /* Report Count (63), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xF1, /* Report ID (241), */
0x09, 0x48, /* Usage (48h), */
0x95, 0x3F, /* Report Count (63), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xF2, /* Report ID (242), */
0x09, 0x49, /* Usage (49h), */
0x95, 0x0F, /* Report Count (15), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA7, /* Report ID (167), */
0x09, 0x4A, /* Usage (4Ah), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA8, /* Report ID (168), */
0x09, 0x4B, /* Usage (4Bh), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xA9, /* Report ID (169), */
0x09, 0x4C, /* Usage (4Ch), */
0x95, 0x08, /* Report Count (8), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAA, /* Report ID (170), */
0x09, 0x4E, /* Usage (4Eh), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAB, /* Report ID (171), */
0x09, 0x4F, /* Usage (4Fh), */
0x95, 0x39, /* Report Count (57), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAC, /* Report ID (172), */
0x09, 0x50, /* Usage (50h), */
0x95, 0x39, /* Report Count (57), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAD, /* Report ID (173), */
0x09, 0x51, /* Usage (51h), */
0x95, 0x0B, /* Report Count (11), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAE, /* Report ID (174), */
0x09, 0x52, /* Usage (52h), */
0x95, 0x01, /* Report Count (1), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xAF, /* Report ID (175), */
0x09, 0x53, /* Usage (53h), */
0x95, 0x02, /* Report Count (2), */
0xB1, 0x02, /* Feature (Variable), */
0x85, 0xB0, /* Report ID (176), */
0x09, 0x54, /* Usage (54h), */
0x95, 0x3F, /* Report Count (63), */
0xB1, 0x02, /* Feature (Variable), */
0xC0 /* End Collection */
};
static int uhid_write(int fd, const struct uhid_event *ev)
{
ssize_t ret;
ret = write(fd, ev, sizeof(*ev));
if (ret < 0) {
fprintf(stderr, "Cannot write to uhid: %d\n", (int)ret);
return -errno;
} else if (ret != sizeof(*ev)) {
fprintf(stderr, "Wrong size written to uhid: %zd != %zu\n",
ret, sizeof(ev));
return -EFAULT;
} else {
return 0;
}
}
static int create(int fd)
{
struct uhid_event ev;
memset(&ev, 0, sizeof(ev));
ev.type = UHID_CREATE;
strcpy((char*)ev.u.create.name, "Sony Corp. DualShock 4 [CUH-ZCT2x]");
ev.u.create.rd_data = rdesc;
ev.u.create.rd_size = sizeof(rdesc);
ev.u.create.bus = BUS_USB;
ev.u.create.vendor = 0x054C;
ev.u.create.product = 0x09CC;
ev.u.create.version = 0;
ev.u.create.country = 0;
return uhid_write(fd, &ev);
}
static void destroy(int fd)
{
struct uhid_event ev;
memset(&ev, 0, sizeof(ev));
ev.type = UHID_DESTROY;
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)
{
/* LED messages are adverised via OUTPUT reports; ignore the rest */
if (ev->u.output.rtype != UHID_OUTPUT_REPORT) {
return;
}
/* LED reports have length 2 bytes */
if (ev->u.output.size != 2)
return;
/* first byte is report-id which is 0x02 for LEDs in our rdesc */
if (ev->u.output.data[0] != 0x2)
return;
/* print flags payload */
fprintf(stderr, "LED output report received with flags %x\n",
ev->u.output.data[1]);
}
static int event(int fd)
{
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 < 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:
fprintf(stderr, "UHID_START from uhid-dev\n");
break;
case UHID_STOP:
fprintf(stderr, "UHID_STOP from uhid-dev\n");
break;
case UHID_OPEN:
fprintf(stderr, "UHID_OPEN from uhid-dev\n");
break;
case UHID_CLOSE:
fprintf(stderr, "UHID_CLOSE from uhid-dev\n");
break;
case UHID_OUTPUT:
fprintf(stderr, "UHID_OUTPUT from uhid-dev\n");
handle_output(&ev);
break;
case UHID_OUTPUT_EV:
fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev\n");
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
const 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, 0xf9, 0xff, 0x09, 0x00, 0xf9, 0xff, 0xfe,
0x22, 0xf4, 0xdc, 0xbb, 0x22, 0x59, 0xdd, 0x89,
0x22, 0x68, 0xdd, 0x1c, 0x02, 0x1c, 0x02, 0xd3,
0x20, 0x07, 0xdf, 0xbf, 0x20, 0xaa, 0xe0, 0xbc,
0x1e, 0x86, 0xe0, 0x06, 0x00,
}
}
}
};
uhid_write(fd, &firmware_info_response);
}
break;
default:
fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type);
}
return 0;
}
static uint8_t get_buttons_byte_by_gs(const gamepad_status_t *const gs) {
uint8_t res = 0;
res |= gs->triangle ? (uint8_t)0b10000000 : (uint8_t)0b000000000;
res |= gs->circle ? (uint8_t)0b01000000 : (uint8_t)0b000000000;
res |= gs->cross ? (uint8_t)0b00100000 : (uint8_t)0b000000000;
res |= gs->square ? (uint8_t)0b00010000 : (uint8_t)0b000000000;
return res;
}
static int send_data(int fd, logic_t *const logic, uint8_t counter) {
gamepad_status_t gs = {};
const int gs_copy_res = logic_copy_gamepad_status(logic, &gs);
if (gs_copy_res != 0) {
fprintf(stderr, "Unable to copy the gamepad status: %d", gs_copy_res);
return gs_copy_res;
}
/*
Example data:
0000 c0 e3 af 02 ac 9c ff ff 43 01 84 08 05 00 2d 00
0010 93 7b 56 65 00 00 00 00 c2 aa 03 00 00 00 00 00
0020 40 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00
0030 04 00 00 00 00 00 00 00 04 02 00 00 00 00 00 00
// above is useless, just send what is below.
0040 01 80 7f 80 7e 08 00 5c 00 00 7e d4 06 3b fe 0c
0050 ff 8c fe 6a 05 4f 15 56 e8 00 00 00 00 00 1b 00
0060 00 00 00 80 00 00 00 80 00 00 00 00 80 00 00 00
0070 80 00 00 00 00 80 00 00 00 80 00 00 00 00 80 00
*/
// see https://www.psdevwiki.com/ps4/DS4-USB
// [12] battery level
uint8_t buf[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
buf[0] = 0x01; // [00] report ID (0x01)
buf[1] = gs.joystick_positions[0][0]; // L stick, X axis
buf[2] = gs.joystick_positions[0][1]; // L stick, Y axis
buf[3] = gs.joystick_positions[1][0]; // R stick, X axis
buf[4] = gs.joystick_positions[1][1]; // R stick, Y axis
buf[5] = get_buttons_byte_by_gs(&gs) | (uint8_t)(gs.dpad);
//buf[06] = get_buttons_byte_by_gs(&gs) | (uint8_t)(gs.dpad);
buf[7] = (counter % (uint8_t)64) << ((uint8_t)2);
buf[8] = gs.l2_trigger;
buf[9] = gs.r2_trigger;
buf[10] = 0;
buf[11] = 0;
buf[12] = 0x20; // [12] battery level
buf[13] = gs.gyro_x;
buf[14] = gs.gyro_y;
buf[15] = gs.gyro_z;
buf[16] = gs.accel_x;
buf[17] = gs.accel_y;
buf[18] = gs.accel_z;
buf[30] = 0x1b; // no headset attached
return 0;
}
void *virt_ds4_thread_func(void *ptr) {
logic_t *const logic = (logic_t*)ptr;
fprintf(stderr, "Open uhid-cdev %s\n", path);
int fd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
if (fd < 0) {
fprintf(stderr, "Cannot open uhid-cdev %s: %d\n", path, fd);
return NULL;
}
fprintf(stderr, "Create uhid device\n");
int ret = create(fd);
if (ret) {
close(fd);
return NULL;
}
uint8_t counter = 0;
for (;;) {
if ((logic->flags & LOGIC_FLAGS_VIRT_DS4_ENABLE) != 0) {
event(fd);
const int res = send_data(fd, logic, counter);
if (res >= 0) {
++counter;
}
}
}
destroy(fd);
return NULL;
}

5
virt_ds4.h Normal file
View file

@ -0,0 +1,5 @@
#pragma once
#include "logic.h"
void *virt_ds4_thread_func(void *ptr);