The open source OpenXR runtime
at main 471 lines 14 kB view raw
1// Copyright 2020-2021, N Madsen. 2// Copyright 2020-2022, Collabora, Ltd. 3// SPDX-License-Identifier: BSL-1.0 4/*! 5 * @file 6 * @brief WMR prober code. 7 * @author Nis Madsen <nima_zero_one@protonmail.com> 8 * @author Jakob Bornecrantz <jakob@collabora.com> 9 * @author Nova King <technobaboo@proton.me> 10 * @ingroup drv_wmr 11 */ 12 13#include "xrt/xrt_config_drivers.h" 14#include "xrt/xrt_prober.h" 15 16#include "os/os_hid.h" 17 18#include "util/u_misc.h" 19#include "util/u_debug.h" 20#include "util/u_prober.h" 21#include "util/u_logging.h" 22#include "util/u_trace_marker.h" 23 24#include "wmr_interface.h" 25#include "wmr_hmd.h" 26#include "wmr_bt_controller.h" 27#include "wmr_common.h" 28 29#include <stdio.h> 30#include <stdlib.h> 31#include <wchar.h> 32 33#ifdef XRT_BUILD_DRIVER_HANDTRACKING 34#include "../multi_wrapper/multi.h" 35#include "../ht_ctrl_emu/ht_ctrl_emu_interface.h" 36#endif 37 38 39/* 40 * 41 * Functions. 42 * 43 */ 44 45static bool 46is_left(const char *product_name, size_t size) 47{ 48 return strncmp(product_name, WMR_CONTROLLER_LEFT_PRODUCT_STRING, size) == 0; 49} 50 51static bool 52is_right(const char *product_name, size_t size) 53{ 54 return strncmp(product_name, WMR_CONTROLLER_RIGHT_PRODUCT_STRING, size) == 0; 55} 56 57static void 58classify_and_assign_controller(struct xrt_prober *xp, 59 struct xrt_prober_device *xpd, 60 struct wmr_bt_controllers_search_results *ctrls) 61{ 62 char buf[256] = {0}; 63 int result = xrt_prober_get_string_descriptor(xp, xpd, XRT_PROBER_STRING_PRODUCT, (uint8_t *)buf, sizeof(buf)); 64 if (result <= 0) { 65 U_LOG_E("xrt_prober_get_string_descriptor: %i\n\tFailed to get product string!", result); 66 return; 67 } 68 69 if (is_left(buf, sizeof(buf))) { 70 ctrls->left = xpd; 71 } else if (is_right(buf, sizeof(buf))) { 72 ctrls->right = xpd; 73 } 74} 75 76static bool 77check_and_get_interface(struct xrt_prober_device *device, 78 enum u_logging_level log_level, 79 enum wmr_headset_type *out_hmd_type) 80{ 81 switch (device->vendor_id) { 82 case HP_VID: 83 U_LOG_IFL_T(log_level, "HP_VID"); 84 85 switch (device->product_id) { 86 case REVERB_G1_PID: *out_hmd_type = WMR_HEADSET_REVERB_G1; return true; 87 case REVERB_G2_PID: *out_hmd_type = WMR_HEADSET_REVERB_G2; return true; 88 case REVERB_G2_OMNICEPT_PID: *out_hmd_type = WMR_HEADSET_REVERB_G2; return true; 89 case VR1000_PID: *out_hmd_type = WMR_HEADSET_HP_VR1000; return true; 90 default: U_LOG_IFL_T(log_level, "No matching PID!"); return false; 91 } 92 93 case LENOVO_VID: 94 U_LOG_IFL_T(log_level, "LENOVO_VID"); 95 96 switch (device->product_id) { 97 case EXPLORER_PID: *out_hmd_type = WMR_HEADSET_LENOVO_EXPLORER; return true; 98 default: U_LOG_IFL_T(log_level, "No matching PID!"); return false; 99 } 100 101 case SAMSUNG_VID: 102 U_LOG_IFL_T(log_level, "SAMSUNG_VID"); 103 104 switch (device->product_id) { 105 case ODYSSEY_PLUS_PID: *out_hmd_type = WMR_HEADSET_SAMSUNG_800ZAA; return true; 106 case ODYSSEY_PID: 107 U_LOG_IFL_W(log_level, "Original Odyssey may not be well-supported - continuing anyway."); 108 *out_hmd_type = WMR_HEADSET_SAMSUNG_XE700X3AI; 109 return true; 110 default: U_LOG_IFL_T(log_level, "No matching PID!"); return false; 111 } 112 113 case QUANTA_VID: 114 U_LOG_IFL_T(log_level, "QUANTA_VID"); 115 116 switch (device->product_id) { 117 case MEDION_ERAZER_X1000_PID: *out_hmd_type = WMR_HEADSET_MEDION_ERAZER_X1000; return true; 118 default: U_LOG_IFL_T(log_level, "No matching PID!"); return false; 119 } 120 121 case DELL_VID: 122 U_LOG_IFL_T(log_level, "DELL_VID"); 123 124 switch (device->product_id) { 125 case VISOR_PID: *out_hmd_type = WMR_HEADSET_DELL_VISOR; return true; 126 default: U_LOG_IFL_T(log_level, "No matching PID!"); return false; 127 } 128 129 case ACER_VID: 130 U_LOG_IFL_T(log_level, "ACER_VID"); 131 132 switch (device->product_id) { 133 case AH100_PID: *out_hmd_type = WMR_HEADSET_ACER_AH100; return true; 134 case AH101_PID: *out_hmd_type = WMR_HEADSET_ACER_AH101; return true; 135 default: U_LOG_IFL_T(log_level, "No matching PID!"); return false; 136 } 137 138 case FUJITSU_VID: 139 U_LOG_IFL_T(log_level, "FUJITSU_VID"); 140 141 switch (device->product_id) { 142 case FMVHDS1_PID: *out_hmd_type = WMR_HEADSET_FUJITSU_FMVHDS1; return true; 143 default: U_LOG_IFL_T(log_level, "No matching PID!"); return false; 144 } 145 146 default: return false; 147 } 148} 149 150static bool 151find_companion_device(struct xrt_prober *xp, 152 struct xrt_prober_device **devices, 153 size_t device_count, 154 enum u_logging_level log_level, 155 enum wmr_headset_type *out_hmd_type, 156 struct xrt_prober_device **out_device) 157{ 158 struct xrt_prober_device *dev = NULL; 159 160 for (size_t i = 0; i < device_count; i++) { 161 bool match = false; 162 163 if (devices[i]->bus != XRT_BUS_TYPE_USB) { 164 continue; 165 } 166 167 match = check_and_get_interface(devices[i], log_level, out_hmd_type); 168 169 if (!match) { 170 continue; 171 } 172 173 if (dev != NULL) { 174 U_LOG_IFL_W(log_level, "Found multiple control devices, using the last."); 175 } 176 dev = devices[i]; 177 } 178 179 if (dev == NULL) { 180 return false; 181 } 182 183 unsigned char m_str[256] = {0}; 184 unsigned char p_str[256] = {0}; 185 xrt_prober_get_string_descriptor(xp, dev, XRT_PROBER_STRING_MANUFACTURER, m_str, sizeof(m_str)); 186 xrt_prober_get_string_descriptor(xp, dev, XRT_PROBER_STRING_PRODUCT, p_str, sizeof(p_str)); 187 188 U_LOG_IFL_D(log_level, "Found Hololens Sensors' companion device '%s' '%s' (vid %04X, pid %04X)", p_str, m_str, 189 dev->vendor_id, dev->product_id); 190 191 192 *out_device = dev; 193 194 return dev != NULL; 195} 196 197 198/* 199 * 200 * 'Exported' builder functions. 201 * 202 */ 203 204void 205wmr_find_bt_controller_pair(struct xrt_prober *xp, 206 struct xrt_prober_device **devices, 207 size_t device_count, 208 enum u_logging_level log_level, 209 struct wmr_bt_controllers_search_results *out_wbtcsr) 210{ 211 // Try to pair controllers of the same type. 212 struct wmr_bt_controllers_search_results odyssey_ctrls = {0}; 213 struct wmr_bt_controllers_search_results wmr_ctrls = {0}; 214 struct wmr_bt_controllers_search_results reverbg2_ctrls = {0}; 215 216 for (size_t i = 0; i < device_count; i++) { 217 struct xrt_prober_device *xpd = devices[i]; 218 219 // All controllers have the Microsoft vendor ID. 220 if (xpd->vendor_id != MICROSOFT_VID) { 221 continue; 222 } 223 224 // Only handle Bluetooth connected controllers here. 225 if (xpd->bus != XRT_BUS_TYPE_BLUETOOTH) { 226 continue; 227 } 228 229 if (xpd->product_id == WMR_CONTROLLER_PID) { 230 classify_and_assign_controller(xp, xpd, &wmr_ctrls); 231 } else if (xpd->product_id == ODYSSEY_CONTROLLER_PID) { 232 classify_and_assign_controller(xp, xpd, &odyssey_ctrls); 233 } else if (xpd->product_id == REVERB_G2_CONTROLLER_PID) { 234 classify_and_assign_controller(xp, xpd, &reverbg2_ctrls); 235 } 236 } 237 238 // We have to prefer one type pair, prefer Odyssey. 239 if (odyssey_ctrls.left != NULL && odyssey_ctrls.right != NULL) { 240 *out_wbtcsr = odyssey_ctrls; 241 return; 242 } 243 244 if (reverbg2_ctrls.left != NULL && reverbg2_ctrls.right != NULL) { 245 *out_wbtcsr = reverbg2_ctrls; 246 return; 247 } 248 249 // Other type pair. 250 if (wmr_ctrls.left != NULL && wmr_ctrls.right != NULL) { 251 *out_wbtcsr = wmr_ctrls; 252 return; 253 } 254 255 // Grab any of them. 256 out_wbtcsr->left = reverbg2_ctrls.left != NULL ? reverbg2_ctrls.left 257 : odyssey_ctrls.left != NULL ? odyssey_ctrls.left 258 : wmr_ctrls.left; 259 out_wbtcsr->right = reverbg2_ctrls.right != NULL ? reverbg2_ctrls.right 260 : odyssey_ctrls.right != NULL ? odyssey_ctrls.right 261 : wmr_ctrls.right; 262} 263 264void 265wmr_find_companion_device(struct xrt_prober *xp, 266 struct xrt_prober_device **xpdevs, 267 size_t xpdev_count, 268 enum u_logging_level log_level, 269 struct xrt_prober_device *xpdev_holo, 270 struct wmr_companion_search_results *out_wcsr) 271{ 272 struct xrt_prober_device *xpdev_companion = NULL; 273 enum wmr_headset_type type = WMR_HEADSET_GENERIC; 274 275 if (!find_companion_device(xp, xpdevs, xpdev_count, log_level, &type, &xpdev_companion)) { 276 U_LOG_IFL_E(log_level, "Did not find HoloLens Sensors' companion device"); 277 return; 278 } 279 280 out_wcsr->xpdev_companion = xpdev_companion; 281 out_wcsr->type = type; 282} 283 284void 285wmr_find_headset(struct xrt_prober *xp, 286 struct xrt_prober_device **xpdevs, 287 size_t xpdev_count, 288 enum u_logging_level log_level, 289 struct wmr_headset_search_results *out_whsr) 290{ 291 struct wmr_companion_search_results wcsr = {0}; 292 struct xrt_prober_device *xpdev_holo = NULL; 293 294 for (size_t i = 0; i < xpdev_count; i++) { 295 struct xrt_prober_device *xpd = xpdevs[i]; 296 297 // Only handle Bluetooth connected controllers here. 298 if (xpd->bus != XRT_BUS_TYPE_USB) { 299 continue; 300 } 301 302 if (xpd->vendor_id != MICROSOFT_VID || xpd->product_id != HOLOLENS_SENSORS_PID) { 303 continue; 304 } 305 306 xpdev_holo = xpd; 307 break; 308 } 309 310 // Did we find any? 311 if (xpdev_holo == NULL) { 312 U_LOG_IFL_D(log_level, "Did not find HoloLens Sensors device, no headset connected?"); 313 return; // Didn't find any hololense device, not an error. 314 } 315 316 317 // Find the companion device. 318 wmr_find_companion_device(xp, xpdevs, xpdev_count, log_level, xpdev_holo, &wcsr); 319 if (wcsr.xpdev_companion == NULL) { 320 U_LOG_IFL_E(log_level, "Found a HoloLens device, but not it's companion device"); 321 return; 322 } 323 324 // Done now, output. 325 out_whsr->xpdev_holo = xpdev_holo; 326 out_whsr->xpdev_companion = wcsr.xpdev_companion; 327 out_whsr->type = wcsr.type; 328} 329 330 331/* 332 * 333 * 'Exported' create functions. 334 * 335 */ 336 337xrt_result_t 338wmr_create_headset(struct xrt_prober *xp, 339 struct xrt_prober_device *xpdev_holo, 340 struct xrt_prober_device *xpdev_companion, 341 enum wmr_headset_type type, 342 enum u_logging_level log_level, 343 struct xrt_device **out_hmd, 344 struct xrt_device **out_left, 345 struct xrt_device **out_right, 346 struct xrt_device **out_ht_left, 347 struct xrt_device **out_ht_right) 348{ 349 DRV_TRACE_MARKER(); 350 351 U_LOG_IFL_D(log_level, "Creating headset."); 352 353 const int interface_holo = 2; 354 const int interface_companion = 0; 355 int ret; 356 357 struct os_hid_device *hid_holo = NULL; 358 ret = xrt_prober_open_hid_interface(xp, xpdev_holo, interface_holo, &hid_holo); 359 if (ret != 0) { 360 U_LOG_IFL_E(log_level, "Failed to open HoloLens Sensors HID interface"); 361 return XRT_ERROR_DEVICE_CREATION_FAILED; 362 } 363 364 struct os_hid_device *hid_companion = NULL; 365 ret = xrt_prober_open_hid_interface(xp, xpdev_companion, interface_companion, &hid_companion); 366 if (ret != 0) { 367 U_LOG_IFL_E(log_level, "Failed to open HoloLens Sensors' companion HID interface."); 368 goto error_holo; 369 } 370 371 struct xrt_device *hmd = NULL; 372 struct xrt_device *ht = NULL; 373 struct xrt_device *two_hands[2] = {NULL, NULL}; // Must initialize, always returned. 374 struct xrt_device *hmd_left_ctrl = NULL, *hmd_right_ctrl = NULL; 375 wmr_hmd_create(type, hid_holo, hid_companion, xpdev_holo, log_level, &hmd, &ht, &hmd_left_ctrl, 376 &hmd_right_ctrl); 377 378 if (hmd == NULL) { 379 U_LOG_IFL_E(log_level, "Failed to create WMR HMD device."); 380 /* No cleanup - the wmr_hmd_create() method cleaned up 381 * the hid devices already */ 382 return XRT_ERROR_DEVICE_CREATION_FAILED; 383 } 384 385#ifdef XRT_BUILD_DRIVER_HANDTRACKING 386 if (ht != NULL) { // Create hand-tracked controllers 387 cemu_devices_create(hmd, ht, two_hands); 388 } 389#endif 390 391 *out_hmd = hmd; 392 *out_left = hmd_left_ctrl; 393 *out_right = hmd_right_ctrl; 394 395 *out_ht_left = two_hands[0]; 396 *out_ht_right = two_hands[1]; 397 398 return XRT_SUCCESS; 399 400error_holo: 401 os_hid_destroy(hid_holo); 402 403 return XRT_ERROR_DEVICE_CREATION_FAILED; 404} 405 406xrt_result_t 407wmr_create_bt_controller(struct xrt_prober *xp, 408 struct xrt_prober_device *xpdev, 409 enum u_logging_level log_level, 410 struct xrt_device **out_xdev) 411{ 412 DRV_TRACE_MARKER(); 413 414 U_LOG_IFL_D(log_level, "Creating Bluetooth controller."); 415 416 struct os_hid_device *hid_controller = NULL; 417 418 // Only handle Bluetooth connected controllers here. 419 if (xpdev->bus != XRT_BUS_TYPE_BLUETOOTH) { 420 U_LOG_IFL_E(log_level, "Got a non Bluetooth device!"); 421 return XRT_ERROR_DEVICE_CREATION_FAILED; 422 } 423 424 char product_name[256] = {0}; 425 int ret = xrt_prober_get_string_descriptor( // 426 xp, // 427 xpdev, // 428 XRT_PROBER_STRING_PRODUCT, // 429 (uint8_t *)product_name, // 430 sizeof(product_name)); // 431 432 enum xrt_device_type controller_type = XRT_DEVICE_TYPE_UNKNOWN; 433 const int interface_controller = 0; 434 435 switch (xpdev->product_id) { 436 case WMR_CONTROLLER_PID: 437 case ODYSSEY_CONTROLLER_PID: 438 case REVERB_G2_CONTROLLER_PID: 439 if (is_left(product_name, sizeof(product_name))) { 440 controller_type = XRT_DEVICE_TYPE_LEFT_HAND_CONTROLLER; 441 break; 442 } else if (is_right(product_name, sizeof(product_name))) { 443 controller_type = XRT_DEVICE_TYPE_RIGHT_HAND_CONTROLLER; 444 break; 445 } 446 // else fall through 447 default: 448 U_LOG_IFL_E(log_level, 449 "Unsupported controller device (Bluetooth): vid: 0x%04X, pid: 0x%04X, Product Name: '%s'", 450 xpdev->vendor_id, xpdev->product_id, product_name); 451 return XRT_ERROR_DEVICE_CREATION_FAILED; 452 } 453 454 ret = xrt_prober_open_hid_interface(xp, xpdev, interface_controller, &hid_controller); 455 if (ret != 0) { 456 U_LOG_IFL_E(log_level, "Failed to open WMR Bluetooth controller's HID interface"); 457 return XRT_ERROR_DEVICE_CREATION_FAILED; 458 } 459 460 // Takes ownership of the hid_controller, even on failure 461 struct xrt_device *xdev = 462 wmr_bt_controller_create(hid_controller, controller_type, xpdev->vendor_id, xpdev->product_id, log_level); 463 if (xdev == NULL) { 464 U_LOG_IFL_E(log_level, "Failed to create WMR controller (Bluetooth)"); 465 return XRT_ERROR_DEVICE_CREATION_FAILED; 466 } 467 468 *out_xdev = xdev; 469 470 return XRT_SUCCESS; 471}