d/wmr: Do device creation via builder interface

This commit is contained in:
Jakob Bornecrantz 2023-01-01 21:48:06 +00:00
parent 56dd75c14d
commit 2ddf868735
3 changed files with 658 additions and 0 deletions
src/xrt

View file

@ -13,6 +13,8 @@
#include "xrt/xrt_prober.h"
#include "wmr_common.h"
#ifdef __cplusplus
extern "C" {
@ -33,6 +35,121 @@ extern "C" {
*/
/*
*
* Builder interface.
*
*/
/*!
* Results from searching for host attached Bluetooth controllers.
*
* @ingroup drv_wmr
*/
struct wmr_bt_controllers_search_results
{
struct xrt_prober_device *left;
struct xrt_prober_device *right;
};
/*!
* Search for a left and right pair of Windows Mixed Reality controllers, groups
* them by type (Classic/Odyssey/G2). Preferring Odyssey over Classic. Will mix
* types in order to get a complete left and right pair if need be, but prefers
* matching types first. G2 currently not supported.
*
* @ingroup drv_wmr
*/
void
wmr_find_bt_controller_pair(struct xrt_prober *xp,
struct xrt_prober_device **xpdevs,
size_t xpdev_count,
enum u_logging_level log_level,
struct wmr_bt_controllers_search_results *out_wbtcsr);
/*!
* Results from searching for a companion device. Doctor?
*
* @ingroup drv_wmr
*/
struct wmr_companion_search_results
{
struct xrt_prober_device *xpdev_companion;
enum wmr_headset_type type;
};
/*!
* Searches for the the list of xpdevs for the companion device of a holo lens
* device.
*
* @ingroup drv_wmr
*/
void
wmr_find_companion_device(struct xrt_prober *xp,
struct xrt_prober_device **xpdevs,
size_t xpdev_count,
enum u_logging_level log_level,
struct xrt_prober_device *xpdev_holo,
struct wmr_companion_search_results *out_wcsr);
/*!
* Results from searching for a headset.
*
* @ingroup drv_wmr
*/
struct wmr_headset_search_results
{
struct xrt_prober_device *xpdev_holo;
struct xrt_prober_device *xpdev_companion;
enum wmr_headset_type type;
};
/*!
* Find a headsets.
*
* @ingroup drv_wmr
*/
void
wmr_find_headset(struct xrt_prober *xp,
struct xrt_prober_device **xpdevs,
size_t xpdev_count,
enum u_logging_level log_level,
struct wmr_headset_search_results *out_whsr);
/*
*
* Creation extensions.
*
*/
/*!
* Creates a WMR headset with the given devices and of headset type.
*
* @ingroup drv_wmr
*/
xrt_result_t
wmr_create_headset(struct xrt_prober *xp,
struct xrt_prober_device *xpdev_holo,
struct xrt_prober_device *xpdev_companion,
enum wmr_headset_type type,
enum u_logging_level log_level,
struct xrt_device **out_hmd,
struct xrt_device **out_left,
struct xrt_device **out_right);
/*!
* Creates a WMR BT controller device.
*
* @ingroup drv_wmr
*/
xrt_result_t
wmr_create_bt_controller(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
enum u_logging_level log_level,
struct xrt_device **out_xdev);
/*
*
* Found functions.

View file

@ -13,6 +13,8 @@
#include "xrt/xrt_config_drivers.h"
#include "xrt/xrt_prober.h"
#include "os/os_hid.h"
#include "util/u_misc.h"
#include "util/u_debug.h"
#include "util/u_prober.h"
@ -48,6 +50,37 @@ DEBUG_GET_ONCE_LOG_OPTION(wmr_log, "WMR_LOG", U_LOGGING_INFO)
*
*/
static bool
is_left(const char *product_name, size_t size)
{
return strncmp(product_name, WMR_CONTROLLER_LEFT_PRODUCT_STRING, size) == 0;
}
static bool
is_right(const char *product_name, size_t size)
{
return strncmp(product_name, WMR_CONTROLLER_RIGHT_PRODUCT_STRING, size) == 0;
}
static void
classify_and_assign_controller(struct xrt_prober *xp,
struct xrt_prober_device *xpd,
struct wmr_bt_controllers_search_results *ctrls)
{
char buf[256] = {0};
int result = xrt_prober_get_string_descriptor(xp, xpd, XRT_PROBER_STRING_PRODUCT, (uint8_t *)buf, sizeof(buf));
if (result <= 0) {
U_LOG_E("xrt_prober_get_string_descriptor: %i\n\tFailed to get product string!", result);
return;
}
if (is_left(buf, sizeof(buf))) {
ctrls->left = xpd;
} else if (is_right(buf, sizeof(buf))) {
ctrls->right = xpd;
}
}
static bool
check_and_get_interface(struct xrt_prober_device *device,
enum u_logging_level log_level,
@ -144,6 +177,266 @@ find_companion_device(struct xrt_prober *xp,
}
/*
*
* 'Exported' builder functions.
*
*/
void
wmr_find_bt_controller_pair(struct xrt_prober *xp,
struct xrt_prober_device **devices,
size_t device_count,
enum u_logging_level log_level,
struct wmr_bt_controllers_search_results *out_wbtcsr)
{
// Try to pair controllers of the same type.
struct wmr_bt_controllers_search_results odyssey_ctrls = {0};
struct wmr_bt_controllers_search_results wmr_ctrls = {0};
for (size_t i = 0; i < device_count; i++) {
struct xrt_prober_device *xpd = devices[i];
// All controllers have the Microsoft vendor ID.
if (xpd->vendor_id != MICROSOFT_VID) {
continue;
}
// Only handle Bluetooth connected controllers here.
if (xpd->bus != XRT_BUS_TYPE_BLUETOOTH) {
continue;
}
if (xpd->product_id == WMR_CONTROLLER_PID) {
classify_and_assign_controller(xp, xpd, &wmr_ctrls);
} else if (xpd->product_id == ODYSSEY_CONTROLLER_PID) {
classify_and_assign_controller(xp, xpd, &odyssey_ctrls);
} else if (xpd->product_id == REVERB_G2_CONTROLLER_PID) {
U_LOG_W(
"Reverb G2 controller connected to host BT controller, correnntly not supported, "
"skipping!");
}
}
// We have to prefer one type pair, prefer Odyssey.
if (odyssey_ctrls.left != NULL && odyssey_ctrls.right != NULL) {
*out_wbtcsr = odyssey_ctrls;
return;
}
// Other type pair.
if (wmr_ctrls.left != NULL && wmr_ctrls.right != NULL) {
*out_wbtcsr = wmr_ctrls;
return;
}
// Grab any of them.
out_wbtcsr->left = odyssey_ctrls.left != NULL ? odyssey_ctrls.left : wmr_ctrls.left;
out_wbtcsr->right = odyssey_ctrls.right != NULL ? odyssey_ctrls.right : wmr_ctrls.right;
}
void
wmr_find_companion_device(struct xrt_prober *xp,
struct xrt_prober_device **xpdevs,
size_t xpdev_count,
enum u_logging_level log_level,
struct xrt_prober_device *xpdev_holo,
struct wmr_companion_search_results *out_wcsr)
{
struct xrt_prober_device *xpdev_companion = NULL;
enum wmr_headset_type type = WMR_HEADSET_GENERIC;
if (!find_companion_device(xp, xpdevs, xpdev_count, log_level, &type, &xpdev_companion)) {
U_LOG_IFL_E(log_level, "Did not find HoloLens Sensors' companion device");
return;
}
out_wcsr->xpdev_companion = xpdev_companion;
out_wcsr->type = type;
}
void
wmr_find_headset(struct xrt_prober *xp,
struct xrt_prober_device **xpdevs,
size_t xpdev_count,
enum u_logging_level log_level,
struct wmr_headset_search_results *out_whsr)
{
struct wmr_companion_search_results wcsr = {0};
struct xrt_prober_device *xpdev_holo = NULL;
for (size_t i = 0; i < xpdev_count; i++) {
struct xrt_prober_device *xpd = xpdevs[i];
// Only handle Bluetooth connected controllers here.
if (xpd->bus != XRT_BUS_TYPE_USB) {
continue;
}
if (xpd->vendor_id != MICROSOFT_VID || xpd->product_id != HOLOLENS_SENSORS_PID) {
continue;
}
xpdev_holo = xpd;
break;
}
// Did we find any?
if (xpdev_holo == NULL) {
U_LOG_IFL_D(log_level, "Did not find HoloLens Sensors device, no headset connected?");
return; // Didn't find any hololense device, not an error.
}
// Find the companion device.
wmr_find_companion_device(xp, xpdevs, xpdev_count, log_level, xpdev_holo, &wcsr);
if (wcsr.xpdev_companion == NULL) {
U_LOG_IFL_E(log_level, "Found a HoloLens device, but not it's companion device");
return;
}
// Done now, output.
out_whsr->xpdev_holo = xpdev_holo;
out_whsr->xpdev_companion = wcsr.xpdev_companion;
out_whsr->type = wcsr.type;
}
/*
*
* 'Exported' create functions.
*
*/
xrt_result_t
wmr_create_headset(struct xrt_prober *xp,
struct xrt_prober_device *xpdev_holo,
struct xrt_prober_device *xpdev_companion,
enum wmr_headset_type type,
enum u_logging_level log_level,
struct xrt_device **out_hmd,
struct xrt_device **out_left,
struct xrt_device **out_right)
{
DRV_TRACE_MARKER();
U_LOG_IFL_D(log_level, "Creating headset.");
const int interface_holo = 2;
const int interface_companion = 0;
int ret;
struct os_hid_device *hid_holo = NULL;
ret = xrt_prober_open_hid_interface(xp, xpdev_holo, interface_holo, &hid_holo);
if (ret != 0) {
U_LOG_IFL_E(log_level, "Failed to open HoloLens Sensors HID interface");
return XRT_ERROR_DEVICE_CREATION_FAILED;
}
struct os_hid_device *hid_companion = NULL;
ret = xrt_prober_open_hid_interface(xp, xpdev_companion, interface_companion, &hid_companion);
if (ret != 0) {
U_LOG_IFL_E(log_level, "Failed to open HoloLens Sensors' companion HID interface.");
goto error_holo;
}
struct xrt_device *hmd = NULL;
struct xrt_device *ht = NULL;
struct xrt_device *two_hands[2] = {NULL, NULL}; // Must initialize, always returned.
wmr_hmd_create(type, hid_holo, hid_companion, xpdev_holo, log_level, &hmd, &ht);
if (hmd == NULL) {
U_LOG_IFL_E(log_level, "Failed to create WMR HMD device.");
goto error_companion;
}
#ifdef XRT_BUILD_DRIVER_HANDTRACKING
if (ht != NULL) { // Create hand-tracked controllers
cemu_devices_create(hmd, ht, two_hands);
}
#endif
*out_hmd = hmd;
*out_left = two_hands[0];
*out_right = two_hands[1];
return XRT_SUCCESS;
error_companion:
os_hid_destroy(hid_companion);
error_holo:
os_hid_destroy(hid_holo);
return XRT_ERROR_DEVICE_CREATION_FAILED;
}
xrt_result_t
wmr_create_bt_controller(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
enum u_logging_level log_level,
struct xrt_device **out_xdev)
{
DRV_TRACE_MARKER();
U_LOG_IFL_D(log_level, "Creating Bluetooth controller.");
struct os_hid_device *hid_controller = NULL;
// Only handle Bluetooth connected controllers here.
if (xpdev->bus != XRT_BUS_TYPE_BLUETOOTH) {
U_LOG_IFL_E(log_level, "Got a non Bluetooth device!");
return XRT_ERROR_DEVICE_CREATION_FAILED;
}
char product_name[XRT_DEVICE_PRODUCT_NAME_LEN] = {0};
int ret = xrt_prober_get_string_descriptor( //
xp, //
xpdev, //
XRT_PROBER_STRING_PRODUCT, //
(uint8_t *)product_name, //
sizeof(product_name)); //
enum xrt_device_type controller_type = XRT_DEVICE_TYPE_UNKNOWN;
const int interface_controller = 0;
switch (xpdev->product_id) {
case WMR_CONTROLLER_PID:
case ODYSSEY_CONTROLLER_PID:
case REVERB_G2_CONTROLLER_PID:
if (is_left(product_name, sizeof(product_name))) {
controller_type = XRT_DEVICE_TYPE_LEFT_HAND_CONTROLLER;
break;
} else if (is_right(product_name, sizeof(product_name))) {
controller_type = XRT_DEVICE_TYPE_RIGHT_HAND_CONTROLLER;
break;
}
// else fall through
default:
U_LOG_IFL_E(log_level,
"Unsupported controller device (Bluetooth): vid: 0x%04X, pid: 0x%04X, Product Name: '%s'",
xpdev->vendor_id, xpdev->product_id, product_name);
return XRT_ERROR_DEVICE_CREATION_FAILED;
}
ret = xrt_prober_open_hid_interface(xp, xpdev, interface_controller, &hid_controller);
if (ret != 0) {
U_LOG_IFL_E(log_level, "Failed to open WMR Bluetooth controller's HID interface");
return XRT_ERROR_DEVICE_CREATION_FAILED;
}
struct xrt_device *xdev = wmr_bt_controller_create(hid_controller, controller_type, log_level);
if (xdev == NULL) {
U_LOG_IFL_E(log_level, "Failed to create WMR controller (Bluetooth)");
os_hid_destroy(hid_controller);
return XRT_ERROR_DEVICE_CREATION_FAILED;
}
*out_xdev = xdev;
return XRT_SUCCESS;
}
/*
*
* 'Exported' found functions.

View file

@ -11,13 +11,17 @@
#include "xrt/xrt_prober.h"
#include "util/u_misc.h"
#include "util/u_debug.h"
#include "util/u_logging.h"
#include "util/u_builders.h"
#include "util/u_config_json.h"
#include "util/u_pretty_print.h"
#include "util/u_space_overseer.h"
#include "util/u_system_helpers.h"
#include "target_builder_interface.h"
#include "wmr/wmr_common.h"
#include "wmr/wmr_interface.h"
#include <assert.h>
@ -26,6 +30,8 @@
#error "Must only be built with XRT_BUILD_DRIVER_WMR set"
#endif
DEBUG_GET_ONCE_LOG_OPTION(wmr_log, "WMR_LOG", U_LOGGING_INFO)
/*
*
@ -37,6 +43,52 @@ static const char *driver_list[] = {
"wmr",
};
static void
print_hmd(u_pp_delegate_t dg,
const char *prefix,
enum wmr_headset_type type,
struct xrt_prober_device *xpdev_holo,
struct xrt_prober_device *xpdev_companion)
{
u_pp(dg, "\n\t%s: ", prefix);
if (xpdev_holo == NULL || xpdev_companion == NULL) {
u_pp(dg, "None");
return;
}
struct xrt_prober_device *c = xpdev_companion;
switch (type) {
case WMR_HEADSET_GENERIC: u_pp(dg, "Generic"); break;
case WMR_HEADSET_HP_VR1000: u_pp(dg, "HP VR1000"); break;
case WMR_HEADSET_REVERB_G1: u_pp(dg, "Reverb G1"); break;
case WMR_HEADSET_REVERB_G2: u_pp(dg, "Reverb G2"); break;
case WMR_HEADSET_SAMSUNG_XE700X3AI: u_pp(dg, "Samsung XE700X3AI"); break;
case WMR_HEADSET_SAMSUNG_800ZAA: u_pp(dg, "Samsung 800ZAA"); break;
case WMR_HEADSET_LENOVO_EXPLORER: u_pp(dg, "Lenovo Explorer"); break;
case WMR_HEADSET_MEDION_ERAZER_X1000: u_pp(dg, "Medion Erazer X1000"); break;
default: u_pp(dg, "Unknown (VID: %04x, PID: 0x%04x)", c->vendor_id, c->product_id); break;
}
}
static void
print_ctrl(u_pp_delegate_t dg, const char *prefix, struct xrt_prober_device *xpdev)
{
u_pp(dg, "\n\t%s: ", prefix);
if (xpdev == NULL) {
u_pp(dg, "None");
return;
}
switch (xpdev->product_id) {
case WMR_CONTROLLER_PID: u_pp(dg, "WinMR Controller"); break;
case ODYSSEY_CONTROLLER_PID: u_pp(dg, "Odyssey Controller"); break;
default: u_pp(dg, "Unknown (VID: %04x, PID: 0x%04x)", xpdev->vendor_id, xpdev->product_id); break;
}
}
/*
*
@ -50,6 +102,76 @@ wmr_estimate_system(struct xrt_builder *xb,
struct xrt_prober *xp,
struct xrt_builder_estimate *out_estimate)
{
enum u_logging_level log_level = debug_get_log_option_wmr_log();
struct wmr_bt_controllers_search_results ctrls = {0};
struct wmr_headset_search_results whsr = {0};
struct xrt_builder_estimate estimate = {0};
struct xrt_prober_device **xpdevs = NULL;
size_t xpdev_count = 0;
xrt_result_t xret = XRT_SUCCESS;
/*
* Pre device looking stuff.
*/
// Lock the device list
xret = xrt_prober_lock_list(xp, &xpdevs, &xpdev_count);
if (xret != XRT_SUCCESS) {
return xret;
}
/*
* Search for devices.
*/
wmr_find_headset(xp, xpdevs, xpdev_count, log_level, &whsr);
wmr_find_bt_controller_pair(xp, xpdevs, xpdev_count, log_level, &ctrls);
if (log_level >= U_LOGGING_DEBUG) {
struct u_pp_sink_stack_only sink;
u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink);
u_pp(dg, "Found:");
print_hmd(dg, "head", whsr.type, whsr.xpdev_holo, whsr.xpdev_companion);
print_ctrl(dg, "left", ctrls.left);
print_ctrl(dg, "right", ctrls.right);
U_LOG_IFL_D(log_level, "%s", sink.buffer);
}
/*
* Tidy.
*/
xret = xrt_prober_unlock_list(xp, &xpdevs);
assert(xret == XRT_SUCCESS);
/*
* Fill out estimate.
*/
if (whsr.xpdev_holo != NULL && whsr.xpdev_companion != NULL) {
estimate.certain.head = true;
if (whsr.type == WMR_HEADSET_REVERB_G2) {
estimate.maybe.left = true;
estimate.maybe.right = true;
}
}
if (ctrls.left != NULL) {
estimate.certain.left = true;
}
if (ctrls.right != NULL) {
estimate.certain.right = true;
}
*out_estimate = estimate;
return XRT_SUCCESS;
}
@ -60,7 +182,133 @@ wmr_open_system(struct xrt_builder *xb,
struct xrt_system_devices **out_xsysd,
struct xrt_space_overseer **out_xso)
{
enum u_logging_level log_level = debug_get_log_option_wmr_log();
struct wmr_bt_controllers_search_results ctrls = {0};
struct wmr_headset_search_results whsr = {0};
struct xrt_prober_device **xpdevs = NULL;
size_t xpdev_count = 0;
xrt_result_t xret_unlock = XRT_SUCCESS;
xrt_result_t xret = XRT_SUCCESS;
/*
* Pre device looking stuff.
*/
// Lock the device list
xret = xrt_prober_lock_list(xp, &xpdevs, &xpdev_count);
if (xret != XRT_SUCCESS) {
return xret;
}
/*
* Search for devices.
*/
wmr_find_headset(xp, xpdevs, xpdev_count, log_level, &whsr);
wmr_find_bt_controller_pair(xp, xpdevs, xpdev_count, log_level, &ctrls);
/*
* Validation.
*/
if (whsr.xpdev_holo == NULL || whsr.xpdev_companion == NULL) {
U_LOG_IFL_E(log_level, "Could not find headset devices! (holo %p, companion %p)",
(void *)whsr.xpdev_holo, (void *)whsr.xpdev_companion);
xret = XRT_ERROR_DEVICE_CREATION_FAILED;
goto error;
}
/*
* Creation.
*/
struct xrt_device *head = NULL;
struct xrt_device *left = NULL;
struct xrt_device *right = NULL;
xret = wmr_create_headset( //
xp, //
whsr.xpdev_holo, //
whsr.xpdev_companion, //
whsr.type, //
log_level, //
&head, //
&left, //
&right); //
if (xret != XRT_SUCCESS) {
goto error;
}
if (left == NULL && ctrls.left != NULL) {
xret = wmr_create_bt_controller(xp, ctrls.left, log_level, &left);
if (xret != XRT_SUCCESS) {
goto error;
}
}
if (right == NULL && ctrls.right != NULL) {
xret = wmr_create_bt_controller(xp, ctrls.right, log_level, &right);
if (xret != XRT_SUCCESS) {
goto error;
}
}
/*
* Tidy
*/
xret_unlock = xrt_prober_unlock_list(xp, &xpdevs);
assert(xret_unlock == XRT_SUCCESS);
struct u_system_devices *usysd = u_system_devices_allocate();
usysd->base.roles.head = head;
usysd->base.roles.left = left;
usysd->base.roles.right = right;
usysd->base.xdevs[usysd->base.xdev_count++] = head;
if (left != NULL) {
usysd->base.xdevs[usysd->base.xdev_count++] = left;
}
if (right != NULL) {
usysd->base.xdevs[usysd->base.xdev_count++] = right;
}
// Find hand tracking devices.
usysd->base.roles.hand_tracking.left =
u_system_devices_get_ht_device(usysd, XRT_INPUT_GENERIC_HAND_TRACKING_LEFT);
usysd->base.roles.hand_tracking.right =
u_system_devices_get_ht_device(usysd, XRT_INPUT_GENERIC_HAND_TRACKING_RIGHT);
// Create space overseer last once all devices set.
struct xrt_space_overseer *xso = NULL;
u_builder_create_space_overseer(&usysd->base, &xso);
assert(xso != NULL);
/*
* Output.
*/
*out_xsysd = &usysd->base;
*out_xso = xso;
return XRT_SUCCESS;
error:
xrt_device_destroy(&head);
xrt_device_destroy(&left);
xrt_device_destroy(&right);
xret_unlock = xrt_prober_unlock_list(xp, &xpdevs);
assert(xret_unlock == XRT_SUCCESS);
return xret;
}
static void