Commit e063e824 authored by Lubosz Sarnecki's avatar Lubosz Sarnecki
Browse files

d/vive: Add native driver for the Vive.

This adds a driver for the Vive family of devices based on Philipp
Zabel's ouvrt.
The driver currently only handles the IMU and main board report streams,
but is able to acquire the JSON configuration and utilize it for IMU
calibration as well as distortion configuration.
parent 8e6cc11d
......@@ -31,6 +31,7 @@ find_package(Libusb1)
find_package(JPEG)
find_package(SDL2)
find_package(Threads)
find_package(ZLIB)
# @TODO Turn into a find_package LIBUVC file.
pkg_check_modules(LIBUVC libuvc)
......@@ -134,6 +135,10 @@ if(BUILD_WITH_HIDAPI)
set(BUILD_DRIVER_PSVR TRUE)
endif()
if(${ZLIB_FOUND})
set(BUILD_DRIVER_VIVE TRUE)
endif()
if(TRUE)
# Uses the Monado internal hid wrapper.
set(BUILD_DRIVER_HDK TRUE)
......
......@@ -43,6 +43,7 @@ sdl2 = dependency('sdl2', required: get_option('gui'))
udev = dependency('libudev', required: false)
libuvc = dependency('libuvc', required: false)
vulkan = dependency('vulkan')
zlib = dependency('zlib', required: false)
opencv = dependency('opencv4', required: false)
if not opencv.found()
......@@ -118,6 +119,12 @@ if hidapi.found() and ('auto' in drivers or 'psvr' in drivers or 'hdk' in driver
endif
endif
if zlib.found() and ('auto' in drivers or 'vive' in drivers)
if 'vive' not in drivers
drivers += ['vive']
endif
endif
if v4l2.found() and ('auto' in drivers or 'v4l2' in drivers)
if 'v4l2' not in drivers
drivers += ['v4l2']
......
......@@ -3,7 +3,7 @@
option('drivers',
type: 'array',
choices: ['auto', 'hdk', 'hydra', 'ohmd', 'psmv', 'psvr', 'v4l2'],
choices: ['auto', 'hdk', 'hydra', 'ohmd', 'psmv', 'psvr', 'v4l2', 'vive'],
value: ['auto'],
description: 'Set of drivers to build')
......
......@@ -30,6 +30,9 @@ if(BUILD_DRIVER_V4L2)
set(XRT_BUILD_DRIVER_V4L2 TRUE)
endif()
if(BUILD_DRIVER_VIVE)
set(XRT_BUILD_DRIVER_VIVE TRUE)
endif()
configure_file(targets_enabled_drivers.h.cmake_in ${CMAKE_CURRENT_BINARY_DIR}/targets_enabled_drivers.h)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
......
......@@ -9,6 +9,7 @@ conf_data.set('XRT_BUILD_DRIVER_OHMD', 'ohmd' in drivers)
conf_data.set('XRT_BUILD_DRIVER_PSMV', 'psmv' in drivers)
conf_data.set('XRT_BUILD_DRIVER_PSVR', 'psvr' in drivers)
conf_data.set('XRT_BUILD_DRIVER_V4L2', 'v4l2' in drivers)
conf_data.set('XRT_BUILD_DRIVER_VIVE', 'vive' in drivers)
targets_enabled_h = configure_file(
input: 'targets_enabled_drivers.h.meson_in',
......
......@@ -20,3 +20,5 @@
#cmakedefine XRT_BUILD_DRIVER_PSVR
#cmakedefine XRT_BUILD_DRIVER_V4L2
#cmakedefine XRT_BUILD_DRIVER_VIVE
......@@ -20,3 +20,5 @@
#mesondefine XRT_BUILD_DRIVER_PSVR
#mesondefine XRT_BUILD_DRIVER_V4L2
#mesondefine XRT_BUILD_DRIVER_VIVE
......@@ -83,6 +83,23 @@ if(BUILD_DRIVER_PSVR)
)
endif()
if(BUILD_DRIVER_VIVE)
set(VIVE_SOURCE_FILES
vive/vive_device.h
vive/vive_device.c
vive/vive_prober.h
vive/vive_prober.c
vive/vive_protocol.h
../../external/nxjson/nxjson.h
../../external/nxjson/nxjson.c
)
# Use OBJECT to not create a archive, since it just gets in the way.
add_library(drv_vive OBJECT ${VIVE_SOURCE_FILES})
target_include_directories(drv_vive SYSTEM PRIVATE ../../external)
target_link_libraries(drv_vive z)
set_property(TARGET drv_vive PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()
if(BUILD_DRIVER_V4L2)
set(V4L2_SOURCE_FILES
......
......@@ -75,3 +75,20 @@ lib_drv_v4l2 = static_library(
dependencies: [aux, v4l2],
build_by_default: 'v4l2' in drivers,
)
lib_drv_vive = static_library(
'drv_vive',
files(
'vive/vive_device.c',
'vive/vive_device.h',
'vive/vive_protocol.h',
'vive/vive_prober.h',
'vive/vive_prober.c',
'../../external/nxjson/nxjson.h',
'../../external/nxjson/nxjson.c'
),
include_directories: [xrt_include,
include_directories('../../external/')],
dependencies: [aux, zlib],
build_by_default: 'vive' in drivers,
)
This diff is collapsed.
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief vive device header
* @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
* @ingroup drv_vive
*/
#pragma once
#include "xrt/xrt_device.h"
#include "os/os_threading.h"
#ifdef __cplusplus
extern "C" {
#endif
enum VIVE_VARIANT
{
VIVE_UNKNOWN = 0,
VIVE_VARIANT_VIVE,
VIVE_VARIANT_PRO,
VIVE_VARIANT_INDEX
};
struct vive_device
{
struct xrt_device base;
struct os_hid_device *mainboard_dev;
struct os_hid_device *sensors_dev;
enum VIVE_VARIANT variant;
struct os_thread_helper sensors_thread;
struct os_thread_helper mainboard_thread;
struct
{
uint64_t time;
uint8_t sequence;
double acc_range;
double gyro_range;
struct xrt_vec3 acc_bias;
struct xrt_vec3 acc_scale;
struct xrt_vec3 gyro_bias;
struct xrt_vec3 gyro_scale;
} imu;
struct
{
double lens_separation;
double persistence;
uint16_t eye_target_height_in_pixels;
uint16_t eye_target_width_in_pixels;
} display;
struct
{
uint16_t ipd;
uint16_t lens_separation;
uint16_t proximity;
uint8_t button;
} board;
struct
{
uint32_t display_firmware_version;
uint32_t firmware_version;
uint8_t hardware_revision;
char *mb_serial_number;
char *model_number;
char *device_serial_number;
} firmware;
struct xrt_quat rot_filtered;
bool print_spew;
bool print_debug;
bool disconnect_notified;
};
struct vive_device *
vive_device_create(struct os_hid_device *mainboard_dev,
struct os_hid_device *sensors_dev,
enum VIVE_VARIANT variant);
/*
*
* Printing functions.
*
*/
#define VIVE_SPEW(p, ...) \
do { \
if (p->print_spew) { \
fprintf(stderr, "%s - ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} \
} while (false)
#define VIVE_DEBUG(p, ...) \
do { \
if (p->print_debug) { \
fprintf(stderr, "%s - ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} \
} while (false)
#define VIVE_ERROR(...) \
do { \
fprintf(stderr, "%s - ", __func__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n"); \
} while (false)
#ifdef __cplusplus
}
#endif
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief drv_vive prober code.
* @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
* @ingroup drv_vive
*/
#include <stdio.h>
#include "util/u_debug.h"
#include "vive_device.h"
#include "vive_prober.h"
static const char VIVE_PRODUCT_STRING[] = "HTC Vive";
static const char VIVE_MANUFACTURER_STRING[] = "HTC";
DEBUG_GET_ONCE_BOOL_OPTION(vive_debug, "VIVE_PRINT_DEBUG", false)
static int
_print_prober_string(struct xrt_prober *xp,
struct xrt_prober_device *dev,
enum xrt_prober_string type)
{
unsigned char s[256] = {0};
int len = xrt_prober_get_string_descriptor(xp, dev, type, s, sizeof(s));
if (len > 0)
printf("%s: %s\n", xrt_prober_string_to_string(type), s);
return len;
}
static void
_print_device_info(struct xrt_prober *xp, struct xrt_prober_device *dev)
{
printf("========================\n");
printf("vive: Probing Device\n");
printf("vive: Vendor %04x\n", dev->vendor_id);
printf("vive: Product %04x\n", dev->product_id);
printf("vive: Class %d\n", dev->usb_dev_class);
printf("vive: %s\n", xrt_bus_type_to_string(dev->bus));
_print_prober_string(xp, dev, XRT_PROBER_STRING_MANUFACTURER);
_print_prober_string(xp, dev, XRT_PROBER_STRING_PRODUCT);
_print_prober_string(xp, dev, XRT_PROBER_STRING_SERIAL_NUMBER);
printf("========================\n");
}
static int
init_vive1(struct xrt_prober *xp,
struct xrt_prober_device *dev,
struct xrt_prober_device **devices,
size_t num_devices,
bool print_debug,
struct xrt_device **out_xdev)
{
if (print_debug)
_print_device_info(xp, dev);
if (!xrt_prober_match_string(xp, dev, XRT_PROBER_STRING_MANUFACTURER,
VIVE_MANUFACTURER_STRING) ||
!xrt_prober_match_string(xp, dev, XRT_PROBER_STRING_PRODUCT,
VIVE_PRODUCT_STRING)) {
return -1;
}
struct os_hid_device *sensors_dev = NULL;
for (uint32_t i = 0; i < num_devices; i++) {
struct xrt_prober_device *d = devices[i];
if (d->vendor_id != VALVE_VID &&
d->product_id != VIVE_LIGHTHOUSE_FPGA_RX)
continue;
if (print_debug)
_print_device_info(xp, d);
int result =
xrt_prober_open_hid_interface(xp, d, 0, &sensors_dev);
if (result != 0) {
VIVE_ERROR("Could not open Vive sensors device.");
return -1;
}
break;
}
if (sensors_dev == NULL) {
VIVE_ERROR("Could not find Vive sensors device.");
return -1;
}
struct os_hid_device *mainboard_dev = NULL;
int result = xrt_prober_open_hid_interface(xp, dev, 0, &mainboard_dev);
if (result != 0) {
VIVE_ERROR("Could not open Vive mainboard device.");
free(sensors_dev);
return -1;
}
struct vive_device *d =
vive_device_create(mainboard_dev, sensors_dev, VIVE_VARIANT_VIVE);
if (d == NULL) {
free(sensors_dev);
free(mainboard_dev);
return -1;
}
*out_xdev = &d->base;
return 1;
}
int
vive_found(struct xrt_prober *xp,
struct xrt_prober_device **devices,
size_t num_devices,
size_t index,
struct xrt_device **out_xdev)
{
struct xrt_prober_device *dev = devices[index];
bool print_debug = debug_get_bool_option_vive_debug();
if (print_debug)
_print_device_info(xp, dev);
if (!xrt_prober_can_open(xp, dev)) {
VIVE_ERROR("Could not open Vive device.");
return -1;
}
switch (dev->product_id) {
case VIVE_PID:
return init_vive1(xp, dev, devices, num_devices, print_debug,
out_xdev);
default:
VIVE_ERROR("No product ids matched %.4x\n", dev->product_id);
return -1;
}
return -1;
}
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Interface to @ref drv_vive.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
* @ingroup drv_vive
*/
#pragma once
#include <stdlib.h>
#include "xrt/xrt_prober.h"
#ifdef __cplusplus
extern "C" {
#endif
/*!
* @defgroup drv_vive Vive and Index Driver
* @ingroup drv
*
* @brief Driver for the HTC Vive and Valve Index family of HMDs.
*/
#define HTC_VID 0x0bb4
#define VALVE_VID 0x28de
#define VIVE_PID 0x2c87
#define VIVE_LIGHTHOUSE_FPGA_RX 0x2000
#define VIVE_PRO_MAINBOARD_PID 0x0309
#define VIVE_PRO_LHR_PID 0x2300
/*!
* Probing function for Vive devices.
*
* @ingroup drv_vive
*/
int
vive_found(struct xrt_prober *xp,
struct xrt_prober_device **devices,
size_t num_devices,
size_t index,
struct xrt_device **out_xdev);
/*!
* @dir drivers/vive
*
* @brief @ref drv_vive files.
*/
#ifdef __cplusplus
}
#endif
// Copyright 2016-2019, Philipp Zabel
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Vive USB HID reports
* @author Lubosz Sarnecki <lubosz.sarnecki@collabora.com>
* @ingroup drv_vive
*/
#pragma once
#include <asm/byteorder.h>
#include <stdint.h>
#define VIVE_CONTROLLER_BUTTON_REPORT_ID 0x01
#define VIVE_CONTROLLER_USB_BUTTON_TRIGGER (1 << 0)
#define VIVE_CONTROLLER_USB_BUTTON_GRIP (1 << 2)
#define VIVE_CONTROLLER_USB_BUTTON_MENU (1 << 12)
#define VIVE_CONTROLLER_USB_BUTTON_SYSTEM (1 << 13)
#define VIVE_CONTROLLER_USB_BUTTON_THUMB (1 << 18)
#define VIVE_CONTROLLER_USB_BUTTON_TOUCH (1 << 20)
struct vive_controller_button_report
{
uint8_t id;
uint8_t unknown1;
uint16_t maybe_type;
uint32_t sequence;
uint32_t buttons;
union {
uint16_t trigger;
uint16_t battery_voltage;
};
uint8_t battery;
uint8_t unknown2;
uint32_t hardware_id;
uint16_t touch[2];
uint16_t unknown3;
uint16_t trigger_hires;
uint8_t unknown4[24];
uint16_t trigger_raw;
uint8_t unknown5[8];
uint8_t maybe_bitfield;
uint8_t unknown6;
} __attribute__((packed));
#define VIVE_IMU_RANGE_MODES_REPORT_ID 0x01
struct vive_imu_range_modes_report
{
uint8_t id;
uint8_t gyro_range;
uint8_t accel_range;
uint8_t unknown[61];
} __attribute__((packed));
#define VIVE_MAINBOARD_STATUS_REPORT_ID 0x03
struct vive_mainboard_status_report
{
uint8_t id;
uint16_t unknown;
uint8_t len;
uint16_t lens_separation;
uint16_t reserved1;
uint8_t button;
uint8_t reserved2[3];
uint8_t proximity_change;
uint8_t reserved3;
uint16_t proximity;
uint16_t ipd;
uint8_t reserved4[46];
} __attribute__((packed));
#define VIVE_HEADSET_POWER_REPORT_ID 0x04
#define VIVE_HEADSET_POWER_REPORT_TYPE 0x2978
struct vive_headset_power_report
{
uint8_t id;
uint16_t type;
uint8_t len;
uint8_t unknown1[9];
uint8_t reserved1[32];
uint8_t unknown2;
uint8_t reserved2[18];
} __attribute__((packed));
#define VIVE_HEADSET_MAINBOARD_DEVICE_INFO_REPORT_ID 0x04
#define VIVE_HEADSET_MAINBOARD_DEVICE_INFO_REPORT_TYPE 0x2987
struct vive_headset_mainboard_device_info_report
{
uint8_t id;
uint16_t type;
uint8_t len;
uint16_t edid_vid;
uint16_t edid_pid;
uint8_t unknown1[4];
uint32_t display_firmware_version;
uint8_t unknown2[48];
} __attribute__((packed));
#define VIVE_FIRMWARE_VERSION_REPORT_ID 0x05
struct vive_firmware_version_report
{
uint8_t id;
uint32_t firmware_version;
uint32_t unknown1;
uint8_t string1[16];
uint8_t string2[16];
uint8_t hardware_version_micro;
uint8_t hardware_version_minor;
uint8_t hardware_version_major;
uint8_t hardware_revision;
uint32_t unknown2;
uint8_t fpga_version_minor;
uint8_t fpga_version_major;
uint8_t reserved[13];
} __attribute__((packed));
#define VIVE_CONFIG_START_REPORT_ID 0x10
struct vive_config_start_report
{
uint8_t id;
uint8_t unused[63];
} __attribute__((packed));
#define VIVE_CONFIG_READ_REPORT_ID 0x11
struct vive_config_read_report
{
uint8_t id;
uint8_t len;
uint8_t payload[62];
} __attribute__((packed));
#define VIVE_IMU_REPORT_ID 0x20
struct vive_imu_sample
{
uint16_t acc[3];
uint16_t gyro[3];
uint32_t time;
uint8_t seq;
} __attribute__((packed));
struct vive_imu_report
{
uint8_t id;
struct vive_imu_sample sample[3];
} __attribute__((packed));
#define VIVE_CONTROLLER_LIGHTHOUSE_PULSE_REPORT_ID 0x21
struct vive_controller_lighthouse_pulse
{
uint16_t id;
uint16_t duration;
uint32_t timestamp;
} __attribute__((packed));