From 25583f245164e5f806e48deaa03e9334fb657ebd Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm Date: Sun, 26 Oct 2025 16:49:48 +0100 Subject: [PATCH] Inital commit --- CMakeLists.txt | 11 +++ vivevolume.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 vivevolume.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1425095 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.20) + +project(vivevolume C) + +set(CMAKE_C_STANDARD 17) +set(SRC_FILES vivevolume.c) +set(LIBS "") + +add_executable(${PROJECT_NAME} ${SRC_FILES}) +target_link_libraries(${PROJECT_NAME} ${LIBS}) +install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin PERMISSIONS WORLD_EXECUTE WORLD_READ SETUID) diff --git a/vivevolume.c b/vivevolume.c new file mode 100644 index 0000000..22df8f9 --- /dev/null +++ b/vivevolume.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_DEVICE_NAME_LEN 256 +#define MAX_REPORT_LEN 128 +#define VIVE_PRO_VID 0x0bb4 +#define VIVE_PRO_PID 0x0309 + +typedef enum +{ + VIVE_PRO_BTN_NONE = 0, + VIVE_PRO_BTN_VOL_UP = 0b1, + VIVE_PRO_BTN_VOL_DOWN = 0b10, + VIVE_PRO_BTN_MIC_MUTE = 0b100, +} vive_pro_btn_t; + +static int open_vive_pro_hidraw(const char *device) +{ + int fd = open(device, O_RDWR); + if (fd < 0) { + perror("Unable to open device"); + return -1; + } + + char device_name[MAX_DEVICE_NAME_LEN]; + int res = ioctl(fd, HIDIOCGRAWNAME(MAX_DEVICE_NAME_LEN), device_name); + if (res < 0) + perror("Unable to read device name"); + else + printf("Connected to device: %s\n", device_name); + + struct hidraw_devinfo info; + res = ioctl(fd, HIDIOCGRAWINFO, &info); + if(res < 0) + { + perror("Unable to get device info"); + return -2; + } + else if (info.vendor != VIVE_PRO_VID || info.product != VIVE_PRO_PID) + { + fprintf(stderr, "%s is not a vive pro\n", device); + return -3; + } + return fd; +} + +static int register_uinput_device(const char* uinputdev, const char* devname, uint16_t vid, uint16_t pid) +{ + int fd = open(uinputdev, O_RDWR); + if(fd < 0) + return fd; + + struct uinput_setup dev = {}; + size_t len = strlen(devname); + if(len > UINPUT_MAX_NAME_SIZE) + len = UINPUT_MAX_NAME_SIZE; + strncpy(dev.name, devname, len); + + dev.id.bustype = BUS_VIRTUAL; + dev.id.vendor = vid; + dev.id.product = pid; + + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_KEYBIT, KEY_VOLUMEUP); + ioctl(fd, UI_SET_KEYBIT, KEY_VOLUMEDOWN); + ioctl(fd, UI_SET_KEYBIT, KEY_MICMUTE); + + int ret = ioctl(fd, UI_DEV_SETUP, &dev); + if(ret < 0) + { + close(fd); + return ret; + } + ret = ioctl(fd, UI_DEV_CREATE); + if(ret < 0) + { + close(fd); + return ret; + } + + return fd; +} + +struct vive_key_state +{ + bool volup; + bool voldown; + bool micmute; +}; + +static int send_keys(struct vive_key_state* state, int uinput_fd, uint8_t keys) +{ + struct input_event ev = {}; + bool syn = false; + + bool volup = keys & VIVE_PRO_BTN_VOL_UP; + if(volup != state->volup) + { + ev.type = EV_KEY; + ev.code = KEY_VOLUMEUP; + ev.value = volup; + state->volup = volup; + if(write(uinput_fd, &ev, sizeof(ev)) != sizeof(ev)) + return -1; + syn = true; + } + + bool voldown = keys & VIVE_PRO_BTN_VOL_DOWN; + if(voldown != state->voldown) + { + ev.type = EV_KEY; + ev.code = KEY_VOLUMEDOWN; + ev.value = voldown; + state->voldown = voldown; + if(write(uinput_fd, &ev, sizeof(ev)) != sizeof(ev)) + return -1; + syn = true; + } + + bool micmute = keys & VIVE_PRO_BTN_MIC_MUTE; + if(micmute != state->micmute) + { + ev.type = EV_KEY; + ev.code = KEY_MICMUTE; + ev.value = micmute; + state->micmute = micmute; + if(write(uinput_fd, &ev, sizeof(ev)) != sizeof(ev)) + return -1; + syn = true; + } + + if(syn) + { + gettimeofday(&ev.time, NULL); + ev.type = EV_SYN; + ev.code = SYN_REPORT; + ev.value = 0; + if(write(uinput_fd, &ev, sizeof(ev)) != sizeof(ev)) + return -2; + } + return 0; +} + +int main(int argc, char **argv) +{ + int res; + + char *device; + if(argc > 1) + { + device = argv[1]; + } + else + { + if(argc > 0) + fprintf(stderr, "Usage: %s [VIVE PRO HIDRAW DEVICE]\n", argv[0]); + return 1; + } + + int hidraw_fd = open_vive_pro_hidraw(device); + if(hidraw_fd < 0) + return abs(hidraw_fd); + + int uinput_fd = register_uinput_device("/dev/uinput", "VIVE PRO", VIVE_PRO_VID, VIVE_PRO_PID); + if(uinput_fd < 0) + { + perror("Unable to register uninput device\n"); + return abs(uinput_fd); + } + + uint8_t buffer[MAX_REPORT_LEN]; + struct vive_key_state state = {}; + while((res = read(hidraw_fd, buffer, MAX_REPORT_LEN)) >= 0) + { + send_keys(&state, uinput_fd, buffer[9]); + } + + close(hidraw_fd); + close(uinput_fd); + return 0; +}