The open source OpenXR runtime

d/xreal_air: Implement changes to support Xreal Air 2 Ultra

Signed-off-by: Jacki <jacki@thejackimonster.de>
Part-of: <https://gitlab.freedesktop.org/monado/monado/-/merge_requests/2436>

authored by

TheJackiMonster and committed by
Simon Zeni
80040ff3 2bf7f5c1

+211 -64
+106 -24
src/xrt/drivers/xreal_air/xreal_air_hmd.c
··· 1 - // Copyright 2023-2024, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air packet parsing implementation. 6 - * @author Tobias Frisch <thejackimonster@gmail.com> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 34 #define XREAL_AIR_DEBUG(hmd, ...) U_LOG_XDEV_IFL_D(&hmd->base, hmd->log_level, __VA_ARGS__) 35 #define XREAL_AIR_ERROR(hmd, ...) U_LOG_XDEV_IFL_E(&hmd->base, hmd->log_level, __VA_ARGS__) 36 37 - #define SENSOR_BUFFER_SIZE 64 38 #define CONTROL_BUFFER_SIZE 64 39 40 #define SENSOR_HEAD 0xFD ··· 88 89 uint32_t static_id; 90 bool display_on; 91 uint8_t imu_stream_state; 92 93 enum u_logging_level log_level; 94 95 uint32_t calibration_buffer_len; 96 uint32_t calibration_buffer_pos; ··· 406 ((data->data[0] << 0u) | (data->data[1] << 8u) | (data->data[2] << 16u) | (data->data[3] << 24u)); 407 408 hmd->calibration_buffer_len = calibration_data_length; 409 410 if (hmd->calibration_buffer_len > 0) { 411 if (hmd->calibration_buffer) { ··· 426 handle_sensor_control_cal_data_get_next_segment(struct xreal_air_hmd *hmd, 427 const struct xreal_air_parsed_sensor_control_data *data) 428 { 429 - if (hmd->calibration_buffer_len == 0) { 430 request_sensor_control_get_cal_data_length(hmd); 431 return; 432 } ··· 437 return; 438 } 439 440 const uint32_t remaining = (hmd->calibration_buffer_len - hmd->calibration_buffer_pos); 441 - const uint8_t next = (remaining > 56 ? 56 : remaining); 442 443 if (hmd->calibration_buffer) { 444 memcpy(hmd->calibration_buffer + hmd->calibration_buffer_pos, data->data, next); ··· 453 // Parse calibration data from raw json. 454 if (!xreal_air_parse_calibration_buffer(&hmd->calibration, hmd->calibration_buffer, 455 hmd->calibration_buffer_len)) { 456 XREAL_AIR_ERROR(hmd, "Failed parse calibration data!"); 457 } else { 458 hmd->calibration_valid = true; ··· 481 482 if (hmd->static_id == 0) { 483 request_sensor_control_get_static_id(hmd); 484 - } else if (!hmd->calibration_valid) { 485 request_sensor_control_get_cal_data_length(hmd); 486 } 487 } ··· 495 496 hmd->static_id = static_id; 497 498 - if (!hmd->calibration_valid) { 499 request_sensor_control_get_cal_data_length(hmd); 500 } 501 } 502 503 static void 504 - handle_sensor_control_data_msg(struct xreal_air_hmd *hmd, unsigned char *buffer, int size) 505 { 506 struct xreal_air_parsed_sensor_control_data data; 507 508 - if (!xreal_air_parse_sensor_control_data_packet(&data, buffer, size)) { 509 XREAL_AIR_ERROR(hmd, "Could not decode sensor control data packet"); 510 } 511 ··· 526 } 527 528 static void 529 - handle_sensor_msg(struct xreal_air_hmd *hmd, unsigned char *buffer, int size) 530 { 531 if (buffer[0] == 0xAA) { 532 handle_sensor_control_data_msg(hmd, buffer, size); ··· 536 timepoint_ns now_ns = (timepoint_ns)os_monotonic_get_ns(); 537 uint64_t last_timestamp = hmd->last.timestamp; 538 539 - if (!xreal_air_parse_sensor_packet(&hmd->last, buffer, size)) { 540 XREAL_AIR_ERROR(hmd, "Could not decode sensor packet"); 541 } else { 542 hmd->imu_stream_state = 0x1; ··· 576 sensor_clear_queue(struct xreal_air_hmd *hmd) 577 { 578 uint8_t buffer[SENSOR_BUFFER_SIZE]; 579 580 - while (os_hid_read(hmd->hid_sensor, buffer, SENSOR_BUFFER_SIZE, 0) > 0) { 581 // Just drop the packets. 582 } 583 } ··· 586 sensor_read_one_packet(struct xreal_air_hmd *hmd) 587 { 588 uint8_t buffer[SENSOR_BUFFER_SIZE]; 589 590 - int size = os_hid_read(hmd->hid_sensor, buffer, SENSOR_BUFFER_SIZE, 0); 591 if (size <= 0) { 592 return size; 593 } 594 595 - handle_sensor_msg(hmd, buffer, size); 596 return 0; 597 } 598 ··· 711 } 712 713 static void 714 handle_control_button(struct xreal_air_hmd *hmd, const struct xreal_air_parsed_control *control) 715 { 716 // Physical button ··· 736 hmd->wants.brightness = brightness; 737 break; 738 } 739 - case XREAL_AIR_BUTTON_VIRT_MODE_UP: { 740 - const uint8_t display_mode = hmd->state.display_mode; 741 742 - if (display_mode == XREAL_AIR_DISPLAY_MODE_2D) { 743 - hmd->wants.display_mode = XREAL_AIR_DISPLAY_MODE_3D; 744 } 745 746 break; 747 } 748 - case XREAL_AIR_BUTTON_VIRT_MODE_DOWN: { 749 const uint8_t display_mode = hmd->state.display_mode; 750 751 - if (display_mode == XREAL_AIR_DISPLAY_MODE_3D) { 752 hmd->wants.display_mode = XREAL_AIR_DISPLAY_MODE_2D; 753 } 754 755 break; 756 } 757 default: { ··· 785 case XREAL_AIR_MSG_R_DISP_MODE: handle_control_display_mode(hmd, control); break; 786 case XREAL_AIR_MSG_W_DISP_MODE: break; 787 case XREAL_AIR_MSG_P_START_HEARTBEAT: handle_control_heartbeat_start(hmd, control); break; 788 case XREAL_AIR_MSG_P_BUTTON_PRESSED: handle_control_button(hmd, control); break; 789 case XREAL_AIR_MSG_P_ASYNC_TEXT_LOG: handle_control_async_text(hmd, control); break; 790 case XREAL_AIR_MSG_P_END_HEARTBEAT: handle_control_heartbeat_end(hmd, control); break; ··· 1065 const enum xrt_space_relation_flags flags = (enum xrt_space_relation_flags)( 1066 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT); 1067 1068 - struct xrt_space_relation relation; 1069 - U_ZERO(&relation); // Clear out the relation. 1070 relation.relation_flags = flags; 1071 1072 m_relation_history_get(hmd->relation_hist, at_timestamp_ns, &relation); 1073 relation.relation_flags = flags; // Needed after history_get 1074 1075 *out_relation = relation; 1076 1077 // Make sure that the orientation is valid. 1078 - math_quat_normalize(&out_relation->pose.orientation); 1079 return XRT_SUCCESS; 1080 } 1081 ··· 1104 struct xrt_device * 1105 xreal_air_hmd_create_device(struct os_hid_device *sensor_device, 1106 struct os_hid_device *control_device, 1107 - enum u_logging_level log_level) 1108 { 1109 enum u_device_alloc_flags flags = 1110 (enum u_device_alloc_flags)(U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE); ··· 1112 int ret; 1113 1114 hmd->log_level = log_level; 1115 hmd->base.update_inputs = xreal_air_hmd_update_inputs; 1116 hmd->base.get_tracked_pose = xreal_air_hmd_get_tracked_pose; 1117 hmd->base.get_view_poses = u_device_get_view_poses; ··· 1134 1135 hmd->static_id = 0; 1136 hmd->display_on = false; 1137 hmd->imu_stream_state = 0; 1138 1139 hmd->calibration_buffer = NULL;
··· 1 + // Copyright 2023-2025, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air packet parsing implementation. 6 + * @author Tobias Frisch <jacki@thejackimonster.de> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 34 #define XREAL_AIR_DEBUG(hmd, ...) U_LOG_XDEV_IFL_D(&hmd->base, hmd->log_level, __VA_ARGS__) 35 #define XREAL_AIR_ERROR(hmd, ...) U_LOG_XDEV_IFL_E(&hmd->base, hmd->log_level, __VA_ARGS__) 36 37 + #define SENSOR_BUFFER_SIZE 512 38 #define CONTROL_BUFFER_SIZE 64 39 40 #define SENSOR_HEAD 0xFD ··· 88 89 uint32_t static_id; 90 bool display_on; 91 + uint8_t blend_state; 92 + uint8_t control_mode; 93 uint8_t imu_stream_state; 94 95 enum u_logging_level log_level; 96 + uint16_t max_sensor_buffer_size; 97 98 uint32_t calibration_buffer_len; 99 uint32_t calibration_buffer_pos; ··· 409 ((data->data[0] << 0u) | (data->data[1] << 8u) | (data->data[2] << 16u) | (data->data[3] << 24u)); 410 411 hmd->calibration_buffer_len = calibration_data_length; 412 + hmd->calibration_valid = false; 413 414 if (hmd->calibration_buffer_len > 0) { 415 if (hmd->calibration_buffer) { ··· 430 handle_sensor_control_cal_data_get_next_segment(struct xreal_air_hmd *hmd, 431 const struct xreal_air_parsed_sensor_control_data *data) 432 { 433 + if (hmd->calibration_valid) { 434 + return; 435 + } else if (hmd->calibration_buffer_len == 0) { 436 request_sensor_control_get_cal_data_length(hmd); 437 return; 438 } ··· 443 return; 444 } 445 446 + uint16_t data_buffer_size = SENSOR_BUFFER_SIZE; 447 + 448 + if (data_buffer_size > hmd->max_sensor_buffer_size) { 449 + data_buffer_size = hmd->max_sensor_buffer_size; 450 + } 451 + 452 + if (data_buffer_size <= 8) { 453 + XREAL_AIR_ERROR(hmd, "Failed to receive next calibration data from buffer!"); 454 + return; 455 + } 456 + 457 + data_buffer_size -= 8; 458 + 459 const uint32_t remaining = (hmd->calibration_buffer_len - hmd->calibration_buffer_pos); 460 + const uint16_t next = (remaining > data_buffer_size ? data_buffer_size : remaining); 461 462 if (hmd->calibration_buffer) { 463 memcpy(hmd->calibration_buffer + hmd->calibration_buffer_pos, data->data, next); ··· 472 // Parse calibration data from raw json. 473 if (!xreal_air_parse_calibration_buffer(&hmd->calibration, hmd->calibration_buffer, 474 hmd->calibration_buffer_len)) { 475 + hmd->calibration_valid = false; 476 + 477 XREAL_AIR_ERROR(hmd, "Failed parse calibration data!"); 478 } else { 479 hmd->calibration_valid = true; ··· 502 503 if (hmd->static_id == 0) { 504 request_sensor_control_get_static_id(hmd); 505 + } else if ((!hmd->calibration_valid) && (!hmd->calibration_buffer_len)) { 506 request_sensor_control_get_cal_data_length(hmd); 507 } 508 } ··· 516 517 hmd->static_id = static_id; 518 519 + if ((!hmd->calibration_valid) && (!hmd->calibration_buffer_len)) { 520 request_sensor_control_get_cal_data_length(hmd); 521 } 522 } 523 524 static void 525 + handle_sensor_control_data_msg(struct xreal_air_hmd *hmd, unsigned char *buffer, size_t size) 526 { 527 struct xreal_air_parsed_sensor_control_data data; 528 529 + if (!xreal_air_parse_sensor_control_data_packet(&data, buffer, size, hmd->max_sensor_buffer_size)) { 530 XREAL_AIR_ERROR(hmd, "Could not decode sensor control data packet"); 531 } 532 ··· 547 } 548 549 static void 550 + handle_sensor_msg(struct xreal_air_hmd *hmd, unsigned char *buffer, size_t size) 551 { 552 if (buffer[0] == 0xAA) { 553 handle_sensor_control_data_msg(hmd, buffer, size); ··· 557 timepoint_ns now_ns = (timepoint_ns)os_monotonic_get_ns(); 558 uint64_t last_timestamp = hmd->last.timestamp; 559 560 + if (!xreal_air_parse_sensor_packet(&hmd->last, buffer, size, hmd->max_sensor_buffer_size)) { 561 XREAL_AIR_ERROR(hmd, "Could not decode sensor packet"); 562 } else { 563 hmd->imu_stream_state = 0x1; ··· 597 sensor_clear_queue(struct xreal_air_hmd *hmd) 598 { 599 uint8_t buffer[SENSOR_BUFFER_SIZE]; 600 + size_t buffer_size = SENSOR_BUFFER_SIZE; 601 602 + if (buffer_size > hmd->max_sensor_buffer_size) { 603 + buffer_size = hmd->max_sensor_buffer_size; 604 + } 605 + 606 + while (os_hid_read(hmd->hid_sensor, buffer, buffer_size, 0) > 0) { 607 // Just drop the packets. 608 } 609 } ··· 612 sensor_read_one_packet(struct xreal_air_hmd *hmd) 613 { 614 uint8_t buffer[SENSOR_BUFFER_SIZE]; 615 + size_t buffer_size = SENSOR_BUFFER_SIZE; 616 617 + if (buffer_size > hmd->max_sensor_buffer_size) { 618 + buffer_size = hmd->max_sensor_buffer_size; 619 + } 620 + 621 + int size = os_hid_read(hmd->hid_sensor, buffer, buffer_size, 0); 622 if (size <= 0) { 623 return size; 624 } 625 626 + handle_sensor_msg(hmd, buffer, (size_t)size); 627 return 0; 628 } 629 ··· 742 } 743 744 static void 745 + handle_control_display_toggled(struct xreal_air_hmd *hmd, const struct xreal_air_parsed_control *control) 746 + { 747 + // State of display 748 + const uint8_t display_state = control->data[0]; 749 + 750 + hmd->display_on = (display_state != 0); 751 + } 752 + 753 + static void 754 handle_control_button(struct xreal_air_hmd *hmd, const struct xreal_air_parsed_control *control) 755 { 756 // Physical button ··· 776 hmd->wants.brightness = brightness; 777 break; 778 } 779 + case XREAL_AIR_BUTTON_VIRT_UP: { 780 + switch (hmd->control_mode) { 781 + case XREAL_AIR_CONTROL_MODE_BRIGHTNESS: break; 782 + case XREAL_AIR_CONTROL_MODE_VOLUME: break; 783 + default: { 784 + XREAL_AIR_ERROR(hmd, "Got unknown mode increase, 0x%02x (0x%02x)", hmd->control_mode, value); 785 + break; 786 + } 787 + } 788 789 + break; 790 + } 791 + case XREAL_AIR_BUTTON_VIRT_DOWN: { 792 + switch (hmd->control_mode) { 793 + case XREAL_AIR_CONTROL_MODE_BRIGHTNESS: break; 794 + case XREAL_AIR_CONTROL_MODE_VOLUME: break; 795 + default: { 796 + XREAL_AIR_ERROR(hmd, "Got unknown mode decrease, 0x%02x (0x%02x)", hmd->control_mode, value); 797 + break; 798 + } 799 } 800 801 break; 802 } 803 + case XREAL_AIR_BUTTON_VIRT_MODE_2D: { 804 const uint8_t display_mode = hmd->state.display_mode; 805 806 + if (display_mode != XREAL_AIR_DISPLAY_MODE_2D) { 807 hmd->wants.display_mode = XREAL_AIR_DISPLAY_MODE_2D; 808 } 809 810 + break; 811 + } 812 + case XREAL_AIR_BUTTON_VIRT_MODE_3D: { 813 + const uint8_t display_mode = hmd->state.display_mode; 814 + 815 + if (display_mode != XREAL_AIR_DISPLAY_MODE_3D) { 816 + hmd->wants.display_mode = XREAL_AIR_DISPLAY_MODE_3D; 817 + } 818 + 819 + break; 820 + } 821 + case XREAL_AIR_BUTTON_VIRT_BLEND_CYCLE: { 822 + hmd->blend_state = value; 823 + break; 824 + } 825 + case XREAL_AIR_BUTTON_VIRT_CONTROL_TOGGLE: { 826 + hmd->control_mode = value; 827 break; 828 } 829 default: { ··· 857 case XREAL_AIR_MSG_R_DISP_MODE: handle_control_display_mode(hmd, control); break; 858 case XREAL_AIR_MSG_W_DISP_MODE: break; 859 case XREAL_AIR_MSG_P_START_HEARTBEAT: handle_control_heartbeat_start(hmd, control); break; 860 + case XREAL_AIR_MSG_P_DISPLAY_TOGGLED: handle_control_display_toggled(hmd, control); break; 861 case XREAL_AIR_MSG_P_BUTTON_PRESSED: handle_control_button(hmd, control); break; 862 case XREAL_AIR_MSG_P_ASYNC_TEXT_LOG: handle_control_async_text(hmd, control); break; 863 case XREAL_AIR_MSG_P_END_HEARTBEAT: handle_control_heartbeat_end(hmd, control); break; ··· 1138 const enum xrt_space_relation_flags flags = (enum xrt_space_relation_flags)( 1139 XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT); 1140 1141 + struct xrt_space_relation relation = XRT_SPACE_RELATION_ZERO; 1142 relation.relation_flags = flags; 1143 1144 m_relation_history_get(hmd->relation_hist, at_timestamp_ns, &relation); 1145 relation.relation_flags = flags; // Needed after history_get 1146 1147 *out_relation = relation; 1148 + struct xrt_quat *orientation = &out_relation->pose.orientation; 1149 1150 // Make sure that the orientation is valid. 1151 + if (math_quat_dot(orientation, orientation) > 0.0f) { 1152 + math_quat_normalize(orientation); 1153 + } else { 1154 + orientation->w = 1.0f; 1155 + } 1156 + 1157 return XRT_SUCCESS; 1158 } 1159 ··· 1182 struct xrt_device * 1183 xreal_air_hmd_create_device(struct os_hid_device *sensor_device, 1184 struct os_hid_device *control_device, 1185 + enum u_logging_level log_level, 1186 + uint16_t max_sensor_buffer_size) 1187 { 1188 enum u_device_alloc_flags flags = 1189 (enum u_device_alloc_flags)(U_DEVICE_ALLOC_HMD | U_DEVICE_ALLOC_TRACKING_NONE); ··· 1191 int ret; 1192 1193 hmd->log_level = log_level; 1194 + hmd->max_sensor_buffer_size = max_sensor_buffer_size; 1195 hmd->base.update_inputs = xreal_air_hmd_update_inputs; 1196 hmd->base.get_tracked_pose = xreal_air_hmd_get_tracked_pose; 1197 hmd->base.get_view_poses = u_device_get_view_poses; ··· 1214 1215 hmd->static_id = 0; 1216 hmd->display_on = false; 1217 + hmd->blend_state = XREAL_AIR_BLEND_STATE_DEFAULT; 1218 + hmd->control_mode = XREAL_AIR_CONTROL_MODE_BRIGHTNESS; 1219 hmd->imu_stream_state = 0; 1220 1221 hmd->calibration_buffer = NULL;
+26 -11
src/xrt/drivers/xreal_air/xreal_air_hmd.h
··· 1 - // Copyright 2023-2024, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air packet parsing implementation. 6 - * @author Tobias Frisch <thejackimonster@gmail.com> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 20 extern "C" { 21 #endif 22 23 - #define XREAL_AIR_HANDLE_IFACE 3 24 - #define XREAL_AIR_CONTROL_IFACE 4 25 - 26 #define XREAL_AIR_MSG_R_BRIGHTNESS 0x03 27 #define XREAL_AIR_MSG_W_BRIGHTNESS 0x04 28 #define XREAL_AIR_MSG_R_DISP_MODE 0x07 29 #define XREAL_AIR_MSG_W_DISP_MODE 0x08 30 31 #define XREAL_AIR_MSG_P_START_HEARTBEAT 0x6c02 32 #define XREAL_AIR_MSG_P_BUTTON_PRESSED 0x6C05 33 #define XREAL_AIR_MSG_P_END_HEARTBEAT 0x6c12 34 #define XREAL_AIR_MSG_P_ASYNC_TEXT_LOG 0x6c09 ··· 41 #define XREAL_AIR_BUTTON_VIRT_MENU_TOGGLE 0x3 42 #define XREAL_AIR_BUTTON_VIRT_BRIGHTNESS_UP 0x6 43 #define XREAL_AIR_BUTTON_VIRT_BRIGHTNESS_DOWN 0x7 44 - #define XREAL_AIR_BUTTON_VIRT_MODE_UP 0x8 45 - #define XREAL_AIR_BUTTON_VIRT_MODE_DOWN 0x9 46 47 #define XREAL_AIR_BRIGHTNESS_MIN 0 48 #define XREAL_AIR_BRIGHTNESS_MAX 7 ··· 122 uint16_t length; 123 uint8_t msgid; 124 125 - uint8_t data[56]; 126 }; 127 128 /*! ··· 147 struct xrt_device * 148 xreal_air_hmd_create_device(struct os_hid_device *sensor_device, 149 struct os_hid_device *control_device, 150 - enum u_logging_level log_level); 151 152 bool 153 xreal_air_parse_calibration_buffer(struct xreal_air_parsed_calibration *calibration, const char *buffer, size_t size); 154 155 bool 156 - xreal_air_parse_sensor_packet(struct xreal_air_parsed_sensor *sensor, const uint8_t *buffer, int size); 157 158 bool 159 xreal_air_parse_sensor_control_data_packet(struct xreal_air_parsed_sensor_control_data *data, 160 const uint8_t *buffer, 161 - int size); 162 163 bool 164 xreal_air_parse_control_packet(struct xreal_air_parsed_control *control, const uint8_t *buffer, int size);
··· 1 + // Copyright 2023-2025, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air packet parsing implementation. 6 + * @author Tobias Frisch <jacki@thejackimonster.de> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 20 extern "C" { 21 #endif 22 23 #define XREAL_AIR_MSG_R_BRIGHTNESS 0x03 24 #define XREAL_AIR_MSG_W_BRIGHTNESS 0x04 25 #define XREAL_AIR_MSG_R_DISP_MODE 0x07 26 #define XREAL_AIR_MSG_W_DISP_MODE 0x08 27 28 #define XREAL_AIR_MSG_P_START_HEARTBEAT 0x6c02 29 + #define XREAL_AIR_MSG_P_DISPLAY_TOGGLED 0x6C04 30 #define XREAL_AIR_MSG_P_BUTTON_PRESSED 0x6C05 31 #define XREAL_AIR_MSG_P_END_HEARTBEAT 0x6c12 32 #define XREAL_AIR_MSG_P_ASYNC_TEXT_LOG 0x6c09 ··· 39 #define XREAL_AIR_BUTTON_VIRT_MENU_TOGGLE 0x3 40 #define XREAL_AIR_BUTTON_VIRT_BRIGHTNESS_UP 0x6 41 #define XREAL_AIR_BUTTON_VIRT_BRIGHTNESS_DOWN 0x7 42 + #define XREAL_AIR_BUTTON_VIRT_UP 0x8 43 + #define XREAL_AIR_BUTTON_VIRT_DOWN 0x9 44 + #define XREAL_AIR_BUTTON_VIRT_MODE_2D 0xA 45 + #define XREAL_AIR_BUTTON_VIRT_MODE_3D 0xB 46 + #define XREAL_AIR_BUTTON_VIRT_BLEND_CYCLE 0xC 47 + #define XREAL_AIR_BUTTON_VIRT_CONTROL_TOGGLE 0xF 48 + 49 + #define XREAL_AIR_BLEND_STATE_LOW 0x0 50 + #define XREAL_AIR_BLEND_STATE_DEFAULT 0x1 51 + #define XREAL_AIR_BLEND_STATE_MEDIUM 0x2 52 + #define XREAL_AIR_BLEND_STATE_FULL 0x3 53 + 54 + #define XREAL_AIR_CONTROL_MODE_BRIGHTNESS 0x0 55 + #define XREAL_AIR_CONTROL_MODE_VOLUME 0x1 56 57 #define XREAL_AIR_BRIGHTNESS_MIN 0 58 #define XREAL_AIR_BRIGHTNESS_MAX 7 ··· 132 uint16_t length; 133 uint8_t msgid; 134 135 + uint8_t data[512 - 8]; 136 }; 137 138 /*! ··· 157 struct xrt_device * 158 xreal_air_hmd_create_device(struct os_hid_device *sensor_device, 159 struct os_hid_device *control_device, 160 + enum u_logging_level log_level, 161 + uint16_t max_sensor_buffer_size); 162 163 bool 164 xreal_air_parse_calibration_buffer(struct xreal_air_parsed_calibration *calibration, const char *buffer, size_t size); 165 166 bool 167 + xreal_air_parse_sensor_packet(struct xreal_air_parsed_sensor *sensor, 168 + const uint8_t *buffer, 169 + size_t size, 170 + size_t max_size); 171 172 bool 173 xreal_air_parse_sensor_control_data_packet(struct xreal_air_parsed_sensor_control_data *data, 174 const uint8_t *buffer, 175 + size_t size, 176 + size_t max_size); 177 178 bool 179 xreal_air_parse_control_packet(struct xreal_air_parsed_control *control, const uint8_t *buffer, int size);
+9 -2
src/xrt/drivers/xreal_air/xreal_air_interface.h
··· 1 - // Copyright 2023-2024, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air packet parsing implementation. 6 - * @author Tobias Frisch <thejackimonster@gmail.com> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 47 * @ingroup drv_xreal_air 48 */ 49 #define XREAL_AIR_2_PRO_PID 0x0432 50 51 /*! 52 * Builder setup for Xreal Air glasses.
··· 1 + // Copyright 2023-2025, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air packet parsing implementation. 6 + * @author Tobias Frisch <jacki@thejackimonster.de> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 47 * @ingroup drv_xreal_air 48 */ 49 #define XREAL_AIR_2_PRO_PID 0x0432 50 + 51 + /*! 52 + * Product id for Xreal Air 2 Ultra. 53 + * 54 + * @ingroup drv_xreal_air 55 + */ 56 + #define XREAL_AIR_2_ULTRA_PID 0x0426 57 58 /*! 59 * Builder setup for Xreal Air glasses.
+41 -21
src/xrt/drivers/xreal_air/xreal_air_packet.c
··· 1 - // Copyright 2023-2024, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air packet parsing implementation. 6 - * @author Tobias Frisch <thejackimonster@gmail.com> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 227 read_i15_to_i32(buffer, &sample->mag.z); 228 } 229 230 231 /* 232 * 233 * Exported functions. 234 * 235 */ 236 237 bool 238 xreal_air_parse_calibration_buffer(struct xreal_air_parsed_calibration *calibration, const char *buffer, size_t size) 239 { 240 cJSON *root = cJSON_ParseWithLength(buffer, size); 241 - 242 cJSON *imu = cJSON_GetObjectItem(root, "IMU"); 243 - cJSON *dev1 = cJSON_GetObjectItem(imu, "device_1"); 244 245 - read_json_vec3(dev1, "accel_bias", &calibration->accel_bias); 246 - read_json_quat(dev1, "accel_q_gyro", &calibration->accel_q_gyro); 247 - read_json_vec3(dev1, "gyro_bias", &calibration->gyro_bias); 248 - read_json_quat(dev1, "gyro_q_mag", &calibration->gyro_q_mag); 249 - read_json_vec3(dev1, "mag_bias", &calibration->mag_bias); 250 - 251 - read_json_vec3(dev1, "scale_accel", &calibration->scale_accel); 252 - read_json_vec3(dev1, "scale_gyro", &calibration->scale_gyro); 253 - read_json_vec3(dev1, "scale_mag", &calibration->scale_mag); 254 255 - read_json_array(dev1, "imu_noises", 4, calibration->imu_noises); 256 257 cJSON_Delete(root); 258 - return true; 259 } 260 261 bool 262 - xreal_air_parse_sensor_packet(struct xreal_air_parsed_sensor *sensor, const uint8_t *buffer, int size) 263 { 264 const uint8_t *start = buffer; 265 266 - if (size != 64) { 267 return false; 268 } 269 ··· 295 bool 296 xreal_air_parse_sensor_control_data_packet(struct xreal_air_parsed_sensor_control_data *data, 297 const uint8_t *buffer, 298 - int size) 299 { 300 const uint8_t *start = buffer; 301 302 - if (size != 64) { 303 return false; 304 } 305 ··· 316 read_u8(&buffer, &data->msgid); 317 318 // Sensor control data depending on action 319 - read_u8_array(&buffer, data->data, 56); 320 321 - return (size_t)buffer - (size_t)start == 64; 322 } 323 324 bool
··· 1 + // Copyright 2023-2025, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air packet parsing implementation. 6 + * @author Tobias Frisch <jacki@thejackimonster.de> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 227 read_i15_to_i32(buffer, &sample->mag.z); 228 } 229 230 + static void 231 + parse_calibration_json(struct xreal_air_parsed_calibration *calibration, cJSON *dev1) 232 + { 233 + read_json_vec3(dev1, "accel_bias", &calibration->accel_bias); 234 + read_json_quat(dev1, "accel_q_gyro", &calibration->accel_q_gyro); 235 + read_json_vec3(dev1, "gyro_bias", &calibration->gyro_bias); 236 + read_json_quat(dev1, "gyro_q_mag", &calibration->gyro_q_mag); 237 + read_json_vec3(dev1, "mag_bias", &calibration->mag_bias); 238 + 239 + read_json_vec3(dev1, "scale_accel", &calibration->scale_accel); 240 + read_json_vec3(dev1, "scale_gyro", &calibration->scale_gyro); 241 + read_json_vec3(dev1, "scale_mag", &calibration->scale_mag); 242 + 243 + read_json_array(dev1, "imu_noises", 4, calibration->imu_noises); 244 + } 245 + 246 247 /* 248 * 249 * Exported functions. 250 * 251 */ 252 + #include <stdio.h> 253 254 bool 255 xreal_air_parse_calibration_buffer(struct xreal_air_parsed_calibration *calibration, const char *buffer, size_t size) 256 { 257 + bool result = false; 258 + 259 cJSON *root = cJSON_ParseWithLength(buffer, size); 260 cJSON *imu = cJSON_GetObjectItem(root, "IMU"); 261 262 + if (imu) { 263 + cJSON *dev1 = cJSON_GetObjectItem(imu, "device_1"); 264 265 + if (dev1) { 266 + parse_calibration_json(calibration, dev1); 267 + result = true; 268 + } 269 + } 270 271 cJSON_Delete(root); 272 + return result; 273 } 274 275 + #include <stdio.h> 276 + 277 bool 278 + xreal_air_parse_sensor_packet(struct xreal_air_parsed_sensor *sensor, 279 + const uint8_t *buffer, 280 + size_t size, 281 + size_t max_size) 282 { 283 const uint8_t *start = buffer; 284 285 + if ((size != max_size) || (size < 64)) { 286 return false; 287 } 288 ··· 314 bool 315 xreal_air_parse_sensor_control_data_packet(struct xreal_air_parsed_sensor_control_data *data, 316 const uint8_t *buffer, 317 + size_t size, 318 + size_t max_size) 319 { 320 const uint8_t *start = buffer; 321 322 + if ((size != max_size) || (size < 8)) { 323 return false; 324 } 325 ··· 336 read_u8(&buffer, &data->msgid); 337 338 // Sensor control data depending on action 339 + read_u8_array(&buffer, data->data, size - 8); 340 341 + return (size_t)buffer - (size_t)start == max_size; 342 } 343 344 bool
+29 -6
src/xrt/targets/common/target_builder_xreal_air.c
··· 1 - // Copyright 2023-2024, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air prober code. 6 - * @author Tobias Frisch <thejackimonster@gmail.com> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 46 "xreal_air", 47 }; 48 49 - #define XREAL_AIR_DRIVER_PRODUCT_IDS 3 50 51 static const uint16_t driver_product_ids[XREAL_AIR_DRIVER_PRODUCT_IDS] = { 52 XREAL_AIR_PID, 53 XREAL_AIR_2_PID, 54 XREAL_AIR_2_PRO_PID, 55 }; 56 57 ··· 142 } 143 144 struct os_hid_device *hid_handle = NULL; 145 - int result = xrt_prober_open_hid_interface(xp, dev_hmd, XREAL_AIR_HANDLE_IFACE, &hid_handle); 146 147 if (result != 0) { 148 XREAL_AIR_ERROR("Failed to open Xreal Air handle interface"); ··· 150 } 151 152 struct os_hid_device *hid_control = NULL; 153 - result = xrt_prober_open_hid_interface(xp, dev_hmd, XREAL_AIR_CONTROL_IFACE, &hid_control); 154 155 if (result != 0) { 156 os_hid_destroy(hid_handle); ··· 172 goto fail; 173 } 174 175 - struct xrt_device *xreal_air_device = xreal_air_hmd_create_device(hid_handle, hid_control, xreal_air_log_level); 176 177 if (xreal_air_device == NULL) { 178 XREAL_AIR_ERROR("Failed to initialise Xreal Air driver");
··· 1 + // Copyright 2023-2025, Tobias Frisch 2 // SPDX-License-Identifier: BSL-1.0 3 /*! 4 * @file 5 * @brief Xreal Air prober code. 6 + * @author Tobias Frisch <jacki@thejackimonster.de> 7 * @ingroup drv_xreal_air 8 */ 9 ··· 46 "xreal_air", 47 }; 48 49 + #define XREAL_AIR_DRIVER_PRODUCT_IDS 4 50 51 static const uint16_t driver_product_ids[XREAL_AIR_DRIVER_PRODUCT_IDS] = { 52 XREAL_AIR_PID, 53 XREAL_AIR_2_PID, 54 XREAL_AIR_2_PRO_PID, 55 + XREAL_AIR_2_ULTRA_PID, 56 + }; 57 + 58 + static const uint16_t driver_handle_ifaces[XREAL_AIR_DRIVER_PRODUCT_IDS] = { 59 + 3, 60 + 3, 61 + 3, 62 + 2, 63 + }; 64 + 65 + static const uint16_t driver_control_ifaces[XREAL_AIR_DRIVER_PRODUCT_IDS] = { 66 + 4, 67 + 4, 68 + 4, 69 + 0, 70 + }; 71 + 72 + static const uint16_t driver_max_sensor_buffer_sizes[XREAL_AIR_DRIVER_PRODUCT_IDS] = { 73 + 64, 74 + 64, 75 + 64, 76 + 512, 77 }; 78 79 ··· 164 } 165 166 struct os_hid_device *hid_handle = NULL; 167 + int result = xrt_prober_open_hid_interface(xp, dev_hmd, driver_handle_ifaces[product_index], &hid_handle); 168 169 if (result != 0) { 170 XREAL_AIR_ERROR("Failed to open Xreal Air handle interface"); ··· 172 } 173 174 struct os_hid_device *hid_control = NULL; 175 + result = xrt_prober_open_hid_interface(xp, dev_hmd, driver_control_ifaces[product_index], &hid_control); 176 177 if (result != 0) { 178 os_hid_destroy(hid_handle); ··· 194 goto fail; 195 } 196 197 + struct xrt_device *xreal_air_device = xreal_air_hmd_create_device( 198 + hid_handle, hid_control, xreal_air_log_level, driver_max_sensor_buffer_sizes[product_index]); 199 200 if (xreal_air_device == NULL) { 201 XREAL_AIR_ERROR("Failed to initialise Xreal Air driver");