// Copyright 2023 Jan Schmidt // SPDX-License-Identifier: BSL-1.0 /*! * @file * @brief Implementation of tunnelled controller connection, * that translates messages passing via an HP G2 or Sasmung Odyssey+ HMD * @author Jan Schmidt * @ingroup drv_wmr */ #include "util/u_trace_marker.h" #include "wmr_hmd_controller.h" #include "wmr_controller.h" #include "wmr_hmd.h" #define WMR_TRACE(c, ...) U_LOG_IFL_T(c->log_level, __VA_ARGS__) #define WMR_DEBUG(c, ...) U_LOG_IFL_D(c->log_level, __VA_ARGS__) #define WMR_INFO(c, ...) U_LOG_IFL_I(c->log_level, __VA_ARGS__) #define WMR_WARN(c, ...) U_LOG_IFL_W(c->log_level, __VA_ARGS__) #define WMR_ERROR(c, ...) U_LOG_IFL_E(c->log_level, __VA_ARGS__) /* A note: * This HMD controller connection object is used for controllers * where the communication is tunnelled through HMD packets. It * handles translating the controller packet IDs to raw * IDs when receiving data from the HMD, and back to HMD packet IDs * when sending data to the controller. * * Both the HMD and the controller hold a reference to this * connection object. The HMD can pass received packets at * any time, and will call wmr_controller_connection_disconnect() * when the HMD xrt_device is freed by the runtime. * * The controller may send packets based on calls from the * runtime (triggering haptics, for example), so can also * want to send packets at any time. It will also call * wmr_controller_connection_disconnect() * when the controller xrt_device is freed by the runtime. * * The conn_lock protects access to the HMD and controller * pointers while making calls to send/receive, to prevent * from invalid access if _disconnect() is called. */ static bool send_bytes_to_controller(struct wmr_controller_connection *wcc, const uint8_t *buffer, uint32_t buf_size) { struct wmr_hmd_controller_connection *conn = (struct wmr_hmd_controller_connection *)(wcc); bool res = false; os_mutex_lock(&conn->lock); if (!conn->disconnected && buf_size > 0) { uint8_t outbuf[64]; assert(buf_size <= sizeof(outbuf)); memcpy(outbuf, buffer, buf_size); outbuf[0] += conn->hmd_cmd_base; res = wmr_hmd_send_controller_packet(conn->hmd, outbuf, buf_size); } os_mutex_unlock(&conn->lock); return res; } static int read_sync_from_controller(struct wmr_controller_connection *wcc, uint8_t *buffer, uint32_t buf_size, int timeout_ms) { struct wmr_hmd_controller_connection *conn = (struct wmr_hmd_controller_connection *)(wcc); int res = -1; os_mutex_lock(&conn->lock); if (!conn->disconnected && buf_size > 0) { res = wmr_hmd_read_sync_from_controller(conn->hmd, buffer, buf_size, timeout_ms); if (res > 0) { buffer[0] -= conn->hmd_cmd_base; } } os_mutex_unlock(&conn->lock); return res; } static void receive_bytes_from_controller(struct wmr_controller_connection *wcc, uint64_t time_ns, uint8_t *buffer, uint32_t buf_size) { struct wmr_hmd_controller_connection *conn = (struct wmr_hmd_controller_connection *)(wcc); os_mutex_lock(&conn->lock); if (!conn->disconnected && buf_size > 0) { buffer[0] -= conn->hmd_cmd_base; struct wmr_controller_base *wcb = wcc->wcb; assert(wcb->receive_bytes != NULL); wcb->receive_bytes(wcb, time_ns, buffer, buf_size); } os_mutex_unlock(&conn->lock); } static void wmr_hmd_controller_connection_destroy(struct wmr_hmd_controller_connection *conn) { DRV_TRACE_MARKER(); os_mutex_destroy(&conn->lock); free(conn); } static void wmr_hmd_controller_connection_disconnect(struct wmr_controller_connection *base) { struct wmr_hmd_controller_connection *conn = (struct wmr_hmd_controller_connection *)(base); if (xrt_reference_dec_and_is_zero(&conn->ref)) { wmr_hmd_controller_connection_destroy(conn); } else { os_mutex_lock(&conn->lock); conn->disconnected = true; conn->base.wcb = NULL; os_mutex_unlock(&conn->lock); } } struct wmr_hmd_controller_connection * wmr_hmd_controller_create(struct wmr_hmd *hmd, uint8_t hmd_cmd_base, enum xrt_device_type controller_type, uint16_t vid, uint16_t pid, enum u_logging_level log_level) { DRV_TRACE_MARKER(); struct wmr_hmd_controller_connection *conn = calloc(1, sizeof(struct wmr_hmd_controller_connection)); conn->log_level = log_level; conn->hmd = hmd; conn->hmd_cmd_base = hmd_cmd_base; conn->base.receive_bytes = receive_bytes_from_controller; conn->base.send_bytes = send_bytes_to_controller; conn->base.read_sync = read_sync_from_controller; conn->base.disconnect = wmr_hmd_controller_connection_disconnect; /* Init 2 references - one for the controller, one for the HMD */ xrt_reference_inc(&conn->ref); xrt_reference_inc(&conn->ref); int ret = 0; ret = os_mutex_init(&conn->lock); if (ret != 0) { WMR_ERROR(conn, "WMR Controller (Tunnelled): Failed to init mutex!"); wmr_hmd_controller_connection_destroy(conn); return NULL; } // Takes ownership of one reference to the connection, the other will // belong to the returned pointer struct wmr_controller_base *wcb = wmr_controller_create(&conn->base, controller_type, vid, pid, log_level); if (wcb == NULL) { WMR_ERROR(conn, "WMR Controller (Tunnelled): Failed to create controller"); wmr_hmd_controller_connection_destroy(conn); return NULL; } // If the controller device was created, the connection belongs to // it now and will be cleaned up when it calls disconnect(). conn->base.wcb = wcb; return conn; } struct xrt_device * wmr_hmd_controller_connection_get_controller(struct wmr_hmd_controller_connection *wcc) { struct wmr_controller_base *wcb = wcc->base.wcb; if (wcb == NULL) return NULL; struct xrt_device *xdev = &wcb->base; return xdev; }