Inital commit

This commit is contained in:
Carl Philipp Klemm 2025-10-26 16:49:48 +01:00
commit 25583f2451
2 changed files with 204 additions and 0 deletions

11
CMakeLists.txt Normal file
View file

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

193
vivevolume.c Normal file
View file

@ -0,0 +1,193 @@
#include <linux/types.h>
#include <linux/input.h>
#include <linux/hidraw.h>
#include <linux/uinput.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#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;
}