#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; }