Files
lbgos--rift-s-linux/src/xrt/ipc/server/ipc_server_process.c
T

1069 lines
27 KiB
C

// Copyright 2020-2023, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Server process functions.
* @author Pete Black <pblack@collabora.com>
* @author Jakob Bornecrantz <jakob@collabora.com>
* @author Rylie Pavlik <rylie.pavlik@collabora.com>
* @ingroup ipc_server
*/
#include "xrt/xrt_device.h"
#include "xrt/xrt_system.h"
#include "xrt/xrt_instance.h"
#include "xrt/xrt_compositor.h"
#include "xrt/xrt_config_have.h"
#include "xrt/xrt_config_os.h"
#include "os/os_time.h"
#include "util/u_var.h"
#include "util/u_misc.h"
#include "util/u_debug.h"
#include "util/u_trace_marker.h"
#include "util/u_verify.h"
#include "util/u_process.h"
#include "util/u_debug_gui.h"
#include "util/u_pretty_print.h"
#include "util/u_git_tag.h"
#include "shared/ipc_shmem.h"
#include "server/ipc_server.h"
#include "server/ipc_server_interface.h"
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#if defined(XRT_OS_WINDOWS)
#include <timeapi.h>
#endif
/*
*
* Defines and helpers.
*
*/
DEBUG_GET_ONCE_BOOL_OPTION(exit_on_disconnect, "IPC_EXIT_ON_DISCONNECT", false)
DEBUG_GET_ONCE_LOG_OPTION(ipc_log, "IPC_LOG", U_LOGGING_INFO)
/*
*
* Idev functions.
*
*/
static int32_t
find_xdev_index(struct ipc_server *s, struct xrt_device *xdev)
{
if (xdev == NULL) {
return -1;
}
for (int32_t i = 0; i < XRT_SYSTEM_MAX_DEVICES; i++) {
if (s->xsysd->xdevs[i] == xdev) {
return i;
}
}
IPC_WARN(s, "Could not find index for xdev: '%s'", xdev->str);
return -1;
}
static void
init_idev(struct ipc_device *idev, struct xrt_device *xdev)
{
if (xdev != NULL) {
idev->io_active = true;
idev->xdev = xdev;
} else {
idev->io_active = false;
}
}
static void
teardown_idev(struct ipc_device *idev)
{
idev->io_active = false;
}
static int
init_idevs(struct ipc_server *s)
{
// Copy the devices over into the idevs array.
for (size_t i = 0; i < XRT_SYSTEM_MAX_DEVICES; i++) {
if (s->xsysd->xdevs[i] == NULL) {
continue;
}
init_idev(&s->idevs[i], s->xsysd->xdevs[i]);
}
return 0;
}
static void
teardown_idevs(struct ipc_server *s)
{
for (size_t i = 0; i < XRT_SYSTEM_MAX_DEVICES; i++) {
teardown_idev(&s->idevs[i]);
}
}
/*
*
* Static functions.
*
*/
XRT_MAYBE_UNUSED static void
print_linux_end_user_failed_information(enum u_logging_level log_level)
{
struct u_pp_sink_stack_only sink;
u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink);
// Print Newline
#define PN() u_pp(dg, "\n")
// Print Newline, Hash, Space
#define PNH() u_pp(dg, "\n#")
// Print Newline, Hash, Space
#define PNHS(...) u_pp(dg, "\n# "__VA_ARGS__)
// Print Newline, 80 Hashes
#define PN80H() \
do { \
PN(); \
for (uint32_t i = 0; i < 8; i++) { \
u_pp(dg, "##########"); \
} \
} while (false)
PN80H();
PNHS(" #");
PNHS(" The Monado service has failed to start. #");
PNHS(" #");
PNHS("If you want to report please upload the logs of the service as a text file. #");
PNHS("You can also capture the output the monado-cli info command to provide more #");
PNHS("information about your system, that will help diagnosing your problem. The #");
PNHS("below commands is how you best capture the information from the commands. #");
PNHS(" #");
PNHS(" monado-cli info 2>&1 | tee info.txt #");
PNHS(" monado-service 2>&1 | tee logs.txt #");
PNHS(" #");
PN80H();
U_LOG_IFL_I(log_level, "%s", sink.buffer);
}
XRT_MAYBE_UNUSED static void
print_linux_end_user_started_information(enum u_logging_level log_level)
{
struct u_pp_sink_stack_only sink;
u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink);
PN80H();
PNHS(" #");
PNHS(" The Monado service has started. #");
PNHS(" #");
PN80H();
#undef PN
#undef PNH
#undef PNHS
#undef PN80H
U_LOG_IFL_I(log_level, "%s", sink.buffer);
}
static void
teardown_all(struct ipc_server *s)
{
u_var_remove_root(s);
xrt_syscomp_destroy(&s->xsysc);
teardown_idevs(s);
xrt_space_overseer_destroy(&s->xso);
xrt_system_devices_destroy(&s->xsysd);
xrt_system_destroy(&s->xsys);
xrt_instance_destroy(&s->xinst);
ipc_server_mainloop_deinit(&s->ml);
u_process_destroy(s->process);
ipc_shmem_destroy(&s->ism_handle, (void **)&s->ism, sizeof(struct ipc_shared_memory));
// Destroyed last.
os_mutex_destroy(&s->global_state.lock);
}
static int
init_tracking_origins(struct ipc_server *s)
{
for (size_t i = 0; i < XRT_SYSTEM_MAX_DEVICES; i++) {
struct xrt_device *xdev = s->idevs[i].xdev;
if (xdev == NULL) {
continue;
}
struct xrt_tracking_origin *xtrack = xdev->tracking_origin;
assert(xtrack != NULL);
size_t index = 0;
for (; index < XRT_SYSTEM_MAX_DEVICES; index++) {
if (s->xtracks[index] == NULL) {
s->xtracks[index] = xtrack;
break;
}
if (s->xtracks[index] == xtrack) {
break;
}
}
}
return 0;
}
static void
handle_binding(struct ipc_shared_memory *ism,
struct xrt_binding_profile *xbp,
struct ipc_shared_binding_profile *isbp,
uint32_t *input_pair_index_ptr,
uint32_t *output_pair_index_ptr)
{
uint32_t input_pair_index = *input_pair_index_ptr;
uint32_t output_pair_index = *output_pair_index_ptr;
isbp->name = xbp->name;
// Copy the initial state and also count the number in input_pairs.
uint32_t input_pair_start = input_pair_index;
for (size_t k = 0; k < xbp->input_count; k++) {
ism->input_pairs[input_pair_index++] = xbp->inputs[k];
}
// Setup the 'offsets' and number of input_pairs.
if (input_pair_start != input_pair_index) {
isbp->input_count = input_pair_index - input_pair_start;
isbp->first_input_index = input_pair_start;
}
// Copy the initial state and also count the number in outputs.
uint32_t output_pair_start = output_pair_index;
for (size_t k = 0; k < xbp->output_count; k++) {
ism->output_pairs[output_pair_index++] = xbp->outputs[k];
}
// Setup the 'offsets' and number of output_pairs.
if (output_pair_start != output_pair_index) {
isbp->output_count = output_pair_index - output_pair_start;
isbp->first_output_index = output_pair_start;
}
*input_pair_index_ptr = input_pair_index;
*output_pair_index_ptr = output_pair_index;
}
static int
init_shm(struct ipc_server *s)
{
const size_t size = sizeof(struct ipc_shared_memory);
xrt_shmem_handle_t handle;
xrt_result_t result = ipc_shmem_create(size, &handle, (void **)&s->ism);
if (result != XRT_SUCCESS) {
return -1;
}
// we have a filehandle, we will pass this to our client
s->ism_handle = handle;
/*
*
* Setup the shared memory state.
*
*/
uint32_t count = 0;
struct ipc_shared_memory *ism = s->ism;
ism->startup_timestamp = os_monotonic_get_ns();
// Setup the tracking origins.
count = 0;
for (size_t i = 0; i < XRT_SYSTEM_MAX_DEVICES; i++) {
struct xrt_tracking_origin *xtrack = s->xtracks[i];
if (xtrack == NULL) {
continue;
}
// The position of the tracking origin matches that in the
// server's memory.
assert(i < XRT_SYSTEM_MAX_DEVICES);
struct ipc_shared_tracking_origin *itrack = &ism->itracks[count++];
memcpy(itrack->name, xtrack->name, sizeof(itrack->name));
itrack->type = xtrack->type;
itrack->offset = xtrack->offset;
}
ism->itrack_count = count;
count = 0;
uint32_t input_index = 0;
uint32_t output_index = 0;
uint32_t binding_index = 0;
uint32_t input_pair_index = 0;
uint32_t output_pair_index = 0;
for (size_t i = 0; i < XRT_SYSTEM_MAX_DEVICES; i++) {
struct xrt_device *xdev = s->idevs[i].xdev;
if (xdev == NULL) {
continue;
}
struct ipc_shared_device *isdev = &ism->isdevs[count++];
isdev->name = xdev->name;
memcpy(isdev->str, xdev->str, sizeof(isdev->str));
memcpy(isdev->serial, xdev->serial, sizeof(isdev->serial));
isdev->orientation_tracking_supported = xdev->orientation_tracking_supported;
isdev->position_tracking_supported = xdev->position_tracking_supported;
isdev->device_type = xdev->device_type;
isdev->hand_tracking_supported = xdev->hand_tracking_supported;
isdev->force_feedback_supported = xdev->force_feedback_supported;
isdev->form_factor_check_supported = xdev->form_factor_check_supported;
isdev->eye_gaze_supported = xdev->eye_gaze_supported;
isdev->stage_supported = xdev->stage_supported;
// Is this a HMD?
if (xdev->hmd != NULL) {
ism->hmd.views[0].display.w_pixels = xdev->hmd->views[0].display.w_pixels;
ism->hmd.views[0].display.h_pixels = xdev->hmd->views[0].display.h_pixels;
ism->hmd.views[1].display.w_pixels = xdev->hmd->views[1].display.w_pixels;
ism->hmd.views[1].display.h_pixels = xdev->hmd->views[1].display.h_pixels;
for (size_t i = 0; i < xdev->hmd->blend_mode_count; i++) {
// Not super necessary, we also do this assert in oxr_system.c
assert(u_verify_blend_mode_valid(xdev->hmd->blend_modes[i]));
ism->hmd.blend_modes[i] = xdev->hmd->blend_modes[i];
}
ism->hmd.blend_mode_count = xdev->hmd->blend_mode_count;
}
// Setup the tracking origin.
isdev->tracking_origin_index = (uint32_t)-1;
for (uint32_t k = 0; k < XRT_SYSTEM_MAX_DEVICES; k++) {
if (xdev->tracking_origin != s->xtracks[k]) {
continue;
}
isdev->tracking_origin_index = k;
break;
}
assert(isdev->tracking_origin_index != (uint32_t)-1);
// Initial update.
xrt_device_update_inputs(xdev);
// Bindings
uint32_t binding_start = binding_index;
for (size_t k = 0; k < xdev->binding_profile_count; k++) {
handle_binding(ism, &xdev->binding_profiles[k], &ism->binding_profiles[binding_index++],
&input_pair_index, &output_pair_index);
}
// Setup the 'offsets' and number of bindings.
if (binding_start != binding_index) {
isdev->binding_profile_count = binding_index - binding_start;
isdev->first_binding_profile_index = binding_start;
}
// Copy the initial state and also count the number in inputs.
uint32_t input_start = input_index;
for (size_t k = 0; k < xdev->input_count; k++) {
ism->inputs[input_index++] = xdev->inputs[k];
}
// Setup the 'offsets' and number of inputs.
if (input_start != input_index) {
isdev->input_count = input_index - input_start;
isdev->first_input_index = input_start;
}
// Copy the initial state and also count the number in outputs.
uint32_t output_start = output_index;
for (size_t k = 0; k < xdev->output_count; k++) {
ism->outputs[output_index++] = xdev->outputs[k];
}
// Setup the 'offsets' and number of outputs.
if (output_start != output_index) {
isdev->output_count = output_index - output_start;
isdev->first_output_index = output_start;
}
}
// Finally tell the client how many devices we have.
s->ism->isdev_count = count;
// Assign all of the roles.
ism->roles.head = find_xdev_index(s, s->xsysd->static_roles.head);
ism->roles.eyes = find_xdev_index(s, s->xsysd->static_roles.eyes);
ism->roles.hand_tracking.left = find_xdev_index(s, s->xsysd->static_roles.hand_tracking.left);
ism->roles.hand_tracking.right = find_xdev_index(s, s->xsysd->static_roles.hand_tracking.right);
// Fill out git version info.
snprintf(s->ism->u_git_tag, IPC_VERSION_NAME_LEN, "%s", u_git_tag);
return 0;
}
static void
init_server_state(struct ipc_server *s)
{
// set up initial state for global vars, and each client state
s->global_state.active_client_index = -1; // we start off with no active client.
s->global_state.last_active_client_index = -1;
s->current_slot_index = 0;
for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
volatile struct ipc_client_state *ics = &s->threads[i].ics;
ics->server = s;
ics->server_thread_index = -1;
}
}
static int
init_all(struct ipc_server *s, enum u_logging_level log_level)
{
xrt_result_t xret;
int ret;
// First order of business set the log level.
s->log_level = log_level;
// This should never fail.
ret = os_mutex_init(&s->global_state.lock);
if (ret < 0) {
IPC_ERROR(s, "Global state lock mutex failed to init!");
// Do not call teardown_all here, os_mutex_destroy will assert.
return ret;
}
s->process = u_process_create_if_not_running();
if (!s->process) {
IPC_ERROR(s, "monado-service is already running! Use XRT_LOG=trace for more information.");
teardown_all(s);
return 1;
}
// Yes we should be running.
s->running = true;
s->exit_on_disconnect = debug_get_bool_option_exit_on_disconnect();
xret = xrt_instance_create(NULL, &s->xinst);
if (xret != XRT_SUCCESS) {
IPC_ERROR(s, "Failed to create instance!");
teardown_all(s);
return -1;
}
xret = xrt_instance_create_system(s->xinst, &s->xsys, &s->xsysd, &s->xso, &s->xsysc);
if (xret != XRT_SUCCESS) {
IPC_ERROR(s, "Could not create system!");
teardown_all(s);
return -1;
}
ret = init_idevs(s);
if (ret < 0) {
IPC_ERROR(s, "Failed to init idevs!");
teardown_all(s);
return ret;
}
ret = init_tracking_origins(s);
if (ret < 0) {
IPC_ERROR(s, "Failed to init tracking origins!");
teardown_all(s);
return ret;
}
ret = init_shm(s);
if (ret < 0) {
IPC_ERROR(s, "Could not init shared memory!");
teardown_all(s);
return ret;
}
ret = ipc_server_mainloop_init(&s->ml);
if (ret < 0) {
IPC_ERROR(s, "Failed to init ipc main loop!");
teardown_all(s);
return ret;
}
// Never fails, do this second last.
init_server_state(s);
u_var_add_root(s, "IPC Server", false);
u_var_add_log_level(s, &s->log_level, "Log level");
u_var_add_bool(s, &s->exit_on_disconnect, "exit_on_disconnect");
u_var_add_bool(s, (bool *)&s->running, "running");
return 0;
}
static int
main_loop(struct ipc_server *s)
{
while (s->running) {
os_nanosleep(U_TIME_1S_IN_NS / 20);
// Check polling.
ipc_server_mainloop_poll(s, &s->ml);
}
return 0;
}
/*
*
* Client management functions.
*
*/
static void
handle_overlay_client_events(volatile struct ipc_client_state *ics, int active_id, int prev_active_id)
{
// Is an overlay session?
if (!ics->client_state.session_overlay) {
return;
}
// Does this client have a compositor yet, if not return?
if (ics->xc == NULL) {
return;
}
// Switch between main applications
if (active_id >= 0 && prev_active_id >= 0) {
xrt_syscomp_set_main_app_visibility(ics->server->xsysc, ics->xc, false);
xrt_syscomp_set_main_app_visibility(ics->server->xsysc, ics->xc, true);
}
// Switch from idle to active application
if (active_id >= 0 && prev_active_id < 0) {
xrt_syscomp_set_main_app_visibility(ics->server->xsysc, ics->xc, true);
}
// Switch from active application to idle
if (active_id < 0 && prev_active_id >= 0) {
xrt_syscomp_set_main_app_visibility(ics->server->xsysc, ics->xc, false);
}
}
static void
handle_focused_client_events(volatile struct ipc_client_state *ics, int active_id, int prev_active_id)
{
// Set start z_order at the bottom.
int64_t z_order = INT64_MIN;
// Set visibility/focus to false on all applications.
bool focused = false;
bool visible = false;
// Set visible + focused if we are the primary application
if (ics->server_thread_index == active_id) {
visible = true;
focused = true;
z_order = INT64_MIN;
}
// Set all overlays to always active and focused.
if (ics->client_state.session_overlay) {
visible = true;
focused = true;
z_order = ics->client_state.z_order;
}
ics->client_state.session_visible = visible;
ics->client_state.session_focused = focused;
ics->client_state.z_order = z_order;
if (ics->xc != NULL) {
xrt_syscomp_set_state(ics->server->xsysc, ics->xc, visible, focused);
xrt_syscomp_set_z_order(ics->server->xsysc, ics->xc, z_order);
}
}
static void
flush_state_to_all_clients_locked(struct ipc_server *s)
{
for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
volatile struct ipc_client_state *ics = &s->threads[i].ics;
// Not running?
if (ics->server_thread_index < 0) {
continue;
}
handle_focused_client_events(ics, s->global_state.active_client_index,
s->global_state.last_active_client_index);
handle_overlay_client_events(ics, s->global_state.active_client_index,
s->global_state.last_active_client_index);
}
}
static void
update_server_state_locked(struct ipc_server *s)
{
// if our client that is set to active is still active,
// and it is the same as our last active client, we can
// early-out, as no events need to be sent
if (s->global_state.active_client_index >= 0) {
volatile struct ipc_client_state *ics = &s->threads[s->global_state.active_client_index].ics;
if (ics->client_state.session_active &&
s->global_state.active_client_index == s->global_state.last_active_client_index) {
return;
}
}
// our active application has changed - this would typically be
// switched by the monado-ctl application or other app making a
// 'set active application' ipc call, or it could be a
// connection loss resulting in us needing to 'fall through' to
// the first active application
//, or finally to the idle 'wallpaper' images.
bool set_idle = true;
int fallback_active_application = -1;
// do we have a fallback application?
for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
volatile struct ipc_client_state *ics = &s->threads[i].ics;
if (ics->client_state.session_overlay == false && ics->server_thread_index >= 0 &&
ics->client_state.session_active) {
fallback_active_application = i;
set_idle = false;
}
}
// if there is a currently-set active primary application and it is not
// actually active/displayable, use the fallback application
// instead.
if (s->global_state.active_client_index >= 0) {
volatile struct ipc_client_state *ics = &s->threads[s->global_state.active_client_index].ics;
if (!(ics->client_state.session_overlay == false && ics->client_state.session_active)) {
s->global_state.active_client_index = fallback_active_application;
}
}
// if we have no applications to fallback to, enable the idle
// wallpaper.
if (set_idle) {
s->global_state.active_client_index = -1;
}
flush_state_to_all_clients_locked(s);
s->global_state.last_active_client_index = s->global_state.active_client_index;
}
static volatile struct ipc_client_state *
find_client_locked(struct ipc_server *s, uint32_t client_id)
{
// Check for invalid IDs.
if (client_id == 0 || client_id > INT_MAX) {
IPC_WARN(s, "Invalid ID '%u', failing operation.", client_id);
return NULL;
}
for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
volatile struct ipc_client_state *ics = &s->threads[i].ics;
// Is this the client we are looking for?
if (ics->client_state.id != client_id) {
continue;
}
// Just in case of state data.
if (!xrt_ipc_handle_is_valid(ics->imc.ipc_handle)) {
IPC_WARN(s, "Encountered invalid state while searching for client with ID '%d'", client_id);
return NULL;
}
return ics;
}
IPC_WARN(s, "No client with ID '%u', failing operation.", client_id);
return NULL;
}
static xrt_result_t
get_client_app_state_locked(struct ipc_server *s, uint32_t client_id, struct ipc_app_state *out_ias)
{
volatile struct ipc_client_state *ics = find_client_locked(s, client_id);
if (ics == NULL) {
return XRT_ERROR_IPC_FAILURE;
}
struct ipc_app_state ias = ics->client_state;
ias.io_active = ics->io_active;
// @todo: track this data in the ipc_client_state struct
ias.primary_application = false;
// The active client is decided by index, so get that from the ics.
int index = ics->server_thread_index;
if (s->global_state.active_client_index == index) {
ias.primary_application = true;
}
*out_ias = ias;
return XRT_SUCCESS;
}
static xrt_result_t
set_active_client_locked(struct ipc_server *s, uint32_t client_id)
{
volatile struct ipc_client_state *ics = find_client_locked(s, client_id);
if (ics == NULL) {
return XRT_ERROR_IPC_FAILURE;
}
// The active client is decided by index, so get that from the ics.
int index = ics->server_thread_index;
if (index != s->global_state.active_client_index) {
s->global_state.active_client_index = index;
}
return XRT_SUCCESS;
}
static xrt_result_t
toggle_io_client_locked(struct ipc_server *s, uint32_t client_id)
{
volatile struct ipc_client_state *ics = find_client_locked(s, client_id);
if (ics == NULL) {
return XRT_ERROR_IPC_FAILURE;
}
ics->io_active = !ics->io_active;
return XRT_SUCCESS;
}
/*
*
* Exported functions.
*
*/
xrt_result_t
ipc_server_get_client_app_state(struct ipc_server *s, uint32_t client_id, struct ipc_app_state *out_ias)
{
os_mutex_lock(&s->global_state.lock);
xrt_result_t xret = get_client_app_state_locked(s, client_id, out_ias);
os_mutex_unlock(&s->global_state.lock);
return xret;
}
xrt_result_t
ipc_server_set_active_client(struct ipc_server *s, uint32_t client_id)
{
os_mutex_lock(&s->global_state.lock);
xrt_result_t xret = set_active_client_locked(s, client_id);
os_mutex_unlock(&s->global_state.lock);
return xret;
}
xrt_result_t
ipc_server_toggle_io_client(struct ipc_server *s, uint32_t client_id)
{
os_mutex_lock(&s->global_state.lock);
xrt_result_t xret = toggle_io_client_locked(s, client_id);
os_mutex_unlock(&s->global_state.lock);
return xret;
}
void
ipc_server_activate_session(volatile struct ipc_client_state *ics)
{
struct ipc_server *s = ics->server;
// Already active, noop.
if (ics->client_state.session_active) {
return;
}
assert(ics->server_thread_index >= 0);
// Multiple threads could call this at the same time.
os_mutex_lock(&s->global_state.lock);
ics->client_state.session_active = true;
if (ics->client_state.session_overlay) {
// For new active overlay sessions only update this session.
handle_focused_client_events(ics, s->global_state.active_client_index,
s->global_state.last_active_client_index);
handle_overlay_client_events(ics, s->global_state.active_client_index,
s->global_state.last_active_client_index);
} else {
// Update active client
set_active_client_locked(s, ics->client_state.id);
// For new active regular sessions update all clients.
update_server_state_locked(s);
}
os_mutex_unlock(&s->global_state.lock);
}
void
ipc_server_deactivate_session(volatile struct ipc_client_state *ics)
{
struct ipc_server *s = ics->server;
// Multiple threads could call this at the same time.
os_mutex_lock(&s->global_state.lock);
ics->client_state.session_active = false;
update_server_state_locked(s);
os_mutex_unlock(&s->global_state.lock);
}
void
ipc_server_update_state(struct ipc_server *s)
{
// Multiple threads could call this at the same time.
os_mutex_lock(&s->global_state.lock);
update_server_state_locked(s);
os_mutex_unlock(&s->global_state.lock);
}
void
ipc_server_handle_failure(struct ipc_server *vs)
{
// Right now handled just the same as a graceful shutdown.
vs->running = false;
}
void
ipc_server_handle_shutdown_signal(struct ipc_server *vs)
{
vs->running = false;
}
void
ipc_server_handle_client_connected(struct ipc_server *vs, xrt_ipc_handle_t ipc_handle)
{
volatile struct ipc_client_state *ics = NULL;
int32_t cs_index = -1;
os_mutex_lock(&vs->global_state.lock);
// find the next free thread in our array (server_thread_index is -1)
// and have it handle this connection
for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
volatile struct ipc_client_state *_cs = &vs->threads[i].ics;
if (_cs->server_thread_index < 0) {
ics = _cs;
cs_index = i;
break;
}
}
if (ics == NULL) {
xrt_ipc_handle_close(ipc_handle);
// Unlock when we are done.
os_mutex_unlock(&vs->global_state.lock);
U_LOG_E("Max client count reached!");
return;
}
struct ipc_thread *it = &vs->threads[cs_index];
if (it->state != IPC_THREAD_READY && it->state != IPC_THREAD_STOPPING) {
// we should not get here
xrt_ipc_handle_close(ipc_handle);
// Unlock when we are done.
os_mutex_unlock(&vs->global_state.lock);
U_LOG_E("Client state management error!");
return;
}
if (it->state != IPC_THREAD_READY) {
os_thread_join(&it->thread);
os_thread_destroy(&it->thread);
it->state = IPC_THREAD_READY;
}
it->state = IPC_THREAD_STARTING;
// Allocate a new ID, avoid zero.
//! @todo validate ID.
uint32_t id = ++vs->id_generator;
// Reset everything.
U_ZERO((struct ipc_client_state *)ics);
// Set state.
ics->client_state.id = id;
ics->imc.ipc_handle = ipc_handle;
ics->server = vs;
ics->server_thread_index = cs_index;
ics->io_active = true;
os_thread_start(&it->thread, ipc_server_client_thread, (void *)ics);
// Unlock when we are done.
os_mutex_unlock(&vs->global_state.lock);
}
xrt_result_t
ipc_server_get_system_properties(struct ipc_server *vs, struct xrt_system_properties *out_properties)
{
memcpy(out_properties, &vs->xsys->properties, sizeof(*out_properties));
return XRT_SUCCESS;
}
#ifndef XRT_OS_ANDROID
int
ipc_server_main(int argc, char **argv)
{
// Get log level first.
enum u_logging_level log_level = debug_get_log_option_ipc_log();
// Log very early who we are.
U_LOG_IFL_I(log_level, "%s '%s' starting up...", u_runtime_description, u_git_tag);
// Allocate the server itself.
struct ipc_server *s = U_TYPED_CALLOC(struct ipc_server);
#ifdef XRT_OS_WINDOWS
timeBeginPeriod(1);
#endif
/*
* Need to create early before any vars are added. Not created in
* init_all since that function is shared with Android and the debug
* GUI isn't supported on Android.
*/
u_debug_gui_create(&s->debug_gui);
int ret = init_all(s, log_level);
if (ret < 0) {
#ifdef XRT_OS_LINUX
// Print information how to debug issues.
print_linux_end_user_failed_information(log_level);
#endif
u_debug_gui_stop(&s->debug_gui);
free(s);
return ret;
}
// Start the debug UI now (if enabled).
u_debug_gui_start(s->debug_gui, s->xinst, s->xsysd);
#ifdef XRT_OS_LINUX
// Print a very clear service started message.
print_linux_end_user_started_information(log_level);
#endif
// Main loop.
ret = main_loop(s);
// Stop the UI before tearing everything down.
u_debug_gui_stop(&s->debug_gui);
// Done after UI stopped.
teardown_all(s);
free(s);
#ifdef XRT_OS_WINDOWS
timeEndPeriod(1);
#endif
U_LOG_IFL_I(log_level, "Server exiting: '%i'", ret);
return ret;
}
#endif // !XRT_OS_ANDROID
#ifdef XRT_OS_ANDROID
int
ipc_server_main_android(struct ipc_server **ps, void (*startup_complete_callback)(void *data), void *data)
{
// Get log level first.
enum u_logging_level log_level = debug_get_log_option_ipc_log();
struct ipc_server *s = U_TYPED_CALLOC(struct ipc_server);
U_LOG_D("Created IPC server!");
int ret = init_all(s, log_level);
if (ret < 0) {
free(s);
startup_complete_callback(data);
return ret;
}
*ps = s;
startup_complete_callback(data);
ret = main_loop(s);
teardown_all(s);
free(s);
U_LOG_I("Server exiting '%i'!", ret);
return ret;
}
#endif // XRT_OS_ANDROID