The open source OpenXR runtime
at main 431 lines 15 kB view raw
1// Copyright 2020-2021, N Madsen. 2// Copyright 2020-2023, Collabora, Ltd. 3// Copyright 2020-2023, Jan Schmidt 4// SPDX-License-Identifier: BSL-1.0 5/*! 6 * @file 7 * @brief Driver for WMR Controllers. 8 * @author Jan Schmidt <jan@centricular.com> 9 * @ingroup drv_wmr 10 */ 11#include "math/m_api.h" 12 13#include "util/u_device.h" 14#include "util/u_trace_marker.h" 15#include "util/u_var.h" 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20#include <assert.h> 21#include <errno.h> 22 23#include "wmr_controller.h" 24 25#define WMR_TRACE(ctrl, ...) U_LOG_XDEV_IFL_T(&ctrl->base.base, ctrl->base.log_level, __VA_ARGS__) 26#define WMR_TRACE_HEX(ctrl, ...) U_LOG_XDEV_IFL_T_HEX(&ctrl->base.base, ctrl->base.log_level, __VA_ARGS__) 27#define WMR_DEBUG(ctrl, ...) U_LOG_XDEV_IFL_D(&ctrl->base.base, ctrl->base.log_level, __VA_ARGS__) 28#define WMR_DEBUG_HEX(ctrl, ...) U_LOG_XDEV_IFL_D_HEX(&ctrl->base.base, ctrl->base.log_level, __VA_ARGS__) 29#define WMR_INFO(ctrl, ...) U_LOG_XDEV_IFL_I(&ctrl->base.base, ctrl->base.log_level, __VA_ARGS__) 30#define WMR_WARN(ctrl, ...) U_LOG_XDEV_IFL_W(&ctrl->base.base, ctrl->base.log_level, __VA_ARGS__) 31#define WMR_ERROR(ctrl, ...) U_LOG_XDEV_IFL_E(&ctrl->base.base, ctrl->base.log_level, __VA_ARGS__) 32 33#ifdef XRT_DOXYGEN 34#define WMR_PACKED 35#else 36#define WMR_PACKED __attribute__((packed)) 37#endif 38 39/*! 40 * Indices in input list of each input. 41 */ 42enum wmr_controller_hp_input_index 43{ 44 WMR_CONTROLLER_INDEX_MENU_CLICK, 45 WMR_CONTROLLER_INDEX_HOME_CLICK, 46 WMR_CONTROLLER_INDEX_SQUEEZE_CLICK, 47 WMR_CONTROLLER_INDEX_SQUEEZE_VALUE, 48 WMR_CONTROLLER_INDEX_TRIGGER_VALUE, 49 WMR_CONTROLLER_INDEX_THUMBSTICK_CLICK, 50 WMR_CONTROLLER_INDEX_THUMBSTICK, 51 WMR_CONTROLLER_INDEX_GRIP_POSE, 52 WMR_CONTROLLER_INDEX_AIM_POSE, 53 WMR_CONTROLLER_INDEX_X_A_CLICK, 54 WMR_CONTROLLER_INDEX_Y_B_CLICK, 55 /* keep as last: */ 56 WMR_CONTROLLER_INDEX_COUNT 57}; 58 59#define SET_INPUT(wcb, INDEX, NAME) \ 60 (wcb->base.inputs[WMR_CONTROLLER_INDEX_##INDEX].name = XRT_INPUT_G2_CONTROLLER_##NAME) 61 62/* 63 * 64 * Bindings 65 * 66 */ 67 68static struct xrt_binding_input_pair touch_inputs[19] = { 69 {XRT_INPUT_TOUCH_X_CLICK, XRT_INPUT_G2_CONTROLLER_X_CLICK}, 70 {XRT_INPUT_TOUCH_X_TOUCH, XRT_INPUT_G2_CONTROLLER_X_CLICK}, 71 {XRT_INPUT_TOUCH_Y_CLICK, XRT_INPUT_G2_CONTROLLER_Y_CLICK}, 72 {XRT_INPUT_TOUCH_Y_TOUCH, XRT_INPUT_G2_CONTROLLER_Y_CLICK}, 73 {XRT_INPUT_TOUCH_MENU_CLICK, XRT_INPUT_G2_CONTROLLER_MENU_CLICK}, 74 {XRT_INPUT_TOUCH_MENU_CLICK, XRT_INPUT_G2_CONTROLLER_HOME_CLICK}, 75 {XRT_INPUT_TOUCH_A_CLICK, XRT_INPUT_G2_CONTROLLER_A_CLICK}, 76 {XRT_INPUT_TOUCH_A_TOUCH, XRT_INPUT_G2_CONTROLLER_A_CLICK}, 77 {XRT_INPUT_TOUCH_B_CLICK, XRT_INPUT_G2_CONTROLLER_B_CLICK}, 78 {XRT_INPUT_TOUCH_B_TOUCH, XRT_INPUT_G2_CONTROLLER_B_CLICK}, 79 {XRT_INPUT_TOUCH_SYSTEM_CLICK, XRT_INPUT_G2_CONTROLLER_MENU_CLICK}, 80 {XRT_INPUT_TOUCH_SYSTEM_CLICK, XRT_INPUT_G2_CONTROLLER_HOME_CLICK}, 81 {XRT_INPUT_TOUCH_SQUEEZE_VALUE, XRT_INPUT_G2_CONTROLLER_SQUEEZE_VALUE}, 82 {XRT_INPUT_TOUCH_TRIGGER_TOUCH, XRT_INPUT_G2_CONTROLLER_TRIGGER_VALUE}, 83 {XRT_INPUT_TOUCH_TRIGGER_VALUE, XRT_INPUT_G2_CONTROLLER_TRIGGER_VALUE}, 84 {XRT_INPUT_TOUCH_THUMBSTICK_CLICK, XRT_INPUT_G2_CONTROLLER_THUMBSTICK_CLICK}, 85 {XRT_INPUT_TOUCH_THUMBSTICK, XRT_INPUT_G2_CONTROLLER_THUMBSTICK}, 86 {XRT_INPUT_TOUCH_GRIP_POSE, XRT_INPUT_G2_CONTROLLER_GRIP_POSE}, 87 {XRT_INPUT_TOUCH_AIM_POSE, XRT_INPUT_G2_CONTROLLER_AIM_POSE}, 88}; 89 90static struct xrt_binding_output_pair touch_outputs[1] = { 91 {XRT_OUTPUT_NAME_TOUCH_HAPTIC, XRT_OUTPUT_NAME_G2_CONTROLLER_HAPTIC}, 92}; 93 94static struct xrt_binding_input_pair simple_inputs[4] = { 95 {XRT_INPUT_SIMPLE_SELECT_CLICK, XRT_INPUT_G2_CONTROLLER_TRIGGER_VALUE}, 96 {XRT_INPUT_SIMPLE_MENU_CLICK, XRT_INPUT_G2_CONTROLLER_MENU_CLICK}, 97 {XRT_INPUT_SIMPLE_GRIP_POSE, XRT_INPUT_G2_CONTROLLER_GRIP_POSE}, 98 {XRT_INPUT_SIMPLE_AIM_POSE, XRT_INPUT_G2_CONTROLLER_AIM_POSE}, 99}; 100 101static struct xrt_binding_output_pair simple_outputs[1] = { 102 {XRT_OUTPUT_NAME_SIMPLE_VIBRATION, XRT_OUTPUT_NAME_G2_CONTROLLER_HAPTIC}, 103}; 104 105static struct xrt_binding_profile binding_profiles[2] = { 106 { 107 .name = XRT_DEVICE_TOUCH_CONTROLLER, 108 .inputs = touch_inputs, 109 .input_count = ARRAY_SIZE(touch_inputs), 110 .outputs = touch_outputs, 111 .output_count = ARRAY_SIZE(touch_outputs), 112 }, 113 { 114 .name = XRT_DEVICE_SIMPLE_CONTROLLER, 115 .inputs = simple_inputs, 116 .input_count = ARRAY_SIZE(simple_inputs), 117 .outputs = simple_outputs, 118 .output_count = ARRAY_SIZE(simple_outputs), 119 }, 120}; 121 122/* OG WMR Controller inputs struct */ 123struct wmr_controller_hp_input 124{ 125 // buttons clicked 126 bool menu; 127 bool home; 128 bool bt_pairing; 129 bool squeeze_click; // Squeeze click reported on full squeeze 130 131 // X/Y/A/B buttons 132 bool x_a; 133 bool y_b; 134 135 float trigger; 136 float squeeze; 137 138 struct 139 { 140 bool click; 141 struct xrt_vec2 values; 142 } thumbstick; 143 144 uint8_t battery; 145 146 struct 147 { 148 uint64_t timestamp_ticks; 149 struct xrt_vec3 acc; 150 struct xrt_vec3 gyro; 151 int32_t temperature; 152 } imu; 153}; 154#undef WMR_PACKED 155 156/* HP WMR Controller device struct */ 157struct wmr_controller_hp 158{ 159 struct wmr_controller_base base; 160 161 //! The last decoded package of IMU and button data 162 struct wmr_controller_hp_input last_inputs; 163}; 164 165/* 166 * 167 * WMR Motion Controller protocol helpers 168 * 169 */ 170 171static inline void 172vec3_from_wmr_controller_accel(const int32_t sample[3], struct xrt_vec3 *out_vec) 173{ 174 // Reverb G1 observation: 1g is approximately 490,000. 175 // @todo: Confirm the scale is correct 176 177 out_vec->x = (float)sample[0] / (98000 / 2); 178 out_vec->y = (float)sample[1] / (98000 / 2); 179 out_vec->z = (float)sample[2] / (98000 / 2); 180} 181 182 183static inline void 184vec3_from_wmr_controller_gyro(const int32_t sample[3], struct xrt_vec3 *out_vec) 185{ 186 // @todo: Confirm the scale is correct 187 out_vec->x = (float)sample[0] * 0.00001f; 188 out_vec->y = (float)sample[1] * 0.00001f; 189 out_vec->z = (float)sample[2] * 0.00001f; 190} 191 192static bool 193wmr_controller_hp_packet_parse(struct wmr_controller_hp *ctrl, const unsigned char *buffer, size_t len) 194{ 195 struct wmr_controller_hp_input *last_input = &ctrl->last_inputs; 196 struct wmr_controller_base *wcb = (struct wmr_controller_base *)(ctrl); 197 198 if (len != 44) { 199 U_LOG_IFL_E(wcb->log_level, "WMR Controller: unexpected message length: %zd", len); 200 return false; 201 } 202 203 const unsigned char *p = buffer; 204 205 // Read buttons 206 uint8_t buttons = read8(&p); 207 last_input->thumbstick.click = buttons & 0x01; 208 last_input->home = buttons & 0x02; 209 last_input->menu = buttons & 0x04; 210 last_input->squeeze_click = buttons & 0x08; // squeeze-click 211 last_input->bt_pairing = buttons & 0x20; 212 213 // Read thumbstick coordinates (12 bit resolution) 214 int16_t stick_x = read8(&p); 215 uint8_t nibbles = read8(&p); 216 stick_x += ((nibbles & 0x0F) << 8); 217 int16_t stick_y = (nibbles >> 4); 218 stick_y += (read8(&p) << 4); 219 220 last_input->thumbstick.values.x = (float)(stick_x - 0x07FF) / 0x07FF; 221 if (last_input->thumbstick.values.x > 1.0f) { 222 last_input->thumbstick.values.x = 1.0f; 223 } 224 225 last_input->thumbstick.values.y = (float)(stick_y - 0x07FF) / 0x07FF; 226 if (last_input->thumbstick.values.y > 1.0f) { 227 last_input->thumbstick.values.y = 1.0f; 228 } 229 230 // Read trigger value (0x00 - 0xFF) 231 last_input->trigger = (float)read8(&p) / 0xFF; 232 233 /* On OG these are touchpad values, but on HP it's 234 * squeeze value and A_X/B_Y click */ 235 last_input->squeeze = (float)read8(&p) / 0xFF; 236 237 buttons = read8(&p); 238 last_input->x_a = buttons & 0x02; 239 last_input->y_b = buttons & 0x01; 240 241 last_input->battery = read8(&p); 242 243 int32_t acc[3]; 244 acc[0] = read24(&p); // x 245 acc[1] = read24(&p); // y 246 acc[2] = read24(&p); // z 247 vec3_from_wmr_controller_accel(acc, &last_input->imu.acc); 248 math_matrix_3x3_transform_vec3(&wcb->config.sensors.accel.mix_matrix, &last_input->imu.acc, 249 &last_input->imu.acc); 250 math_vec3_accum(&wcb->config.sensors.accel.bias_offsets, &last_input->imu.acc); 251 math_quat_rotate_vec3(&wcb->config.sensors.transforms.P_oxr_acc.orientation, &last_input->imu.acc, 252 &last_input->imu.acc); 253 254 U_LOG_IFL_T(wcb->log_level, "Accel [m/s^2] : %f", 255 sqrtf(last_input->imu.acc.x * last_input->imu.acc.x + 256 last_input->imu.acc.y * last_input->imu.acc.y + 257 last_input->imu.acc.z * last_input->imu.acc.z)); 258 259 260 last_input->imu.temperature = read16(&p); 261 262 int32_t gyro[3]; 263 gyro[0] = read24(&p); 264 gyro[1] = read24(&p); 265 gyro[2] = read24(&p); 266 vec3_from_wmr_controller_gyro(gyro, &last_input->imu.gyro); 267 math_matrix_3x3_transform_vec3(&wcb->config.sensors.gyro.mix_matrix, &last_input->imu.gyro, 268 &last_input->imu.gyro); 269 math_vec3_accum(&wcb->config.sensors.gyro.bias_offsets, &last_input->imu.gyro); 270 math_quat_rotate_vec3(&wcb->config.sensors.transforms.P_oxr_gyr.orientation, &last_input->imu.gyro, 271 &last_input->imu.gyro); 272 273 274 uint32_t prev_ticks = last_input->imu.timestamp_ticks & UINT32_C(0xFFFFFFFF); 275 276 // Write the new ticks value into the lower half of timestamp_ticks 277 last_input->imu.timestamp_ticks &= (UINT64_C(0xFFFFFFFF) << 32u); 278 last_input->imu.timestamp_ticks += (uint32_t)read32(&p); 279 280 if ((last_input->imu.timestamp_ticks & UINT64_C(0xFFFFFFFF)) < prev_ticks) { 281 // Timer overflow, so increment the upper half of timestamp_ticks 282 last_input->imu.timestamp_ticks += (UINT64_C(0x1) << 32u); 283 } 284 285 /* Todo: More decoding here 286 read16(&p); // Unknown. Seems to depend on controller orientation (probably mag) 287 read32(&p); // Unknown. 288 read16(&p); // Unknown. Device state, etc. 289 read16(&p); 290 read16(&p); 291 */ 292 293 return true; 294} 295 296static bool 297handle_input_packet(struct wmr_controller_base *wcb, uint64_t time_ns, uint8_t *buffer, uint32_t buf_size) 298{ 299 struct wmr_controller_hp *ctrl = (struct wmr_controller_hp *)(wcb); 300 301 bool b = wmr_controller_hp_packet_parse(ctrl, buffer, buf_size); 302 if (b) { 303 m_imu_3dof_update(&wcb->fusion, 304 ctrl->last_inputs.imu.timestamp_ticks * WMR_MOTION_CONTROLLER_NS_PER_TICK, 305 &ctrl->last_inputs.imu.acc, &ctrl->last_inputs.imu.gyro); 306 307 wcb->last_imu_timestamp_ns = time_ns; 308 wcb->last_angular_velocity = ctrl->last_inputs.imu.gyro; 309 } 310 311 return b; 312} 313 314static xrt_result_t 315wmr_controller_hp_update_inputs(struct xrt_device *xdev) 316{ 317 DRV_TRACE_MARKER(); 318 319 struct wmr_controller_hp *ctrl = (struct wmr_controller_hp *)(xdev); 320 struct wmr_controller_base *wcb = (struct wmr_controller_base *)(xdev); 321 322 os_mutex_lock(&wcb->data_lock); 323 324 struct xrt_input *xrt_inputs = xdev->inputs; 325 struct wmr_controller_hp_input *cur_inputs = &ctrl->last_inputs; 326 327 xrt_inputs[WMR_CONTROLLER_INDEX_MENU_CLICK].value.boolean = cur_inputs->menu; 328 xrt_inputs[WMR_CONTROLLER_INDEX_HOME_CLICK].value.boolean = cur_inputs->home; 329 xrt_inputs[WMR_CONTROLLER_INDEX_X_A_CLICK].value.boolean = cur_inputs->x_a; 330 xrt_inputs[WMR_CONTROLLER_INDEX_Y_B_CLICK].value.boolean = cur_inputs->y_b; 331 xrt_inputs[WMR_CONTROLLER_INDEX_SQUEEZE_CLICK].value.boolean = cur_inputs->squeeze; 332 xrt_inputs[WMR_CONTROLLER_INDEX_SQUEEZE_VALUE].value.vec1.x = cur_inputs->squeeze; 333 xrt_inputs[WMR_CONTROLLER_INDEX_TRIGGER_VALUE].value.vec1.x = cur_inputs->trigger; 334 xrt_inputs[WMR_CONTROLLER_INDEX_THUMBSTICK_CLICK].value.boolean = cur_inputs->thumbstick.click; 335 xrt_inputs[WMR_CONTROLLER_INDEX_THUMBSTICK].value.vec2 = cur_inputs->thumbstick.values; 336 337 os_mutex_unlock(&wcb->data_lock); 338 339 return XRT_SUCCESS; 340} 341 342static void 343wmr_controller_hp_destroy(struct xrt_device *xdev) 344{ 345 struct wmr_controller_base *wcb = (struct wmr_controller_base *)(xdev); 346 347 wmr_controller_base_deinit(wcb); 348 free(wcb); 349} 350 351struct wmr_controller_base * 352wmr_controller_hp_create(struct wmr_controller_connection *conn, 353 enum xrt_device_type controller_type, 354 enum u_logging_level log_level) 355{ 356 DRV_TRACE_MARKER(); 357 358 enum u_device_alloc_flags flags = U_DEVICE_ALLOC_TRACKING_NONE; 359 struct wmr_controller_hp *ctrl = 360 U_DEVICE_ALLOCATE(struct wmr_controller_hp, flags, WMR_CONTROLLER_INDEX_COUNT, 1); 361 struct wmr_controller_base *wcb = (struct wmr_controller_base *)(ctrl); 362 363 if (!wmr_controller_base_init(wcb, conn, controller_type, log_level, wmr_controller_hp_destroy)) { 364 wmr_controller_hp_destroy(&wcb->base); 365 return NULL; 366 } 367 368 wcb->handle_input_packet = handle_input_packet; 369 370 // Only set those we want to overwrite. 371 wcb->base.update_inputs = wmr_controller_hp_update_inputs; 372 wcb->base.name = XRT_DEVICE_HP_REVERB_G2_CONTROLLER; 373 374 if (controller_type == XRT_DEVICE_TYPE_LEFT_HAND_CONTROLLER) { 375 snprintf(wcb->base.str, ARRAY_SIZE(wcb->base.str), "HP Reverb G2 Left Controller"); 376 } else { 377 snprintf(wcb->base.str, ARRAY_SIZE(wcb->base.str), "HP Reverb G2 Right Controller"); 378 } 379 380 SET_INPUT(wcb, MENU_CLICK, MENU_CLICK); 381 SET_INPUT(wcb, HOME_CLICK, HOME_CLICK); 382 SET_INPUT(wcb, SQUEEZE_CLICK, SQUEEZE_CLICK); 383 SET_INPUT(wcb, SQUEEZE_VALUE, SQUEEZE_VALUE); 384 SET_INPUT(wcb, TRIGGER_VALUE, TRIGGER_VALUE); 385 SET_INPUT(wcb, THUMBSTICK_CLICK, THUMBSTICK_CLICK); 386 SET_INPUT(wcb, THUMBSTICK, THUMBSTICK); 387 SET_INPUT(wcb, GRIP_POSE, GRIP_POSE); 388 SET_INPUT(wcb, AIM_POSE, AIM_POSE); 389 if (controller_type == XRT_DEVICE_TYPE_LEFT_HAND_CONTROLLER) { 390 SET_INPUT(wcb, X_A_CLICK, X_CLICK); 391 SET_INPUT(wcb, Y_B_CLICK, Y_CLICK); 392 } else { 393 SET_INPUT(wcb, X_A_CLICK, A_CLICK); 394 SET_INPUT(wcb, Y_B_CLICK, B_CLICK); 395 } 396 397 for (uint32_t i = 0; i < wcb->base.input_count; i++) { 398 wcb->base.inputs[0].active = true; 399 } 400 401 ctrl->last_inputs.imu.timestamp_ticks = 0; 402 403 wcb->base.outputs[0].name = XRT_OUTPUT_NAME_WMR_HAPTIC; 404 405 wcb->base.binding_profiles = binding_profiles; 406 wcb->base.binding_profile_count = ARRAY_SIZE(binding_profiles); 407 408 u_var_add_bool(wcb, &ctrl->last_inputs.menu, "input.menu"); 409 u_var_add_bool(wcb, &ctrl->last_inputs.home, "input.home"); 410 u_var_add_bool(wcb, &ctrl->last_inputs.bt_pairing, "input.bt_pairing"); 411 u_var_add_bool(wcb, &ctrl->last_inputs.squeeze_click, "input.squeeze.click"); 412 u_var_add_f32(wcb, &ctrl->last_inputs.squeeze, "input.squeeze.value"); 413 u_var_add_f32(wcb, &ctrl->last_inputs.trigger, "input.trigger"); 414 u_var_add_u8(wcb, &ctrl->last_inputs.battery, "input.battery"); 415 u_var_add_bool(wcb, &ctrl->last_inputs.thumbstick.click, "input.thumbstick.click"); 416 u_var_add_f32(wcb, &ctrl->last_inputs.thumbstick.values.x, "input.thumbstick.values.y"); 417 u_var_add_f32(wcb, &ctrl->last_inputs.thumbstick.values.y, "input.thumbstick.values.x"); 418 if (controller_type == XRT_DEVICE_TYPE_LEFT_HAND_CONTROLLER) { 419 u_var_add_bool(wcb, &ctrl->last_inputs.x_a, "input.x"); 420 u_var_add_bool(wcb, &ctrl->last_inputs.y_b, "input.y"); 421 } else { 422 u_var_add_bool(wcb, &ctrl->last_inputs.x_a, "input.a"); 423 u_var_add_bool(wcb, &ctrl->last_inputs.y_b, "input.b"); 424 } 425 426 u_var_add_ro_vec3_f32(wcb, &ctrl->last_inputs.imu.acc, "imu.acc"); 427 u_var_add_ro_vec3_f32(wcb, &ctrl->last_inputs.imu.gyro, "imu.gyro"); 428 u_var_add_i32(wcb, &ctrl->last_inputs.imu.temperature, "imu.temperature"); 429 430 return wcb; 431}