193 lines
3.8 KiB
C
193 lines
3.8 KiB
C
#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;
|
|
}
|