monado/src/xrt/state_trackers/prober/p_prober.c
Christoph Haag 162915f022 p/prober: disable all drivers when qwerty is enabled
qwerty is auto probed, making HMDs that are not auto probed always take precedence.
When setting QWERTY_ENABLE=1 the intent is usually to exclusively use qwerty.
Therefore we default to disabling all other drivers when this variable is set.

To make the old behavior of adding qwerty devices with lower priority than actual
devices, the variable QWERTY_COMBINE=1 is introduced.
2021-07-16 15:15:28 +00:00

1090 lines
25 KiB
C

// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Main prober code.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup st_prober
*/
#include "xrt/xrt_config_drivers.h"
#include "xrt/xrt_settings.h"
#include "util/u_var.h"
#include "util/u_misc.h"
#include "util/u_config_json.h"
#include "util/u_debug.h"
#include "os/os_hid.h"
#include "p_prober.h"
#ifdef XRT_HAVE_V4L2
#include "v4l2/v4l2_interface.h"
#endif
#ifdef XRT_BUILD_DRIVER_VF
#include "vf/vf_interface.h"
#endif
#ifdef XRT_BUILD_DRIVER_REMOTE
#include "remote/r_interface.h"
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "multi_wrapper/multi.h"
/*
*
* Pre-declare functions.
*
*/
DEBUG_GET_ONCE_LOG_OPTION(prober_log, "PROBER_LOG", U_LOGGING_INFO)
DEBUG_GET_ONCE_BOOL_OPTION(qwerty_enable, "QWERTY_ENABLE", false)
DEBUG_GET_ONCE_BOOL_OPTION(qwerty_combine, "QWERTY_COMBINE", false)
static void
add_device(struct prober *p, struct prober_device **out_dev);
static int
initialize(struct prober *p, struct xrt_prober_entry_lists *lists);
static void
teardown_devices(struct prober *p);
static void
teardown(struct prober *p);
static int
probe(struct xrt_prober *xp);
static int
dump(struct xrt_prober *xp);
static int
select_device(struct xrt_prober *xp, struct xrt_device **xdevs, size_t num_xdevs);
static int
open_hid_interface(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
int interface,
struct os_hid_device **out_hid_dev);
static int
open_video_device(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
struct xrt_frame_context *xfctx,
struct xrt_fs **out_xfs);
static int
list_video_devices(struct xrt_prober *xp, xrt_prober_list_video_cb cb, void *ptr);
static int
get_entries(struct xrt_prober *xp,
size_t *out_num_entries,
struct xrt_prober_entry ***out_entries,
struct xrt_auto_prober ***out_auto_probers);
static int
get_string_descriptor(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
enum xrt_prober_string which_string,
unsigned char *buffer,
size_t length);
static bool
can_open(struct xrt_prober *xp, struct xrt_prober_device *xpdev);
static void
destroy(struct xrt_prober **xp);
/*
*
* "Exported" functions.
*
*/
int
xrt_prober_create_with_lists(struct xrt_prober **out_xp, struct xrt_prober_entry_lists *lists)
{
struct prober *p = U_TYPED_CALLOC(struct prober);
int ret = initialize(p, lists);
if (ret != 0) {
free(p);
return ret;
}
*out_xp = &p->base;
return 0;
}
#define ENUM_TO_STR(r) \
case r: return #r
const char *
xrt_prober_string_to_string(enum xrt_prober_string t)
{
switch (t) {
ENUM_TO_STR(XRT_PROBER_STRING_MANUFACTURER);
ENUM_TO_STR(XRT_PROBER_STRING_PRODUCT);
ENUM_TO_STR(XRT_PROBER_STRING_SERIAL_NUMBER);
}
return "";
}
const char *
xrt_bus_type_to_string(enum xrt_bus_type t)
{
switch (t) {
ENUM_TO_STR(XRT_BUS_TYPE_UNKNOWN);
ENUM_TO_STR(XRT_BUS_TYPE_USB);
ENUM_TO_STR(XRT_BUS_TYPE_BLUETOOTH);
}
return "";
}
bool
xrt_prober_match_string(struct xrt_prober *xp,
struct xrt_prober_device *dev,
enum xrt_prober_string type,
const char *to_match)
{
unsigned char s[256] = {0};
int len = xrt_prober_get_string_descriptor(xp, dev, type, s, sizeof(s));
if (len <= 0) {
return false;
}
return 0 == strncmp(to_match, (const char *)s, sizeof(s));
}
int
p_dev_get_usb_dev(struct prober *p,
uint16_t bus,
uint16_t addr,
uint16_t vendor_id,
uint16_t product_id,
struct prober_device **out_pdev)
{
struct prober_device *pdev;
for (size_t i = 0; i < p->num_devices; i++) {
struct prober_device *pdev = &p->devices[i];
if (pdev->base.bus != XRT_BUS_TYPE_USB || pdev->usb.bus != bus || pdev->usb.addr != addr) {
continue;
}
if (pdev->base.vendor_id != vendor_id || pdev->base.product_id != product_id) {
P_ERROR(p,
"USB device with same address but different "
"vendor and product found!\n"
"\tvendor: %04x %04x\n"
"\tproduct: %04x %04x",
pdev->base.vendor_id, vendor_id, pdev->base.product_id, product_id);
continue;
}
*out_pdev = pdev;
return 0;
}
add_device(p, &pdev);
pdev->base.vendor_id = vendor_id;
pdev->base.product_id = product_id;
pdev->base.bus = XRT_BUS_TYPE_USB;
pdev->usb.bus = bus;
pdev->usb.addr = addr;
*out_pdev = pdev;
return 0;
}
int
p_dev_get_bluetooth_dev(
struct prober *p, uint64_t id, uint16_t vendor_id, uint16_t product_id, struct prober_device **out_pdev)
{
struct prober_device *pdev;
for (size_t i = 0; i < p->num_devices; i++) {
struct prober_device *pdev = &p->devices[i];
if (pdev->base.bus != XRT_BUS_TYPE_BLUETOOTH || pdev->bluetooth.id != id) {
continue;
}
if (pdev->base.vendor_id != vendor_id || pdev->base.product_id != product_id) {
P_ERROR(p,
"Bluetooth device with same address but "
"different vendor and product found!\n"
"\tvendor: %04x %04x\n"
"\tproduct: %04x %04x",
pdev->base.vendor_id, vendor_id, pdev->base.product_id, product_id);
continue;
}
*out_pdev = pdev;
return 0;
}
add_device(p, &pdev);
pdev->base.vendor_id = vendor_id;
pdev->base.product_id = product_id;
pdev->base.bus = XRT_BUS_TYPE_BLUETOOTH;
pdev->bluetooth.id = id;
*out_pdev = pdev;
return 0;
}
/*
*
* Internal functions.
*
*/
static void
fill_out_product(struct prober *p, struct prober_device *pdev)
{
const char *bus = pdev->base.bus == XRT_BUS_TYPE_BLUETOOTH ? "bluetooth" : "usb";
char *str = NULL;
int ret = 0;
do {
ret = snprintf(str, ret, "Unknown %s device: %04x:%04x", bus, pdev->base.vendor_id,
pdev->base.product_id);
if (ret <= 0) {
return;
}
if (str == NULL) {
str = U_CALLOC_WITH_CAST(char, ret + 1);
} else {
pdev->usb.product = str;
return;
}
} while (true);
}
static void
add_device(struct prober *p, struct prober_device **out_dev)
{
U_ARRAY_REALLOC_OR_FREE(p->devices, struct prober_device, (p->num_devices + 1));
struct prober_device *dev = &p->devices[p->num_devices++];
U_ZERO(dev);
*out_dev = dev;
}
static void
add_usb_entry(struct prober *p, struct xrt_prober_entry *entry)
{
U_ARRAY_REALLOC_OR_FREE(p->entries, struct xrt_prober_entry *, (p->num_entries + 1));
p->entries[p->num_entries++] = entry;
}
static int
collect_entries(struct prober *p)
{
struct xrt_prober_entry_lists *lists = p->lists;
while (lists) {
for (size_t j = 0; lists->entries != NULL && lists->entries[j]; j++) {
struct xrt_prober_entry *entry = lists->entries[j];
for (size_t k = 0; entry[k].found != NULL; k++) {
add_usb_entry(p, &entry[k]);
}
}
lists = lists->next;
}
return 0;
}
#define num_driver_conflicts 1
char *driver_conflicts[num_driver_conflicts][2] = {{"survive", "vive"}};
static void
disable_drivers_from_conflicts(struct prober *p)
{
if (debug_get_bool_option_qwerty_enable() && !debug_get_bool_option_qwerty_combine()) {
for (size_t entry = 0; entry < p->num_entries; entry++) {
if (strcmp(p->entries[entry]->driver_name, "Qwerty") != 0) {
P_INFO(p, "Disabling %s because we have %s", p->entries[entry]->driver_name, "Qwerty");
size_t index = p->num_disabled_drivers++;
U_ARRAY_REALLOC_OR_FREE(p->disabled_drivers, char *, p->num_disabled_drivers);
p->disabled_drivers[index] = (char *)p->entries[entry]->driver_name;
}
}
return;
}
for (size_t i = 0; i < num_driver_conflicts; i++) {
bool have_first = false;
bool have_second = false;
char *first = driver_conflicts[i][0];
char *second = driver_conflicts[i][1];
// disable second driver if we have first driver
for (size_t entry = 0; entry < p->num_entries; entry++) {
if (strcmp(p->entries[entry]->driver_name, first) == 0) {
have_first = true;
}
if (strcmp(p->entries[entry]->driver_name, second) == 0) {
have_second = true;
}
}
for (size_t ap = 0; ap < MAX_AUTO_PROBERS; ap++) {
if (p->auto_probers[ap] == NULL) {
continue;
}
if (strcmp(p->auto_probers[ap]->name, first) == 0) {
have_first = true;
}
if (strcmp(p->auto_probers[ap]->name, second) == 0) {
have_second = true;
}
}
if (have_first && have_second) {
// except don't disable second driver, if first driver is already disabled'
bool first_already_disabled = false;
;
for (size_t disabled = 0; disabled < p->num_disabled_drivers; disabled++) {
if (strcmp(p->disabled_drivers[disabled], first) == 0) {
first_already_disabled = true;
break;
}
}
if (first_already_disabled) {
P_INFO(p, "Not disabling %s because %s is disabled", second, first);
continue;
}
P_INFO(p, "Disabling %s because we have %s", second, first);
size_t index = p->num_disabled_drivers++;
U_ARRAY_REALLOC_OR_FREE(p->disabled_drivers, char *, p->num_disabled_drivers);
p->disabled_drivers[index] = second;
}
}
}
static void
parse_disabled_drivers(struct prober *p)
{
cJSON *disabled_drivers = cJSON_GetObjectItemCaseSensitive(p->json.root, "disabled");
if (!disabled_drivers) {
return;
}
cJSON *disabled_driver = NULL;
cJSON_ArrayForEach(disabled_driver, disabled_drivers)
{
if (!cJSON_IsString(disabled_driver)) {
continue;
}
size_t index = p->num_disabled_drivers++;
U_ARRAY_REALLOC_OR_FREE(p->disabled_drivers, char *, p->num_disabled_drivers);
p->disabled_drivers[index] = disabled_driver->valuestring;
}
}
static int
initialize(struct prober *p, struct xrt_prober_entry_lists *lists)
{
p->base.probe = probe;
p->base.dump = dump;
p->base.select = select_device;
p->base.open_hid_interface = open_hid_interface;
p->base.open_video_device = open_video_device;
p->base.list_video_devices = list_video_devices;
p->base.get_entries = get_entries;
p->base.get_string_descriptor = get_string_descriptor;
p->base.can_open = can_open;
p->base.destroy = destroy;
p->lists = lists;
p->ll = debug_get_log_option_prober_log();
p->json.file_loaded = false;
p->json.root = NULL;
u_var_add_root((void *)p, "Prober", true);
u_var_add_ro_u32(p, (uint32_t *)&p->ll, "Log Level");
int ret;
u_config_json_open_or_create_main_file(&p->json);
ret = collect_entries(p);
if (ret != 0) {
teardown(p);
return -1;
}
#ifdef XRT_HAVE_LIBUSB
ret = p_libusb_init(p);
if (ret != 0) {
teardown(p);
return -1;
}
#endif
#ifdef XRT_HAVE_LIBUVC
ret = p_libuvc_init(p);
if (ret != 0) {
teardown(p);
return -1;
}
#endif
ret = p_tracking_init(p);
if (ret != 0) {
teardown(p);
return -1;
}
for (int i = 0; i < MAX_AUTO_PROBERS && lists->auto_probers[i]; i++) {
p->auto_probers[i] = lists->auto_probers[i]();
}
p->num_disabled_drivers = 0;
parse_disabled_drivers(p);
disable_drivers_from_conflicts(p);
return 0;
}
static void
teardown_devices(struct prober *p)
{
// Need to free all devices.
for (size_t i = 0; i < p->num_devices; i++) {
struct prober_device *pdev = &p->devices[i];
if (pdev->usb.product != NULL) {
free((char *)pdev->usb.product);
pdev->usb.product = NULL;
}
if (pdev->usb.manufacturer != NULL) {
free((char *)pdev->usb.manufacturer);
pdev->usb.manufacturer = NULL;
}
if (pdev->usb.serial != NULL) {
free((char *)pdev->usb.serial);
pdev->usb.serial = NULL;
}
if (pdev->usb.path != NULL) {
free((char *)pdev->usb.path);
pdev->usb.path = NULL;
}
#ifdef XRT_HAVE_LIBUSB
if (pdev->usb.dev != NULL) {
//! @todo Free somewhere else
}
#endif
#ifdef XRT_HAVE_LIBUVC
if (pdev->uvc.dev != NULL) {
//! @todo Free somewhere else
}
#endif
#ifdef XRT_HAVE_V4L2
for (size_t j = 0; j < pdev->num_v4ls; j++) {
struct prober_v4l *v4l = &pdev->v4ls[j];
free((char *)v4l->path);
v4l->path = NULL;
}
if (pdev->v4ls != NULL) {
free(pdev->v4ls);
pdev->v4ls = NULL;
pdev->num_v4ls = 0;
}
#endif
#ifdef XRT_OS_LINUX
for (size_t j = 0; j < pdev->num_hidraws; j++) {
struct prober_hidraw *hidraw = &pdev->hidraws[j];
free((char *)hidraw->path);
hidraw->path = NULL;
}
if (pdev->hidraws != NULL) {
free(pdev->hidraws);
pdev->hidraws = NULL;
pdev->num_hidraws = 0;
}
#endif
}
if (p->devices != NULL) {
free(p->devices);
p->devices = NULL;
p->num_devices = 0;
}
}
static void
teardown(struct prober *p)
{
// First remove the variable tracking.
u_var_remove_root((void *)p);
// Clean up all auto_probers.
for (int i = 0; i < MAX_AUTO_PROBERS && p->auto_probers[i]; i++) {
p->auto_probers[i]->destroy(p->auto_probers[i]);
p->auto_probers[i] = NULL;
}
// Need to turn off tracking early.
p_tracking_teardown(p);
// Need to free all entries.
if (p->entries != NULL) {
free(p->entries);
p->entries = NULL;
p->num_entries = 0;
}
teardown_devices(p);
#ifdef XRT_HAVE_LIBUVC
p_libuvc_teardown(p);
#endif
#ifdef XRT_HAVE_LIBUSB
p_libusb_teardown(p);
#endif
u_config_json_close(&p->json);
free(p->disabled_drivers);
}
/*
*
* Member functions.
*
*/
static int
probe(struct xrt_prober *xp)
{
struct prober *p = (struct prober *)xp;
XRT_MAYBE_UNUSED int ret = 0;
// Free old list first.
teardown_devices(p);
#ifdef XRT_HAVE_LIBUDEV
ret = p_udev_probe(p);
if (ret != 0) {
P_ERROR(p, "Failed to enumerate udev devices\n");
return -1;
}
#endif
#ifdef XRT_HAVE_LIBUSB
ret = p_libusb_probe(p);
if (ret != 0) {
P_ERROR(p, "Failed to enumerate libusb devices\n");
return -1;
}
#endif
#ifdef XRT_HAVE_LIBUVC
ret = p_libuvc_probe(p);
if (ret != 0) {
P_ERROR(p, "Failed to enumerate libuvc devices\n");
return -1;
}
#endif
return 0;
}
static int
dump(struct xrt_prober *xp)
{
struct prober *p = (struct prober *)xp;
XRT_MAYBE_UNUSED ssize_t k = 0;
XRT_MAYBE_UNUSED size_t j = 0;
for (size_t i = 0; i < p->num_devices; i++) {
struct prober_device *pdev = &p->devices[i];
p_dump_device(p, pdev, (int)i);
}
return 0;
}
static void
handle_found_device(
struct prober *p, struct xrt_device **xdevs, size_t num_xdevs, bool *have_hmd, struct xrt_device *xdev)
{
P_DEBUG(p, "Found '%s' %p", xdev->str, (void *)xdev);
size_t i = 0;
for (; i < num_xdevs; i++) {
if (xdevs[i] == NULL) {
break;
}
}
if (i + 1 > num_xdevs) {
P_ERROR(p, "Too many devices, closing '%s'", xdev->str);
xdev->destroy(xdev);
return;
}
// we can have only one HMD
if (xdev->device_type == XRT_DEVICE_TYPE_HMD) {
if (*have_hmd) {
P_ERROR(p, "Too many HMDs, closing '%s'", xdev->str);
xdev->destroy(xdev);
return;
}
*have_hmd = true;
}
xdevs[i] = xdev;
}
static void
add_from_devices(struct prober *p, struct xrt_device **xdevs, size_t num_xdevs, bool *have_hmd)
{
// Build a list of all current probed devices.
struct xrt_prober_device **dev_list = U_TYPED_ARRAY_CALLOC(struct xrt_prober_device *, p->num_devices);
for (size_t i = 0; i < p->num_devices; i++) {
dev_list[i] = &p->devices[i].base;
}
// Loop over all devices and entries that might match them.
for (size_t i = 0; i < p->num_devices; i++) {
struct prober_device *pdev = &p->devices[i];
for (size_t k = 0; k < p->num_entries; k++) {
struct xrt_prober_entry *entry = p->entries[k];
if (pdev->base.vendor_id != entry->vendor_id || pdev->base.product_id != entry->product_id) {
continue;
}
bool skip = false;
for (size_t disabled = 0; disabled < p->num_disabled_drivers; disabled++) {
if (strcmp(entry->driver_name, p->disabled_drivers[disabled]) == 0) {
P_INFO(p, "Skipping disabled driver %s", entry->driver_name);
skip = true;
break;
;
}
}
if (skip) {
continue;
}
struct xrt_device *new_xdevs[XRT_MAX_DEVICES_PER_PROBE] = {NULL};
int num_found = entry->found(&p->base, dev_list, p->num_devices, i, NULL, &(new_xdevs[0]));
if (num_found <= 0) {
continue;
}
for (int created_idx = 0; created_idx < num_found; ++created_idx) {
if (new_xdevs[created_idx] == NULL) {
P_DEBUG(p,
"Leaving device creation loop "
"early: found function reported %i "
"created, but only %i non-null",
num_found, created_idx);
continue;
}
handle_found_device(p, xdevs, num_xdevs, have_hmd, new_xdevs[created_idx]);
}
}
}
// Free the temporary list.
free(dev_list);
}
static void
add_from_auto_probers(struct prober *p, struct xrt_device **xdevs, size_t num_xdevs, bool *have_hmd)
{
for (int i = 0; i < MAX_AUTO_PROBERS && p->auto_probers[i]; i++) {
bool skip = false;
for (size_t disabled = 0; disabled < p->num_disabled_drivers; disabled++) {
if (strcmp(p->auto_probers[i]->name, p->disabled_drivers[disabled]) == 0) {
P_INFO(p, "Skipping disabled driver %s", p->auto_probers[i]->name);
skip = true;
break;
}
}
if (skip) {
continue;
}
/*
* If we have found a HMD, tell the auto probers not to open
* any more HMDs. This is mostly to stop OpenHMD and Monado
* fighting over devices.
*/
bool no_hmds = *have_hmd;
struct xrt_device *new_xdevs[XRT_MAX_DEVICES_PER_PROBE] = {NULL};
int num_found =
p->auto_probers[i]->lelo_dallas_autoprobe(p->auto_probers[i], NULL, no_hmds, &p->base, new_xdevs);
if (num_found <= 0) {
continue;
}
for (int created_idx = 0; created_idx < num_found; ++created_idx) {
if (new_xdevs[created_idx] == NULL) {
P_DEBUG(p,
"Leaving device creation loop early: %s autoprobe function reported %i "
"created, but only %i non-null",
p->auto_probers[i]->name, num_found, created_idx);
continue;
}
handle_found_device(p, xdevs, num_xdevs, have_hmd, new_xdevs[created_idx]);
}
}
}
static void
add_from_remote(struct prober *p, struct xrt_device **xdevs, size_t num_xdevs, bool *have_hmd)
{
if (num_xdevs < 3) {
return;
}
#ifdef XRT_BUILD_DRIVER_REMOTE
int port = 4242;
if (!u_config_json_get_remote_port(&p->json, &port)) {
port = 4242;
}
r_create_devices(port, &xdevs[0], &xdevs[1], &xdevs[2]);
*have_hmd = xdevs[0] != NULL;
#endif
}
static void
apply_tracking_override(struct prober *p, struct xrt_device **xdevs, size_t num_xdevs, struct xrt_tracking_override *o)
{
struct xrt_device *target_xdev = NULL;
size_t target_idx = 0;
struct xrt_device *tracker_xdev = NULL;
for (size_t i = 0; i < num_xdevs; i++) {
struct xrt_device *xdev = xdevs[i];
if (xdev == NULL) {
continue;
}
if (strncmp(xdev->serial, o->target_device_serial, XRT_DEVICE_NAME_LEN) == 0) {
target_xdev = xdev;
target_idx = i;
}
if (strncmp(xdev->serial, o->tracker_device_serial, XRT_DEVICE_NAME_LEN) == 0) {
tracker_xdev = xdev;
}
}
if (target_xdev == NULL) {
P_WARN(p, "Tracking override target xdev %s not found", o->target_device_serial);
}
if (tracker_xdev == NULL) {
P_WARN(p, "Tracking override tracker xdev %s not found", o->tracker_device_serial);
}
if (target_xdev != NULL && tracker_xdev != NULL) {
struct xrt_device *multi = multi_create_tracking_override(o->override_type, target_xdev, tracker_xdev,
o->input_name, &o->offset);
if (multi) {
P_INFO(p, "Applying Tracking override %s <- %s", o->target_device_serial,
o->tracker_device_serial);
// drops the target device from the list, but keeps the tracker
// a tracker could be attached to multiple targets with different names
xdevs[target_idx] = multi;
} else {
P_ERROR(p, "Failed to create tracking override multi device");
}
}
}
static int
select_device(struct xrt_prober *xp, struct xrt_device **xdevs, size_t num_xdevs)
{
struct prober *p = (struct prober *)xp;
enum u_config_json_active_config active;
bool have_hmd = false;
u_config_json_get_active(&p->json, &active);
switch (active) {
case U_ACTIVE_CONFIG_NONE:
case U_ACTIVE_CONFIG_TRACKING:
add_from_devices(p, xdevs, num_xdevs, &have_hmd);
add_from_auto_probers(p, xdevs, num_xdevs, &have_hmd);
break;
case U_ACTIVE_CONFIG_REMOTE: add_from_remote(p, xdevs, num_xdevs, &have_hmd); break;
default: assert(false);
}
// It's easier if we just put the first hmd first,
// but keep other internal ordering of devices.
for (size_t i = 1; i < num_xdevs; i++) {
if (xdevs[i] == NULL) {
continue;
}
if (xdevs[i]->hmd == NULL) {
continue;
}
// This is a HMD, but it's not in the first slot.
struct xrt_device *hmd = xdevs[i];
for (size_t k = i; k > 0; k--) {
xdevs[k] = xdevs[k - 1];
}
xdevs[0] = hmd;
break;
}
struct xrt_tracking_override overrides[XRT_MAX_TRACKING_OVERRIDES];
size_t num_overrides = 0;
if (u_config_json_get_tracking_overrides(&p->json, overrides, &num_overrides)) {
for (size_t i = 0; i < num_overrides; i++) {
struct xrt_tracking_override *o = &overrides[i];
apply_tracking_override(p, xdevs, num_xdevs, o);
}
}
if (have_hmd) {
P_DEBUG(p, "Found HMD! '%s'", xdevs[0]->str);
return 0;
}
P_DEBUG(p, "Didn't find any HMD devices");
// Even if we've found some controllers, we don't use them without an
// HMD. So, destroy all other found devices.
for (size_t i = 1; i < num_xdevs; i++) {
if (xdevs[i] == NULL) {
continue;
}
P_DEBUG(p, "Destroying '%s'", xdevs[i]->str);
xrt_device_destroy(&xdevs[i]);
}
return 0;
}
static int
open_hid_interface(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
int interface,
struct os_hid_device **out_hid_dev)
{
struct prober_device *pdev = (struct prober_device *)xpdev;
int ret;
#ifdef XRT_OS_LINUX
for (size_t j = 0; j < pdev->num_hidraws; j++) {
struct prober_hidraw *hidraw = &pdev->hidraws[j];
if (hidraw->interface != interface) {
continue;
}
ret = os_hid_open_hidraw(hidraw->path, out_hid_dev);
if (ret != 0) {
U_LOG_E("Failed to open device '%s' got '%i'", hidraw->path, ret);
return ret;
}
return 0;
}
#endif // XRT_OS_LINUX
U_LOG_E(
"Could not find the requested "
"hid interface (%i) on the device!",
interface);
return -1;
}
DEBUG_GET_ONCE_OPTION(vf_path, "VF_PATH", NULL)
static int
open_video_device(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
struct xrt_frame_context *xfctx,
struct xrt_fs **out_xfs)
{
XRT_MAYBE_UNUSED struct prober_device *pdev = (struct prober_device *)xpdev;
#if defined(XRT_BUILD_DRIVER_VF)
const char *path = debug_get_option_vf_path();
if (path != NULL) {
struct xrt_fs *xfs = vf_fs_open_file(xfctx, path);
if (xfs) {
*out_xfs = xfs;
return 0;
}
}
#endif
#if defined(XRT_HAVE_V4L2)
if (pdev->num_v4ls == 0) {
return -1;
}
struct xrt_fs *xfs =
v4l2_fs_create(xfctx, pdev->v4ls[0].path, pdev->usb.product, pdev->usb.manufacturer, pdev->usb.serial);
if (xfs == NULL) {
return -1;
}
*out_xfs = xfs;
return 0;
#else
return -1;
#endif
}
static int
list_video_devices(struct xrt_prober *xp, xrt_prober_list_video_cb cb, void *ptr)
{
struct prober *p = (struct prober *)xp;
const char *path = debug_get_option_vf_path();
if (path != NULL) {
cb(xp, NULL, "Video File", "Collabora", path, ptr);
}
// Loop over all devices and find video devices.
for (size_t i = 0; i < p->num_devices; i++) {
struct prober_device *pdev = &p->devices[i];
bool has = false;
#ifdef XRT_HAVE_LIBUVC
has |= pdev->uvc.dev != NULL;
#endif
#ifdef XRT_HAVE_V4L2
has |= pdev->num_v4ls > 0;
#endif
if (!has) {
continue;
}
if (pdev->usb.product == NULL) {
fill_out_product(p, pdev);
}
cb(xp, &pdev->base, pdev->usb.product, pdev->usb.manufacturer, pdev->usb.serial, ptr);
}
return 0;
}
static int
get_entries(struct xrt_prober *xp,
size_t *out_num_entries,
struct xrt_prober_entry ***out_entries,
struct xrt_auto_prober ***out_auto_probers)
{
struct prober *p = (struct prober *)xp;
*out_num_entries = p->num_entries;
*out_entries = p->entries;
*out_auto_probers = p->auto_probers;
return 0;
}
static int
get_string_descriptor(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
enum xrt_prober_string which_string,
unsigned char *buffer,
size_t max_length)
{
XRT_MAYBE_UNUSED struct prober *p = (struct prober *)xp;
XRT_MAYBE_UNUSED struct prober_device *pdev = (struct prober_device *)xpdev;
XRT_MAYBE_UNUSED int ret;
#ifdef XRT_HAVE_LIBUSB
if (pdev->base.bus == XRT_BUS_TYPE_USB && pdev->usb.dev != NULL) {
ret = p_libusb_get_string_descriptor(p, pdev, which_string, buffer, max_length);
if (ret >= 0) {
return ret;
}
}
#endif
if (pdev->base.bus == XRT_BUS_TYPE_BLUETOOTH && which_string == XRT_PROBER_STRING_SERIAL_NUMBER) {
union {
uint8_t arr[8];
uint64_t v;
} u;
u.v = pdev->bluetooth.id;
return snprintf((char *)buffer, max_length, "%02X:%02X:%02X:%02X:%02X:%02X", u.arr[5], u.arr[4],
u.arr[3], u.arr[2], u.arr[1], u.arr[0]);
}
//! @todo add more backends
//! @todo make this unicode (utf-16)? utf-8 would be better...
return 0;
}
static bool
can_open(struct xrt_prober *xp, struct xrt_prober_device *xpdev)
{
XRT_MAYBE_UNUSED struct prober *p = (struct prober *)xp;
XRT_MAYBE_UNUSED struct prober_device *pdev = (struct prober_device *)xpdev;
#ifdef XRT_HAVE_LIBUSB
if (pdev->usb.dev != NULL) {
return p_libusb_can_open(p, pdev);
}
#endif
//! @todo add more backends
return false;
}
static void
destroy(struct xrt_prober **xp)
{
struct prober *p = (struct prober *)*xp;
if (p == NULL) {
return;
}
teardown(p);
free(p);
*xp = NULL;
}