mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2024-12-29 11:06:18 +00:00
d/hydra: Initially-functional multi-device Razer Hydra driver.
Not pretty - no filtering on tracking, coordinate system probably a little bit wrong still, no hemisphere tracking, but enough to show movement and resizing in hello_xr.
This commit is contained in:
parent
9a4fee3896
commit
2cd28914c7
|
@ -115,6 +115,9 @@ endif()
|
|||
if(TRUE)
|
||||
# Uses the Monado internal hid wrapper.
|
||||
set(BUILD_DRIVER_PSMV TRUE)
|
||||
|
||||
add_definitions(-DXRT_BUILD_HYDRA)
|
||||
set(BUILD_DRIVER_HYDRA TRUE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Wall -Wextra -Wno-unused-parameter")
|
||||
|
|
|
@ -25,6 +25,17 @@ if(BUILD_DRIVER_HDK)
|
|||
endif()
|
||||
|
||||
|
||||
if(BUILD_DRIVER_HYDRA)
|
||||
set(HYDRA_SOURCE_FILES
|
||||
hydra/hydra_driver.c
|
||||
hydra/hydra_interface.h
|
||||
)
|
||||
|
||||
# Use OBJECT to not create a archive, since it just gets in the way.
|
||||
add_library(drv_hydra OBJECT ${HYDRA_SOURCE_FILES})
|
||||
set_property(TARGET drv_hydra PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
|
||||
if(BUILD_DRIVER_OHMD)
|
||||
set(OHMD_SOURCE_FILES
|
||||
ohmd/oh_device.c
|
||||
|
|
710
src/xrt/drivers/hydra/hydra_driver.c
Normal file
710
src/xrt/drivers/hydra/hydra_driver.c
Normal file
|
@ -0,0 +1,710 @@
|
|||
// Copyright 2019, Collabora, Ltd.
|
||||
// Copyright 2011, Iowa State University
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Razer Hydra prober and driver code
|
||||
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||
*
|
||||
* Portions based on the VRPN Razer Hydra driver,
|
||||
* originally written by Ryan Pavlik and available under the BSL-1.0.
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "xrt/xrt_prober.h"
|
||||
|
||||
#include "math/m_api.h"
|
||||
#include "util/u_debug.h"
|
||||
#include "util/u_device.h"
|
||||
#include "util/u_misc.h"
|
||||
#include "util/u_time.h"
|
||||
|
||||
#include "hydra_interface.h"
|
||||
|
||||
#ifdef XRT_OS_LINUX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
* Defines & structs.
|
||||
*
|
||||
*/
|
||||
|
||||
#define HYDRA_SPEW(p, ...) \
|
||||
do { \
|
||||
if (p->print_spew) { \
|
||||
fprintf(stderr, "%s - ", __func__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define HYDRA_DEBUG(p, ...) \
|
||||
do { \
|
||||
if (p->print_debug) { \
|
||||
fprintf(stderr, "%s - ", __func__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define HYDRA_ERROR(p, ...) \
|
||||
do { \
|
||||
fprintf(stderr, "%s - ", __func__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n"); \
|
||||
} while (false)
|
||||
|
||||
DEBUG_GET_ONCE_BOOL_OPTION(hydra_spew, "HYDRA_PRINT_SPEW", false)
|
||||
DEBUG_GET_ONCE_BOOL_OPTION(hydra_debug, "HYDRA_PRINT_DEBUG", false)
|
||||
enum hydra_input_index
|
||||
{
|
||||
HYDRA_INDEX_1_CLICK,
|
||||
HYDRA_INDEX_2_CLICK,
|
||||
HYDRA_INDEX_3_CLICK,
|
||||
HYDRA_INDEX_4_CLICK,
|
||||
HYDRA_INDEX_MIDDLE_CLICK,
|
||||
HYDRA_INDEX_BUMPER_CLICK,
|
||||
HYDRA_INDEX_JOYSTICK_CLICK,
|
||||
HYDRA_INDEX_JOYSTICK_VALUE,
|
||||
HYDRA_INDEX_TRIGGER_VALUE,
|
||||
HYDRA_INDEX_POSE,
|
||||
HYDRA_MAX_CONTROLLER_INDEX
|
||||
};
|
||||
|
||||
/* Yes this is a bizarre bit mask. Mysteries of the Hydra. */
|
||||
enum hydra_button_bit
|
||||
{
|
||||
HYDRA_BUTTON_BIT_BUMPER = (1 << 0),
|
||||
|
||||
HYDRA_BUTTON_BIT_3 = (1 << 1),
|
||||
HYDRA_BUTTON_BIT_1 = (1 << 2),
|
||||
HYDRA_BUTTON_BIT_2 = (1 << 3),
|
||||
HYDRA_BUTTON_BIT_4 = (1 << 4),
|
||||
|
||||
HYDRA_BUTTON_BIT_MIDDLE = (1 << 5),
|
||||
HYDRA_BUTTON_BIT_JOYSTICK = (1 << 6),
|
||||
};
|
||||
|
||||
static const uint8_t HYDRA_REPORT_START_MOTION[] = {
|
||||
|
||||
0x00, // first byte must be report type
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x06, 0x00};
|
||||
|
||||
static const uint8_t HYDRA_REPORT_START_GAMEPAD[] = {
|
||||
0x00, // first byte must be report type
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x05, 0x00};
|
||||
|
||||
struct hydra_controller_state
|
||||
{
|
||||
struct xrt_pose pose;
|
||||
struct xrt_vec2 js;
|
||||
float trigger;
|
||||
uint8_t buttons;
|
||||
};
|
||||
/*!
|
||||
* The states of the finite-state machine controlling the Hydra.
|
||||
*/
|
||||
enum hydra_sm_state
|
||||
{
|
||||
HYDRA_SM_LISTENING_AFTER_CONNECT = 0,
|
||||
HYDRA_SM_LISTENING_AFTER_SET_FEATURE,
|
||||
HYDRA_SM_REPORTING
|
||||
};
|
||||
|
||||
/*!
|
||||
* The details of the Hydra state machine in a convenient package.
|
||||
*/
|
||||
struct hydra_state_machine
|
||||
{
|
||||
enum hydra_sm_state current_state;
|
||||
|
||||
//! Time of the last (non-trivial) state transition
|
||||
timepoint_ns transition_time;
|
||||
};
|
||||
|
||||
struct hydra_device;
|
||||
|
||||
/*!
|
||||
* A Razer Hydra system containing two controllers.
|
||||
*
|
||||
* @ingroup drv_hydra
|
||||
*/
|
||||
struct hydra_system
|
||||
{
|
||||
struct xrt_tracking base;
|
||||
struct os_hid_device *data_hid;
|
||||
struct os_hid_device *command_hid;
|
||||
|
||||
struct hydra_state_machine sm;
|
||||
struct hydra_device *devs[2];
|
||||
|
||||
int16_t report_counter;
|
||||
|
||||
//! Last time that we received a report
|
||||
timepoint_ns report_time;
|
||||
|
||||
/*!
|
||||
* Reference count of the number of devices still alive using this
|
||||
* system
|
||||
*/
|
||||
uint8_t refs;
|
||||
|
||||
/*!
|
||||
* Was the hydra in gamepad mode at start?
|
||||
*
|
||||
* If it was, we set it back to gamepad on destruction.
|
||||
*/
|
||||
bool was_in_gamepad_mode;
|
||||
int motion_attempt_number;
|
||||
|
||||
bool print_spew;
|
||||
bool print_debug;
|
||||
};
|
||||
|
||||
/*!
|
||||
* A Razer Hydra device, representing just a single controller.
|
||||
*
|
||||
* @ingroup drv_hydra
|
||||
*/
|
||||
struct hydra_device
|
||||
{
|
||||
struct xrt_device base;
|
||||
struct hydra_system *sys;
|
||||
|
||||
|
||||
//! Last time that we updated inputs
|
||||
timepoint_ns input_time;
|
||||
|
||||
// bool calibration_done;
|
||||
// int mirror;
|
||||
// int sign_x;
|
||||
|
||||
struct hydra_controller_state state;
|
||||
|
||||
//! Which hydra controller in the system are we?
|
||||
size_t index;
|
||||
|
||||
bool print_spew;
|
||||
bool print_debug;
|
||||
};
|
||||
|
||||
/*
|
||||
*
|
||||
* Internal functions.
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
hydra_device_parse_controller(struct hydra_device *hd, uint8_t *buf);
|
||||
|
||||
static inline struct hydra_device *
|
||||
hydra_device(struct xrt_device *xdev)
|
||||
{
|
||||
assert(xdev);
|
||||
struct hydra_device *ret = (struct hydra_device *)xdev;
|
||||
assert(ret->sys != NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline struct hydra_system *
|
||||
hydra_system(struct xrt_tracking *xtrack)
|
||||
{
|
||||
assert(xtrack);
|
||||
struct hydra_system *ret = (struct hydra_system *)xtrack;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Reports the number of seconds since the most recent change of state.
|
||||
*
|
||||
* @relates hydra_sm
|
||||
*/
|
||||
static float
|
||||
hydra_sm_seconds_since_transition(struct hydra_state_machine *hsm,
|
||||
timepoint_ns now)
|
||||
{
|
||||
|
||||
if (hsm->transition_time == 0) {
|
||||
hsm->transition_time = now;
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
float state_duration_s = time_ns_to_s(now - hsm->transition_time);
|
||||
return state_duration_s;
|
||||
}
|
||||
/*!
|
||||
* Performs a state transition, updating the transition time if the state
|
||||
* actually changed.
|
||||
*
|
||||
* @relates hydra_sm
|
||||
*/
|
||||
static int
|
||||
hydra_sm_transition(struct hydra_state_machine *hsm,
|
||||
enum hydra_sm_state new_state,
|
||||
timepoint_ns now)
|
||||
{
|
||||
if (hsm->transition_time == 0) {
|
||||
hsm->transition_time = now;
|
||||
}
|
||||
if (new_state != hsm->current_state) {
|
||||
hsm->current_state = new_state;
|
||||
hsm->transition_time = now;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static inline uint8_t
|
||||
hydra_read_uint8(uint8_t **bufptr)
|
||||
{
|
||||
uint8_t ret = **bufptr;
|
||||
(*bufptr)++;
|
||||
return ret;
|
||||
}
|
||||
static inline int16_t
|
||||
hydra_read_int16_le(uint8_t **bufptr)
|
||||
{
|
||||
uint8_t *buf = *bufptr;
|
||||
//! @todo nothing actually defines XRT_BIG_ENDIAN when needed!
|
||||
#ifdef XRT_BIG_ENDIAN
|
||||
uint8_t bytes[2] = {buf[1], buf[0]};
|
||||
#else
|
||||
uint8_t bytes[2] = {buf[0], buf[1]};
|
||||
#endif // XRT_BIG_ENDIAN
|
||||
(*bufptr) += 2;
|
||||
int16_t ret;
|
||||
memcpy(&ret, bytes, sizeof(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Parse the controller-specific part of a buffer into a hydra device.
|
||||
*/
|
||||
static void
|
||||
hydra_device_parse_controller(struct hydra_device *hd, uint8_t *buf)
|
||||
{
|
||||
struct hydra_controller_state *state = &hd->state;
|
||||
static const float SCALE_MM_TO_METER = 0.001f;
|
||||
static const float SCALE_INT16_TO_FLOAT_PLUSMINUS_1 = 1.0f / 32768.0f;
|
||||
static const float SCALE_UINT8_TO_FLOAT_0_TO_1 = 1.0f / 255.0f;
|
||||
state->pose.position.x = hydra_read_int16_le(&buf) * SCALE_MM_TO_METER;
|
||||
state->pose.position.z = hydra_read_int16_le(&buf) * SCALE_MM_TO_METER;
|
||||
state->pose.position.y = -hydra_read_int16_le(&buf) * SCALE_MM_TO_METER;
|
||||
|
||||
// the negatives are to fix handedness
|
||||
state->pose.orientation.w =
|
||||
hydra_read_int16_le(&buf) * SCALE_INT16_TO_FLOAT_PLUSMINUS_1;
|
||||
state->pose.orientation.x =
|
||||
hydra_read_int16_le(&buf) * SCALE_INT16_TO_FLOAT_PLUSMINUS_1;
|
||||
state->pose.orientation.y =
|
||||
-hydra_read_int16_le(&buf) * SCALE_INT16_TO_FLOAT_PLUSMINUS_1;
|
||||
state->pose.orientation.z =
|
||||
-hydra_read_int16_le(&buf) * SCALE_INT16_TO_FLOAT_PLUSMINUS_1;
|
||||
|
||||
//! @todo the presence of this suggest we're not decoding the
|
||||
//! orientation right.
|
||||
math_quat_normalize(&state->pose.orientation);
|
||||
|
||||
state->buttons = hydra_read_uint8(&buf);
|
||||
|
||||
state->js.x =
|
||||
hydra_read_int16_le(&buf) * SCALE_INT16_TO_FLOAT_PLUSMINUS_1;
|
||||
state->js.y =
|
||||
hydra_read_int16_le(&buf) * SCALE_INT16_TO_FLOAT_PLUSMINUS_1;
|
||||
|
||||
state->trigger = hydra_read_uint8(&buf) * SCALE_UINT8_TO_FLOAT_0_TO_1;
|
||||
|
||||
HYDRA_SPEW(hd,
|
||||
"\n\t"
|
||||
"controller: %i\n\t"
|
||||
"position: (%-1.2f, %-1.2f, %-1.2f)\n\t"
|
||||
"orientation: (%-1.2f, %-1.2f, %-1.2f, %-1.2f)\n\t"
|
||||
"buttons: %08x\n\t"
|
||||
"joystick: (%-1.2f, %-1.2f)\n\t"
|
||||
"trigger: %01.2f\n",
|
||||
(int)hd->index, state->pose.position.x,
|
||||
state->pose.position.y, state->pose.position.z,
|
||||
state->pose.orientation.x, state->pose.orientation.y,
|
||||
state->pose.orientation.z, state->pose.orientation.w,
|
||||
state->buttons, state->js.x, state->js.y, state->trigger);
|
||||
}
|
||||
|
||||
static int
|
||||
hydra_system_read_data_hid(struct hydra_system *hs, timepoint_ns now)
|
||||
{
|
||||
assert(hs);
|
||||
uint8_t buffer[128];
|
||||
bool got_message = false;
|
||||
do {
|
||||
int ret = os_hid_read(hs->data_hid, buffer, sizeof(buffer), 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return got_message ? 1 : 0;
|
||||
}
|
||||
if (ret != 52) {
|
||||
HYDRA_ERROR(hs, "Unexpected data report of size %d",
|
||||
ret);
|
||||
return -1;
|
||||
}
|
||||
got_message = true;
|
||||
uint8_t new_counter = buffer[7];
|
||||
bool missed = false;
|
||||
if (hs->report_counter != -1) {
|
||||
uint8_t expected_counter =
|
||||
((hs->report_counter + 1) & 0xff);
|
||||
missed = new_counter != expected_counter;
|
||||
}
|
||||
hs->report_counter = new_counter;
|
||||
|
||||
if (hs->devs[0] != NULL) {
|
||||
hydra_device_parse_controller(hs->devs[0], buffer + 8);
|
||||
}
|
||||
if (hs->devs[1] != NULL) {
|
||||
hydra_device_parse_controller(hs->devs[1], buffer + 30);
|
||||
}
|
||||
|
||||
hs->report_time = now;
|
||||
HYDRA_SPEW(hs,
|
||||
"\n\t"
|
||||
"missed: %s\n\t"
|
||||
"seq_no: %x\n",
|
||||
missed ? "yes" : "no", new_counter);
|
||||
} while (true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Switch to motion controller mode.
|
||||
*/
|
||||
static int
|
||||
hydra_system_enter_motion_control(struct hydra_system *hs, timepoint_ns now)
|
||||
{
|
||||
assert(hs);
|
||||
|
||||
hs->was_in_gamepad_mode = true;
|
||||
hs->motion_attempt_number++;
|
||||
HYDRA_DEBUG(hs,
|
||||
"Setting feature report to start motion-controller mode, "
|
||||
"attempt %d",
|
||||
hs->motion_attempt_number);
|
||||
|
||||
os_hid_set_feature(hs->command_hid, HYDRA_REPORT_START_MOTION,
|
||||
sizeof(HYDRA_REPORT_START_MOTION));
|
||||
|
||||
// Doing a dummy get-feature now.
|
||||
uint8_t buf[91] = {0};
|
||||
os_hid_get_feature(hs->command_hid, 0, buf, sizeof(buf));
|
||||
|
||||
return hydra_sm_transition(&hs->sm,
|
||||
HYDRA_SM_LISTENING_AFTER_SET_FEATURE, now);
|
||||
}
|
||||
/*!
|
||||
* Update the internal state of the Hydra driver.
|
||||
*
|
||||
* Reads devices, checks the state machine and timeouts, etc.
|
||||
*
|
||||
*/
|
||||
static int
|
||||
hydra_system_update(struct hydra_system *hs, struct time_state *timekeeping)
|
||||
{
|
||||
assert(hs);
|
||||
timepoint_ns now = time_state_get_now(timekeeping);
|
||||
|
||||
// In all states of the state machine:
|
||||
// Try reading a report: will only return >0 if we get a full motion
|
||||
// report.
|
||||
int received = hydra_system_read_data_hid(hs, now);
|
||||
|
||||
if (received > 0) {
|
||||
return hydra_sm_transition(&hs->sm, HYDRA_SM_REPORTING, now);
|
||||
}
|
||||
|
||||
|
||||
switch (hs->sm.current_state) {
|
||||
|
||||
case HYDRA_SM_LISTENING_AFTER_CONNECT: {
|
||||
float state_duration_s =
|
||||
hydra_sm_seconds_since_transition(&hs->sm, now);
|
||||
if (state_duration_s > 1.0f) {
|
||||
// only waiting 1 second for the initial report after
|
||||
// connect
|
||||
hydra_system_enter_motion_control(hs, now);
|
||||
}
|
||||
} break;
|
||||
|
||||
case HYDRA_SM_LISTENING_AFTER_SET_FEATURE: {
|
||||
float state_duration_s =
|
||||
hydra_sm_seconds_since_transition(&hs->sm, now);
|
||||
if (state_duration_s > 5.0f) {
|
||||
// giving each motion control attempt 5 seconds to work.
|
||||
hydra_system_enter_motion_control(hs, now);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
hydra_device_update_input_click(struct hydra_device *hd,
|
||||
timepoint_ns now,
|
||||
int index,
|
||||
uint32_t bit)
|
||||
{
|
||||
assert(hd);
|
||||
hd->base.inputs[index].timestamp = now;
|
||||
hd->base.inputs[index].value.boolean = (hd->state.buttons & bit) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Device functions.
|
||||
*
|
||||
*/
|
||||
|
||||
static void
|
||||
hydra_device_update_inputs(struct xrt_device *xdev,
|
||||
struct time_state *timekeeping)
|
||||
{
|
||||
struct hydra_device *hd = hydra_device(xdev);
|
||||
struct hydra_system *hs = hydra_system(xdev->tracking);
|
||||
|
||||
hydra_system_update(hs, timekeeping);
|
||||
|
||||
if (hd->input_time != hs->report_time) {
|
||||
timepoint_ns now = hs->report_time;
|
||||
hd->input_time = now;
|
||||
|
||||
hydra_device_update_input_click(hd, now, HYDRA_INDEX_1_CLICK,
|
||||
HYDRA_BUTTON_BIT_1);
|
||||
hydra_device_update_input_click(hd, now, HYDRA_INDEX_2_CLICK,
|
||||
HYDRA_BUTTON_BIT_2);
|
||||
hydra_device_update_input_click(hd, now, HYDRA_INDEX_3_CLICK,
|
||||
HYDRA_BUTTON_BIT_3);
|
||||
hydra_device_update_input_click(hd, now, HYDRA_INDEX_4_CLICK,
|
||||
HYDRA_BUTTON_BIT_4);
|
||||
|
||||
hydra_device_update_input_click(
|
||||
hd, now, HYDRA_INDEX_MIDDLE_CLICK, HYDRA_BUTTON_BIT_MIDDLE);
|
||||
hydra_device_update_input_click(
|
||||
hd, now, HYDRA_INDEX_BUMPER_CLICK, HYDRA_BUTTON_BIT_BUMPER);
|
||||
hydra_device_update_input_click(hd, now,
|
||||
HYDRA_INDEX_JOYSTICK_CLICK,
|
||||
HYDRA_INDEX_JOYSTICK_CLICK);
|
||||
|
||||
struct xrt_input *inputs = hd->base.inputs;
|
||||
struct hydra_controller_state *state = &(hd->state);
|
||||
|
||||
inputs[HYDRA_INDEX_JOYSTICK_VALUE].timestamp = now;
|
||||
inputs[HYDRA_INDEX_JOYSTICK_VALUE].value.vec2 = state->js;
|
||||
|
||||
inputs[HYDRA_INDEX_TRIGGER_VALUE].timestamp = now;
|
||||
inputs[HYDRA_INDEX_TRIGGER_VALUE].value.vec1.x = state->trigger;
|
||||
|
||||
|
||||
//! @todo report pose
|
||||
// inputs[HYDRA_INDEX_POSE].timestamp = now;
|
||||
// inputs[HYDRA_INDEX_POSE].value.
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hydra_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 hydra_device *hd = hydra_device(xdev);
|
||||
struct hydra_system *hs = hydra_system(xdev->tracking);
|
||||
|
||||
hydra_system_update(hs, timekeeping);
|
||||
|
||||
*out_timestamp = hs->report_time;
|
||||
out_relation->pose = hd->state.pose;
|
||||
|
||||
//! @todo how do we report this is not (necessarily) the same base space
|
||||
//! as the HMD?
|
||||
out_relation->relation_flags = (enum xrt_space_relation_flags)(
|
||||
XRT_SPACE_RELATION_POSITION_VALID_BIT |
|
||||
XRT_SPACE_RELATION_POSITION_TRACKED_BIT |
|
||||
XRT_SPACE_RELATION_ORIENTATION_VALID_BIT |
|
||||
XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT);
|
||||
// struct xrt_vec3 pos = out_relation->pose.position;
|
||||
// struct xrt_quat quat = out_relation->pose.orientation;
|
||||
// HYDRA_SPEW(hd, "GET_TRACKED_POSE (%f, %f, %f) (%f, %f, %f, %f) ",
|
||||
// pos.x,
|
||||
// pos.y, pos.z, quat.x, quat.y, quat.z, quat.w);
|
||||
}
|
||||
|
||||
static void
|
||||
hydra_system_remove_child(struct hydra_system *hs, struct hydra_device *hd)
|
||||
{
|
||||
assert(hydra_system(hd->base.tracking) == hs);
|
||||
assert(hd->index == 0 || hd->index == 1);
|
||||
|
||||
// Make the device not point to the system
|
||||
hd->sys = NULL;
|
||||
|
||||
// Make the system not point to the device
|
||||
assert(hs->devs[hd->index] == hd);
|
||||
hs->devs[hd->index] = NULL;
|
||||
|
||||
// Decrease ref count of system
|
||||
hs->refs--;
|
||||
|
||||
if (hs->refs == 0) {
|
||||
// No more children, destroy system.
|
||||
if (hs->data_hid != NULL && hs->command_hid != NULL &&
|
||||
hs->sm.current_state == HYDRA_SM_REPORTING &&
|
||||
hs->was_in_gamepad_mode) {
|
||||
|
||||
HYDRA_DEBUG(
|
||||
hs,
|
||||
"hydra: Sending command to re-enter gamepad mode "
|
||||
"and pausing while it takes effect.");
|
||||
|
||||
os_hid_set_feature(hs->command_hid,
|
||||
HYDRA_REPORT_START_GAMEPAD,
|
||||
sizeof(HYDRA_REPORT_START_GAMEPAD));
|
||||
sleep(2);
|
||||
}
|
||||
if (hs->data_hid != NULL) {
|
||||
os_hid_destroy(hs->data_hid);
|
||||
hs->data_hid = NULL;
|
||||
}
|
||||
if (hs->command_hid != NULL) {
|
||||
os_hid_destroy(hs->command_hid);
|
||||
hs->command_hid = NULL;
|
||||
}
|
||||
free(hs);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hydra_device_destroy(struct xrt_device *xdev)
|
||||
{
|
||||
struct hydra_device *hd = hydra_device(xdev);
|
||||
struct hydra_system *hs = hydra_system(xdev->tracking);
|
||||
|
||||
hydra_system_remove_child(hs, hd);
|
||||
|
||||
free(hd);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Prober functions.
|
||||
*
|
||||
*/
|
||||
#define SET_INPUT(NAME) \
|
||||
do { \
|
||||
(hd->base.inputs[HYDRA_INDEX_##NAME].name = \
|
||||
XRT_INPUT_HYDRA_##NAME); \
|
||||
} while (0)
|
||||
|
||||
int
|
||||
hydra_found(struct xrt_prober *xp,
|
||||
struct xrt_prober_device **devices,
|
||||
size_t index,
|
||||
struct xrt_device **out_xdevs)
|
||||
{
|
||||
struct xrt_prober_device *dev = devices[index];
|
||||
int ret;
|
||||
|
||||
struct os_hid_device *data_hid = NULL;
|
||||
ret = xp->open_hid_interface(xp, dev, 0, &data_hid);
|
||||
if (ret != 0) {
|
||||
return -1;
|
||||
}
|
||||
struct os_hid_device *command_hid = NULL;
|
||||
ret = xp->open_hid_interface(xp, dev, 1, &command_hid);
|
||||
if (ret != 0) {
|
||||
data_hid->destroy(data_hid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create the system
|
||||
struct hydra_system *hs = U_TYPED_CALLOC(struct hydra_system);
|
||||
hs->base.type = XRT_TRACKING_TYPE_NONE; // ??
|
||||
snprintf(hs->base.name, XRT_TRACKING_NAME_LEN, "%s",
|
||||
"Razer Hydra magnetic tracking");
|
||||
// Dummy transform from local space to base.
|
||||
hs->base.offset.position.y = 1.0f;
|
||||
hs->base.offset.position.z = -0.25f;
|
||||
hs->base.offset.orientation.w = 1.0f;
|
||||
|
||||
hs->data_hid = data_hid;
|
||||
hs->command_hid = command_hid;
|
||||
|
||||
enum u_device_alloc_flags flags = (enum u_device_alloc_flags)0;
|
||||
hs->devs[0] = U_DEVICE_ALLOCATE(struct hydra_device, flags, 10, 0);
|
||||
hs->devs[1] = U_DEVICE_ALLOCATE(struct hydra_device, flags, 10, 0);
|
||||
|
||||
// Populate the "tracking" member with the system.
|
||||
hs->devs[0]->base.tracking = &(hs->base);
|
||||
hs->devs[1]->base.tracking = &(hs->base);
|
||||
|
||||
hs->report_counter = -1;
|
||||
hs->refs = 2;
|
||||
|
||||
hs->print_spew = debug_get_bool_option_hydra_spew();
|
||||
hs->print_debug = debug_get_bool_option_hydra_debug();
|
||||
|
||||
// Populate the individual devices
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
struct hydra_device *hd = hs->devs[i];
|
||||
|
||||
hd->base.destroy = hydra_device_destroy;
|
||||
hd->base.update_inputs = hydra_device_update_inputs;
|
||||
hd->base.get_tracked_pose = hydra_device_get_tracked_pose;
|
||||
// hs->base.set_output = hydra_device_set_output;
|
||||
snprintf(hd->base.name, XRT_DEVICE_NAME_LEN, "%s %i",
|
||||
"Razer Hydra Controller", (int)(i + 1));
|
||||
SET_INPUT(1_CLICK);
|
||||
SET_INPUT(2_CLICK);
|
||||
SET_INPUT(3_CLICK);
|
||||
SET_INPUT(4_CLICK);
|
||||
SET_INPUT(MIDDLE_CLICK);
|
||||
SET_INPUT(BUMPER_CLICK);
|
||||
SET_INPUT(JOYSTICK_CLICK);
|
||||
SET_INPUT(JOYSTICK_VALUE);
|
||||
SET_INPUT(TRIGGER_VALUE);
|
||||
SET_INPUT(POSE);
|
||||
hd->index = i;
|
||||
hd->sys = hs;
|
||||
hd->print_spew = hs->print_spew;
|
||||
hd->print_debug = hs->print_debug;
|
||||
|
||||
out_xdevs[i] = &(hd->base);
|
||||
}
|
||||
|
||||
printf("Opened razer hydra!\n");
|
||||
return 2;
|
||||
}
|
48
src/xrt/drivers/hydra/hydra_interface.h
Normal file
48
src/xrt/drivers/hydra/hydra_interface.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2019, Collabora, Ltd.
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Interface to @ref drv_hydra
|
||||
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||
* @ingroup drv_hydra
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*!
|
||||
* @defgroup drv_hydra Razer Hydra Driver
|
||||
* @ingroup drv
|
||||
*
|
||||
* @brief Driver for the Razer Hydra motion controllers.
|
||||
*/
|
||||
|
||||
|
||||
#define HYDRA_VID 0x1532
|
||||
#define HYDRA_PID 0x0300
|
||||
|
||||
/*!
|
||||
* Probing function for Razer Hydra devices.
|
||||
*
|
||||
* @ingroup drv_hydra
|
||||
*/
|
||||
int
|
||||
hydra_found(struct xrt_prober *xp,
|
||||
struct xrt_prober_device **devices,
|
||||
size_t index,
|
||||
struct xrt_device **out_xdevs);
|
||||
|
||||
|
||||
/*!
|
||||
* @dir drivers/hydra
|
||||
*
|
||||
* @brief @ref drv_hydra files.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -270,6 +270,17 @@ enum xrt_input_name
|
|||
XRT_INPUT_PSMV_BODY_CENTER_POSE = XRT_INPUT_NAME(0x0029, POSE),
|
||||
XRT_INPUT_PSMV_BALL_CENTER_POSE = XRT_INPUT_NAME(0x002A, POSE),
|
||||
XRT_INPUT_PSMV_BALL_TIP_POSE = XRT_INPUT_NAME(0x002B, POSE),
|
||||
|
||||
XRT_INPUT_HYDRA_1_CLICK = XRT_INPUT_NAME(0x0030, BOOLEAN),
|
||||
XRT_INPUT_HYDRA_2_CLICK = XRT_INPUT_NAME(0x0031, BOOLEAN),
|
||||
XRT_INPUT_HYDRA_3_CLICK = XRT_INPUT_NAME(0x0032, BOOLEAN),
|
||||
XRT_INPUT_HYDRA_4_CLICK = XRT_INPUT_NAME(0x0033, BOOLEAN),
|
||||
XRT_INPUT_HYDRA_MIDDLE_CLICK = XRT_INPUT_NAME(0x0034, BOOLEAN),
|
||||
XRT_INPUT_HYDRA_BUMPER_CLICK = XRT_INPUT_NAME(0x0035, BOOLEAN),
|
||||
XRT_INPUT_HYDRA_JOYSTICK_CLICK = XRT_INPUT_NAME(0x0036, BOOLEAN),
|
||||
XRT_INPUT_HYDRA_JOYSTICK_VALUE = XRT_INPUT_NAME(0x0037, VEC2_MINUS_ONE_TO_ONE),
|
||||
XRT_INPUT_HYDRA_TRIGGER_VALUE = XRT_INPUT_NAME(0x0038, VEC1_ZERO_TO_ONE),
|
||||
XRT_INPUT_HYDRA_POSE = XRT_INPUT_NAME(0x0039, POSE),
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
|
|
|
@ -24,6 +24,10 @@ if(BUILD_DRIVER_HDK)
|
|||
list(APPEND DRIVER_LIBRARIES ${HIDAPI_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(BUILD_DRIVER_HYDRA)
|
||||
list(APPEND DRIVER_OBJECTS $<TARGET_OBJECTS:drv_hydra>)
|
||||
endif()
|
||||
|
||||
if(BUILD_DRIVER_OHMD)
|
||||
set(XRT_BUILD_OHMD TRUE)
|
||||
list(APPEND DRIVER_OBJECTS $<TARGET_OBJECTS:drv_ohmd>)
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
#include "psvr/psvr_interface.h"
|
||||
#endif
|
||||
|
||||
#ifdef XRT_BUILD_HYDRA
|
||||
#include "hydra/hydra_interface.h"
|
||||
#endif
|
||||
|
||||
|
||||
/*!
|
||||
* Each entry should be a vendor ID (VID), product ID (PID), a "found" function,
|
||||
|
@ -48,6 +52,11 @@ struct xrt_prober_entry target_entry_list[] = {
|
|||
#ifdef XRT_BUILD_PSMV
|
||||
{PSMV_VID, PSMV_PID, psmv_found, "PS Move"},
|
||||
#endif
|
||||
|
||||
#ifdef XRT_BUILD_HYDRA
|
||||
{HYDRA_VID, HYDRA_PID, hydra_found, "Razer Hydra"},
|
||||
#endif
|
||||
|
||||
{0x0000, 0x0000, NULL, NULL}, // Terminate
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue