mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2025-01-23 23:21:46 +00:00
1f64f714f6
This patch has been spun of the rather massive amount of work for adding input devices into Monado. The interfaces feels somewhat stable now so could do with another pair of eyes on them, before too much work is done on st/oxr.
760 lines
16 KiB
C
760 lines
16 KiB
C
// Copyright 2016, Joey Ferwerda.
|
|
// Copyright 2019, Collabora, Ltd.
|
|
// SPDX-License-Identifier: BSL-1.0
|
|
/*!
|
|
* @file
|
|
* @brief PSVR device implementation, imported from OpenHMD.
|
|
* @author Joey Ferwerda <joeyferweda@gmail.com>
|
|
* @author Philipp Zabel <philipp.zabel@gmail.com>
|
|
* @author Jakob Bornecrantz <jakob@collabora.com>
|
|
* @ingroup drv_psvr
|
|
*/
|
|
|
|
#include "xrt/xrt_compiler.h"
|
|
|
|
#include "util/u_misc.h"
|
|
#include "util/u_time.h"
|
|
#include "util/u_debug.h"
|
|
#include "util/u_device.h"
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "psvr_device.h"
|
|
|
|
|
|
/*
|
|
*
|
|
* Structs and defines.
|
|
*
|
|
*/
|
|
|
|
DEBUG_GET_ONCE_BOOL_OPTION(psvr_disco, "PSVR_DISCO", false)
|
|
|
|
#define FEATURE_BUFFER_SIZE 256
|
|
|
|
/*!
|
|
* Private struct for the @ref drv_psvr device.
|
|
*
|
|
* @ingroup drv_psvr
|
|
*/
|
|
struct psvr_device
|
|
{
|
|
struct xrt_device base;
|
|
|
|
hid_device *hmd_handle;
|
|
hid_device *hmd_control;
|
|
|
|
struct psvr_sensor_packet sensor;
|
|
|
|
struct
|
|
{
|
|
struct xrt_vec3 gyro;
|
|
struct xrt_vec3 accel;
|
|
} raw;
|
|
|
|
uint16_t buttons;
|
|
|
|
bool powered_on;
|
|
bool in_vr_mode;
|
|
|
|
bool print_spew;
|
|
bool print_debug;
|
|
};
|
|
|
|
|
|
// Alternative way to turn on all of the leds.
|
|
XRT_MAYBE_UNUSED static const unsigned char psvr_tracking_on[12] = {
|
|
0x11, 0x00, 0xaa, 0x08, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
|
|
#define PSVR_LED_POWER_OFF 0
|
|
#define PSVR_LED_POWER_MAX 100
|
|
|
|
enum psvr_leds
|
|
{
|
|
PSVR_LED_A = (1 << 0),
|
|
PSVR_LED_B = (1 << 1),
|
|
PSVR_LED_C = (1 << 2),
|
|
PSVR_LED_D = (1 << 3),
|
|
PSVR_LED_E = (1 << 4),
|
|
PSVR_LED_F = (1 << 5),
|
|
PSVR_LED_G = (1 << 6),
|
|
PSVR_LED_H = (1 << 7),
|
|
PSVR_LED_I = (1 << 8),
|
|
|
|
PSVR_LED_FRONT = PSVR_LED_A | PSVR_LED_B | PSVR_LED_C | PSVR_LED_D |
|
|
PSVR_LED_E | PSVR_LED_F | PSVR_LED_G,
|
|
|
|
PSVR_LED_BACK = PSVR_LED_H | PSVR_LED_I,
|
|
|
|
PSVR_LED_ALL = PSVR_LED_FRONT | PSVR_LED_BACK,
|
|
};
|
|
|
|
|
|
/*
|
|
*
|
|
* Helpers and internal functions.
|
|
*
|
|
*/
|
|
|
|
static inline struct psvr_device *
|
|
psvr_device(struct xrt_device *p)
|
|
{
|
|
return (struct psvr_device *)p;
|
|
}
|
|
|
|
static int
|
|
open_hid(struct psvr_device *p,
|
|
struct hid_device_info *dev_info,
|
|
hid_device **out_dev)
|
|
{
|
|
hid_device *dev = NULL;
|
|
int ret;
|
|
|
|
dev = hid_open_path(dev_info->path);
|
|
if (dev == NULL) {
|
|
PSVR_ERROR(p, "Failed to open '%s'", dev_info->path);
|
|
return -1;
|
|
}
|
|
|
|
ret = hid_set_nonblocking(dev, 1);
|
|
if (ret != 0) {
|
|
PSVR_ERROR(p, "Failed to set non-blocking on device");
|
|
hid_close(dev);
|
|
return -1;
|
|
}
|
|
|
|
*out_dev = dev;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
send_to_control(struct psvr_device *psvr, const uint8_t *data, size_t size)
|
|
{
|
|
return hid_write(psvr->hmd_control, data, size);
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* Packet reading code.
|
|
*
|
|
*/
|
|
|
|
static void
|
|
accel_from_psvr_vec(const int16_t smp[3], struct xrt_vec3 *out_vec)
|
|
{
|
|
//! @todo Figure out callibration data and use here.
|
|
|
|
// clang-format off
|
|
out_vec->x = (float)(smp[1] * (9.81 / 16384.0));
|
|
out_vec->y = (float)(smp[0] * (9.81 / 16384.0));
|
|
out_vec->z = (float)(smp[2] * -(9.81 / 16384.0));
|
|
// clang-format on
|
|
}
|
|
|
|
static void
|
|
gyro_from_psvr_vec(const int16_t smp[3], struct xrt_vec3 *out_vec)
|
|
{
|
|
//! @todo Figure out callibration data and use here.
|
|
|
|
out_vec->x = (float)(smp[1] * 0.00105);
|
|
out_vec->y = (float)(smp[0] * 0.00105);
|
|
out_vec->z = (float)(smp[2] * 0.00105 * -1.0);
|
|
}
|
|
|
|
static void
|
|
update_fusion(struct psvr_device *psvr,
|
|
struct psvr_sensor_sample *sample,
|
|
uint32_t tick_delta)
|
|
{
|
|
struct xrt_vec3 mag = {0.0f, 0.0f, 0.0f};
|
|
float dt = tick_delta * PSVR_TICK_PERIOD;
|
|
(void)mag;
|
|
(void)dt;
|
|
|
|
accel_from_psvr_vec(sample->accel, &psvr->raw.accel);
|
|
gyro_from_psvr_vec(sample->gyro, &psvr->raw.gyro);
|
|
|
|
//! @todo This is where we do the sensor fusion.
|
|
// ofusion_update(&psvr->sensor_fusion, dt, &psvr->raw.gyro,
|
|
// &psvr->raw.accel, &mag);
|
|
}
|
|
|
|
static uint32_t
|
|
calc_delta_and_handle_rollover(uint32_t next, uint32_t last)
|
|
{
|
|
uint32_t tick_delta = next - last;
|
|
|
|
// The 24-bit tick counter has rolled over,
|
|
// adjust the "negative" value to be positive.
|
|
if (tick_delta > 0xffffff) {
|
|
tick_delta += 0x1000000;
|
|
}
|
|
|
|
return tick_delta;
|
|
}
|
|
|
|
static void
|
|
handle_tracker_sensor_msg(struct psvr_device *psvr,
|
|
unsigned char *buffer,
|
|
int size)
|
|
{
|
|
uint32_t last_sample_tick = psvr->sensor.samples[1].tick;
|
|
|
|
if (!psvr_parse_sensor_packet(&psvr->sensor, buffer, size)) {
|
|
PSVR_ERROR(psvr, "couldn't decode tracker sensor message");
|
|
}
|
|
|
|
struct psvr_sensor_packet *s = &psvr->sensor;
|
|
|
|
// Simplest is the buttons.
|
|
psvr->buttons = s->buttons;
|
|
|
|
uint32_t tick_delta = 500;
|
|
|
|
// Startup correction, ignore last_sample_tick if zero.
|
|
if (last_sample_tick > 0) {
|
|
tick_delta = calc_delta_and_handle_rollover(s->samples[0].tick,
|
|
last_sample_tick);
|
|
|
|
// The PSVR device can buffer sensor data from previous
|
|
// sessions which we can get at the start of new sessions.
|
|
// @todo Maybe just skip the first 10 sensor packets?
|
|
// @todo Maybe reset sensor fusion?
|
|
if (tick_delta < 400 || tick_delta > 600) {
|
|
PSVR_DEBUG(psvr, "tick_delta = %u", tick_delta);
|
|
tick_delta = 500;
|
|
}
|
|
}
|
|
|
|
// Update the fusion with first sample.
|
|
update_fusion(psvr, &s->samples[0], tick_delta);
|
|
|
|
// New delta between the two samples.
|
|
tick_delta = calc_delta_and_handle_rollover(s->samples[1].tick,
|
|
s->samples[0].tick);
|
|
|
|
// Update the fusion with second sample.
|
|
update_fusion(psvr, &s->samples[1], tick_delta);
|
|
}
|
|
|
|
static void
|
|
handle_control_status_msg(struct psvr_device *psvr,
|
|
unsigned char *buffer,
|
|
int size)
|
|
{
|
|
struct psvr_status_packet packet;
|
|
|
|
if (!psvr_parse_status_packet(&packet, buffer, size)) {
|
|
PSVR_ERROR(psvr, "couldn't decode tracker sensor message");
|
|
}
|
|
|
|
|
|
/*
|
|
* Power
|
|
*/
|
|
|
|
if (packet.status & PSVR_STATUS_BIT_POWER) {
|
|
if (!psvr->powered_on) {
|
|
PSVR_DEBUG(psvr, "Device powered on! '%02x'",
|
|
packet.status);
|
|
}
|
|
psvr->powered_on = true;
|
|
} else {
|
|
if (psvr->powered_on) {
|
|
PSVR_DEBUG(psvr, "Device powered off! '%02x'",
|
|
packet.status);
|
|
}
|
|
psvr->powered_on = false;
|
|
}
|
|
|
|
|
|
/*
|
|
* VR-Mode
|
|
*/
|
|
|
|
if (packet.vr_mode == PSVR_STATUS_VR_MODE_OFF) {
|
|
if (psvr->in_vr_mode) {
|
|
PSVR_DEBUG(psvr, "Device not in vr-mode! '%02x'",
|
|
packet.vr_mode);
|
|
}
|
|
psvr->in_vr_mode = false;
|
|
} else if (packet.vr_mode == PSVR_STATUS_VR_MODE_ON) {
|
|
if (!psvr->in_vr_mode) {
|
|
PSVR_DEBUG(psvr, "Device in vr-mode! '%02x'",
|
|
packet.vr_mode);
|
|
}
|
|
psvr->in_vr_mode = true;
|
|
} else {
|
|
PSVR_ERROR(psvr, "Unknown vr_mode status!");
|
|
}
|
|
}
|
|
|
|
static void
|
|
handle_control_0xA0(struct psvr_device *psvr, unsigned char *buffer, int size)
|
|
{
|
|
if (size < 4) {
|
|
return;
|
|
}
|
|
|
|
PSVR_DEBUG(psvr, "%02x %02x %02x %02x", buffer[0], buffer[1], buffer[2],
|
|
buffer[3]);
|
|
}
|
|
|
|
static int
|
|
read_handle_packets(struct psvr_device *psvr)
|
|
{
|
|
uint8_t buffer[FEATURE_BUFFER_SIZE];
|
|
int size = 0;
|
|
|
|
do {
|
|
size = hid_read(psvr->hmd_handle, buffer, FEATURE_BUFFER_SIZE);
|
|
if (size == 0) {
|
|
return 0;
|
|
}
|
|
if (size < 0) {
|
|
return -1;
|
|
}
|
|
|
|
handle_tracker_sensor_msg(psvr, buffer, size);
|
|
} while (true);
|
|
}
|
|
|
|
static int
|
|
read_control_packets(struct psvr_device *psvr)
|
|
{
|
|
uint8_t buffer[FEATURE_BUFFER_SIZE];
|
|
int size = 0;
|
|
|
|
do {
|
|
size = hid_read(psvr->hmd_control, buffer, FEATURE_BUFFER_SIZE);
|
|
if (size == 0) {
|
|
return 0;
|
|
}
|
|
if (size < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (buffer[0] == PSVR_PKG_STATUS) {
|
|
handle_control_status_msg(psvr, buffer, size);
|
|
} else if (buffer[0] == PSVR_PKG_0xA0) {
|
|
handle_control_0xA0(psvr, buffer, size);
|
|
} else {
|
|
PSVR_DEBUG(psvr, "Got report, 0x%02x", buffer[0]);
|
|
}
|
|
|
|
} while (true);
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* Control sending functions.
|
|
*
|
|
*/
|
|
|
|
static int
|
|
wait_for_power(struct psvr_device *psvr, bool on)
|
|
{
|
|
for (int i = 0; i < 5000; i++) {
|
|
read_handle_packets(psvr);
|
|
read_control_packets(psvr);
|
|
|
|
if (psvr->powered_on == on) {
|
|
return 0;
|
|
}
|
|
|
|
usleep(1000);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
wait_for_vr_mode(struct psvr_device *psvr, bool on)
|
|
{
|
|
for (int i = 0; i < 5000; i++) {
|
|
read_handle_packets(psvr);
|
|
read_control_packets(psvr);
|
|
|
|
if (psvr->in_vr_mode == on) {
|
|
return 0;
|
|
}
|
|
|
|
usleep(1000);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
control_power_and_wait(struct psvr_device *psvr, bool on)
|
|
{
|
|
const char *status = on ? "on" : "off";
|
|
const uint8_t data[8] = {
|
|
0x17, 0x00, 0xaa, 0x04, on, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
int ret = send_to_control(psvr, data, sizeof(data));
|
|
if (ret < 0) {
|
|
PSVR_ERROR(psvr, "Failed to switch %s the headset! '%i'",
|
|
status, ret);
|
|
}
|
|
|
|
ret = wait_for_power(psvr, on);
|
|
if (ret < 0) {
|
|
PSVR_ERROR(psvr, "Failed to wait for headset power %s! '%i'",
|
|
status, ret);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
control_vrmode_and_wait(struct psvr_device *psvr, bool on)
|
|
{
|
|
const uint8_t data[8] = {
|
|
0x23, 0x00, 0xaa, 0x04, on, 0x00, 0x00, 0x00,
|
|
};
|
|
int ret;
|
|
|
|
ret = send_to_control(psvr, data, sizeof(data));
|
|
if (ret < 0) {
|
|
PSVR_ERROR(psvr, "Failed %s vr-mode the headset! '%i'",
|
|
on ? "enable" : "disable", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = wait_for_vr_mode(psvr, on);
|
|
if (ret < 0) {
|
|
PSVR_ERROR(psvr, "Failed to wait for vr mode! '%i'", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*!
|
|
* Control the leds on the headset, allowing you to turn on and off different
|
|
* leds with a single call.
|
|
*
|
|
* @param[in] psvr The PSVR to control leds on.
|
|
* @param[in] adjust The leds to adjust with @p power.
|
|
* @param[in] power The power level to give to @p adjust leds.
|
|
* @param[in] off Leds that should be turned off,
|
|
* @p adjust has higher priority.
|
|
* @ingroup drv_psvr
|
|
*/
|
|
static int
|
|
control_leds(struct psvr_device *psvr,
|
|
enum psvr_leds adjust,
|
|
uint8_t power,
|
|
enum psvr_leds off)
|
|
{
|
|
// Get the leds we should control and remove any extra bits.
|
|
enum psvr_leds all = 0;
|
|
all |= adjust;
|
|
all |= off;
|
|
all &= PSVR_LED_ALL;
|
|
if (all == 0) {
|
|
// Nothing todo.
|
|
return 0;
|
|
}
|
|
|
|
// Just in case, if the value is larger
|
|
// then max it will turn the leds off.
|
|
if (power > PSVR_LED_POWER_MAX) {
|
|
power = PSVR_LED_POWER_MAX;
|
|
}
|
|
|
|
uint8_t data[20] = {
|
|
0x15,
|
|
0x00,
|
|
0xaa,
|
|
0x10,
|
|
all,
|
|
all >> 8,
|
|
adjust & PSVR_LED_A ? power : PSVR_LED_POWER_OFF,
|
|
adjust & PSVR_LED_B ? power : PSVR_LED_POWER_OFF,
|
|
adjust & PSVR_LED_C ? power : PSVR_LED_POWER_OFF,
|
|
adjust & PSVR_LED_D ? power : PSVR_LED_POWER_OFF,
|
|
adjust & PSVR_LED_E ? power : PSVR_LED_POWER_OFF,
|
|
adjust & PSVR_LED_F ? power : PSVR_LED_POWER_OFF,
|
|
adjust & PSVR_LED_G ? power : PSVR_LED_POWER_OFF,
|
|
adjust & PSVR_LED_H ? power : PSVR_LED_POWER_OFF,
|
|
adjust & PSVR_LED_I ? power : PSVR_LED_POWER_OFF,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
};
|
|
|
|
return send_to_control(psvr, data, sizeof(data));
|
|
}
|
|
|
|
static int
|
|
disco_leds(struct psvr_device *psvr)
|
|
{
|
|
static const uint16_t leds[] = {
|
|
// First loop
|
|
PSVR_LED_A,
|
|
PSVR_LED_E,
|
|
PSVR_LED_B,
|
|
PSVR_LED_G,
|
|
PSVR_LED_D,
|
|
PSVR_LED_C,
|
|
PSVR_LED_F,
|
|
// Second loop
|
|
PSVR_LED_A,
|
|
PSVR_LED_E,
|
|
PSVR_LED_B,
|
|
PSVR_LED_G,
|
|
PSVR_LED_D,
|
|
PSVR_LED_C,
|
|
PSVR_LED_F,
|
|
// Blink loop
|
|
PSVR_LED_BACK,
|
|
PSVR_LED_FRONT,
|
|
PSVR_LED_BACK,
|
|
PSVR_LED_FRONT,
|
|
// All on after loop
|
|
PSVR_LED_ALL,
|
|
};
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(leds); i++) {
|
|
int ret = control_leds(psvr, leds[i], PSVR_LED_POWER_MAX,
|
|
PSVR_LED_ALL);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
// Sleep for a tenth of a second while polling for packages.
|
|
for (int k = 0; k < 100; k++) {
|
|
ret = read_handle_packets(psvr);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = read_control_packets(psvr);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
usleep(1000);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
teardown(struct psvr_device *psvr)
|
|
{
|
|
|
|
if (psvr->hmd_control != NULL) {
|
|
// Turn off VR-mode and power down headset.
|
|
if (control_vrmode_and_wait(psvr, false) < 0 ||
|
|
control_power_and_wait(psvr, false) < 0) {
|
|
PSVR_ERROR(psvr, "Failed to shut down the headset!");
|
|
}
|
|
|
|
hid_close(psvr->hmd_control);
|
|
psvr->hmd_control = NULL;
|
|
}
|
|
|
|
if (psvr->hmd_handle != NULL) {
|
|
hid_close(psvr->hmd_handle);
|
|
psvr->hmd_handle = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* xrt_device functions.
|
|
*
|
|
*/
|
|
|
|
static void
|
|
psvr_device_update_inputs(struct xrt_device *xdev,
|
|
struct time_state *timekeeping)
|
|
{
|
|
// Empty
|
|
}
|
|
|
|
static void
|
|
psvr_device_get_tracked_pose(struct xrt_device *xdev,
|
|
enum xrt_input_name name,
|
|
struct time_state *timekeeping,
|
|
int64_t *out_timestamp,
|
|
struct xrt_space_relation *out_relation)
|
|
{
|
|
struct psvr_device *psvr = psvr_device(xdev);
|
|
|
|
if (name != XRT_INPUT_GENERIC_HEAD_RELATION) {
|
|
PSVR_ERROR(psvr, "unknown input name");
|
|
return;
|
|
}
|
|
|
|
// Read all packets.
|
|
read_handle_packets(psvr);
|
|
read_control_packets(psvr);
|
|
|
|
// Clear out the relation.
|
|
memset(out_relation, 0, sizeof(*out_relation));
|
|
|
|
int64_t now = time_state_get_now(timekeeping);
|
|
//! @todo adjust for latency here
|
|
*out_timestamp = now;
|
|
|
|
out_relation->pose.orientation.w = 1.0f;
|
|
|
|
//! @todo assuming that orientation is actually currently tracked.
|
|
out_relation->relation_flags = (enum xrt_space_relation_flags)(
|
|
XRT_SPACE_RELATION_ORIENTATION_VALID_BIT |
|
|
XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT);
|
|
|
|
PSVR_SPEW(psvr, "\n\taccel = %f %f %f\n\tgyro = %f %f %f",
|
|
psvr->raw.accel.x, psvr->raw.accel.y, psvr->raw.accel.z,
|
|
psvr->raw.gyro.x, psvr->raw.gyro.y, psvr->raw.gyro.z);
|
|
}
|
|
|
|
static void
|
|
psvr_device_get_view_pose(struct xrt_device *xdev,
|
|
struct xrt_vec3 *eye_relation,
|
|
uint32_t view_index,
|
|
struct xrt_pose *out_pose)
|
|
{
|
|
struct xrt_pose pose = {{0.0f, 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 0.0f}};
|
|
bool adjust = view_index == 0;
|
|
|
|
pose.position.x = eye_relation->x / 2.0f;
|
|
pose.position.y = eye_relation->y / 2.0f;
|
|
pose.position.z = eye_relation->z / 2.0f;
|
|
|
|
// Adjust for left/right while also making sure there aren't any -0.f.
|
|
if (pose.position.x > 0.0f && adjust) {
|
|
pose.position.x = -pose.position.x;
|
|
}
|
|
if (pose.position.y > 0.0f && adjust) {
|
|
pose.position.y = -pose.position.y;
|
|
}
|
|
if (pose.position.z > 0.0f && adjust) {
|
|
pose.position.z = -pose.position.z;
|
|
}
|
|
|
|
*out_pose = pose;
|
|
}
|
|
|
|
static void
|
|
psvr_device_destroy(struct xrt_device *xdev)
|
|
{
|
|
struct psvr_device *psvr = psvr_device(xdev);
|
|
teardown(psvr);
|
|
free(psvr);
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* Exported functions.
|
|
*
|
|
*/
|
|
|
|
struct xrt_device *
|
|
psvr_device_create(struct hid_device_info *hmd_handle_info,
|
|
struct hid_device_info *hmd_control_info,
|
|
bool print_spew,
|
|
bool print_debug)
|
|
{
|
|
enum u_device_alloc_flags flags =
|
|
U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE;
|
|
struct psvr_device *psvr =
|
|
U_DEVICE_ALLOCATE(struct psvr_device, flags, 1);
|
|
int ret;
|
|
|
|
psvr->print_spew = print_spew;
|
|
psvr->print_debug = print_debug;
|
|
psvr->base.update_inputs = psvr_device_update_inputs;
|
|
psvr->base.get_tracked_pose = psvr_device_get_tracked_pose;
|
|
psvr->base.get_view_pose = psvr_device_get_view_pose;
|
|
psvr->base.destroy = psvr_device_destroy;
|
|
psvr->base.inputs[0].name = XRT_INPUT_GENERIC_HEAD_RELATION;
|
|
|
|
ret = open_hid(psvr, hmd_handle_info, &psvr->hmd_handle);
|
|
if (ret != 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = open_hid(psvr, hmd_control_info, &psvr->hmd_control);
|
|
if (ret < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (control_power_and_wait(psvr, true) < 0 ||
|
|
control_vrmode_and_wait(psvr, true) < 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (debug_get_bool_option_psvr_disco()) {
|
|
ret = disco_leds(psvr);
|
|
} else {
|
|
ret = control_leds(psvr, PSVR_LED_ALL, PSVR_LED_POWER_MAX, 0);
|
|
}
|
|
if (ret < 0) {
|
|
PSVR_ERROR(psvr, "Failed to control leds '%i'", ret);
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/*
|
|
* Device setup.
|
|
*/
|
|
|
|
struct u_device_simple_info info;
|
|
info.display.w_pixels = 1980;
|
|
info.display.h_pixels = 1080;
|
|
info.display.w_meters = 0.126; // from calculated specs
|
|
info.display.h_meters = 0.071;
|
|
info.lens_horizontal_separation_meters = 0.0630999878f;
|
|
info.lens_vertical_position_meters = 0.0394899882f;
|
|
info.views[0].fov = 103.57f * M_PI / 180.0f;
|
|
info.views[1].fov = 103.57f * M_PI / 180.0f;
|
|
|
|
if (!u_device_setup_split_side_by_side(&psvr->base, &info)) {
|
|
PSVR_ERROR(psvr, "Failed to setup basic device info");
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/*
|
|
* Finishing touches.
|
|
*/
|
|
|
|
if (psvr->print_debug) {
|
|
u_device_dump_config(&psvr->base, __func__, "Sony PSVR");
|
|
}
|
|
|
|
PSVR_DEBUG(psvr, "YES!");
|
|
|
|
return &psvr->base;
|
|
|
|
|
|
cleanup:
|
|
PSVR_DEBUG(psvr, "NO! :(");
|
|
|
|
teardown(psvr);
|
|
free(psvr);
|
|
|
|
return NULL;
|
|
}
|