monado/src/xrt/state_trackers/prober/p_prober.c

1090 lines
25 KiB
C
Raw Normal View History

2019-04-30 13:33:34 +00:00
// Copyright 2019, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Main prober code.
* @author Jakob Bornecrantz <jakob@collabora.com>
* @ingroup st_prober
*/
2020-10-15 17:11:25 +00:00
#include "xrt/xrt_config_drivers.h"
#include "xrt/xrt_settings.h"
2020-10-15 17:11:25 +00:00
2019-08-31 11:49:32 +00:00
#include "util/u_var.h"
2019-04-30 13:33:34 +00:00
#include "util/u_misc.h"
#include "util/u_config_json.h"
2019-08-31 11:49:32 +00:00
#include "util/u_debug.h"
2020-03-03 22:39:03 +00:00
#include "os/os_hid.h"
2019-04-30 13:33:34 +00:00
#include "p_prober.h"
2019-11-10 01:36:34 +00:00
#ifdef XRT_HAVE_V4L2
#include "v4l2/v4l2_interface.h"
#endif
#ifdef XRT_BUILD_DRIVER_VF
2020-11-26 19:16:02 +00:00
#include "vf/vf_interface.h"
#endif
2020-10-15 17:11:25 +00:00
#ifdef XRT_BUILD_DRIVER_REMOTE
#include "remote/r_interface.h"
#endif
2019-04-30 13:33:34 +00:00
#include <stdio.h>
#include <string.h>
#include <assert.h>
2019-04-30 13:33:34 +00:00
#include "multi_wrapper/multi.h"
2019-04-30 13:33:34 +00:00
/*
*
* 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)
2019-04-30 13:33:34 +00:00
static void
add_device(struct prober *p, struct prober_device **out_dev);
2019-04-30 13:33:34 +00:00
static int
initialize(struct prober *p, struct xrt_prober_entry_lists *lists);
2019-04-30 13:33:34 +00:00
static void
teardown_devices(struct prober *p);
2019-04-30 13:33:34 +00:00
static void
teardown(struct prober *p);
2019-04-30 13:33:34 +00:00
static int
probe(struct xrt_prober *xp);
2019-04-30 13:33:34 +00:00
static int
dump(struct xrt_prober *xp);
2019-04-30 13:33:34 +00:00
static int
2021-01-14 14:13:48 +00:00
select_device(struct xrt_prober *xp, struct xrt_device **xdevs, size_t num_xdevs);
2019-04-30 13:33:34 +00:00
static int
open_hid_interface(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
2019-04-30 13:33:34 +00:00
int interface,
struct os_hid_device **out_hid_dev);
2019-04-30 13:33:34 +00:00
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
2021-01-14 14:13:48 +00:00
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);
2019-04-30 13:33:34 +00:00
static void
destroy(struct xrt_prober **xp);
2019-04-30 13:33:34 +00:00
/*
*
* "Exported" functions.
*
*/
int
2021-01-14 14:13:48 +00:00
xrt_prober_create_with_lists(struct xrt_prober **out_xp, struct xrt_prober_entry_lists *lists)
2019-04-30 13:33:34 +00:00
{
struct prober *p = U_TYPED_CALLOC(struct prober);
2019-04-30 13:33:34 +00:00
int ret = initialize(p, lists);
if (ret != 0) {
free(p);
return ret;
}
*out_xp = &p->base;
2019-04-30 13:33:34 +00:00
return 0;
}
2021-01-14 14:13:48 +00:00
#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));
}
2019-04-30 13:33:34 +00:00
int
p_dev_get_usb_dev(struct prober *p,
2019-04-30 13:33:34 +00:00
uint16_t bus,
uint16_t addr,
uint16_t vendor_id,
uint16_t product_id,
struct prober_device **out_pdev)
2019-04-30 13:33:34 +00:00
{
struct prober_device *pdev;
2019-04-30 13:33:34 +00:00
for (size_t i = 0; i < p->num_devices; i++) {
struct prober_device *pdev = &p->devices[i];
2019-04-30 13:33:34 +00:00
2021-01-14 14:13:48 +00:00
if (pdev->base.bus != XRT_BUS_TYPE_USB || pdev->usb.bus != bus || pdev->usb.addr != addr) {
2019-04-30 13:33:34 +00:00
continue;
}
2021-01-14 14:13:48 +00:00
if (pdev->base.vendor_id != vendor_id || pdev->base.product_id != product_id) {
2019-04-30 13:33:34 +00:00
P_ERROR(p,
"USB device with same address but different "
"vendor and product found!\n"
"\tvendor: %04x %04x\n"
"\tproduct: %04x %04x",
2021-01-14 14:13:48 +00:00
pdev->base.vendor_id, vendor_id, pdev->base.product_id, product_id);
2019-04-30 13:33:34 +00:00
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
2021-01-14 14:13:48 +00:00
p_dev_get_bluetooth_dev(
struct prober *p, uint64_t id, uint16_t vendor_id, uint16_t product_id, struct prober_device **out_pdev)
2019-04-30 13:33:34 +00:00
{
struct prober_device *pdev;
2019-04-30 13:33:34 +00:00
for (size_t i = 0; i < p->num_devices; i++) {
struct prober_device *pdev = &p->devices[i];
2019-04-30 13:33:34 +00:00
2021-01-14 14:13:48 +00:00
if (pdev->base.bus != XRT_BUS_TYPE_BLUETOOTH || pdev->bluetooth.id != id) {
2019-04-30 13:33:34 +00:00
continue;
}
2021-01-14 14:13:48 +00:00
if (pdev->base.vendor_id != vendor_id || pdev->base.product_id != product_id) {
2019-04-30 13:33:34 +00:00
P_ERROR(p,
"Bluetooth device with same address but "
"different vendor and product found!\n"
"\tvendor: %04x %04x\n"
"\tproduct: %04x %04x",
2021-01-14 14:13:48 +00:00
pdev->base.vendor_id, vendor_id, pdev->base.product_id, product_id);
2019-04-30 13:33:34 +00:00
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)
{
2021-01-14 14:13:48 +00:00
const char *bus = pdev->base.bus == XRT_BUS_TYPE_BLUETOOTH ? "bluetooth" : "usb";
char *str = NULL;
int ret = 0;
do {
2021-01-14 14:13:48 +00:00
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);
}
2019-04-30 13:33:34 +00:00
static void
add_device(struct prober *p, struct prober_device **out_dev)
2019-04-30 13:33:34 +00:00
{
2021-01-14 14:13:48 +00:00
U_ARRAY_REALLOC_OR_FREE(p->devices, struct prober_device, (p->num_devices + 1));
2019-04-30 13:33:34 +00:00
struct prober_device *dev = &p->devices[p->num_devices++];
2019-06-18 19:03:53 +00:00
U_ZERO(dev);
2019-04-30 13:33:34 +00:00
*out_dev = dev;
}
static void
add_usb_entry(struct prober *p, struct xrt_prober_entry *entry)
2019-04-30 13:33:34 +00:00
{
2021-01-14 14:13:48 +00:00
U_ARRAY_REALLOC_OR_FREE(p->entries, struct xrt_prober_entry *, (p->num_entries + 1));
2019-04-30 13:33:34 +00:00
p->entries[p->num_entries++] = entry;
}
static int
collect_entries(struct prober *p)
2019-04-30 13:33:34 +00:00
{
struct xrt_prober_entry_lists *lists = p->lists;
2019-04-30 13:33:34 +00:00
while (lists) {
2021-01-14 14:13:48 +00:00
for (size_t j = 0; lists->entries != NULL && lists->entries[j]; j++) {
struct xrt_prober_entry *entry = lists->entries[j];
2019-04-30 13:33:34 +00:00
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;
}
}
2019-04-30 13:33:34 +00:00
static int
initialize(struct prober *p, struct xrt_prober_entry_lists *lists)
2019-04-30 13:33:34 +00:00
{
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;
2019-04-30 13:33:34 +00:00
p->base.destroy = destroy;
p->lists = lists;
2020-12-16 17:12:56 +00:00
p->ll = debug_get_log_option_prober_log();
2019-04-30 13:33:34 +00:00
p->json.file_loaded = false;
p->json.root = NULL;
u_var_add_root((void *)p, "Prober", true);
2021-03-25 17:00:07 +00:00
u_var_add_ro_u32(p, (uint32_t *)&p->ll, "Log Level");
2019-08-31 11:49:32 +00:00
2019-04-30 13:33:34 +00:00
int ret;
u_config_json_open_or_create_main_file(&p->json);
2019-04-30 13:33:34 +00:00
ret = collect_entries(p);
if (ret != 0) {
teardown(p);
return -1;
}
#ifdef XRT_HAVE_LIBUSB
ret = p_libusb_init(p);
2019-04-30 13:33:34 +00:00
if (ret != 0) {
teardown(p);
return -1;
}
#endif
2019-04-30 13:33:34 +00:00
#ifdef XRT_HAVE_LIBUVC
ret = p_libuvc_init(p);
2019-04-30 13:33:34 +00:00
if (ret != 0) {
teardown(p);
return -1;
}
#endif
ret = p_tracking_init(p);
if (ret != 0) {
teardown(p);
return -1;
}
2019-04-30 13:33:34 +00:00
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);
2019-04-30 13:33:34 +00:00
return 0;
}
static void
teardown_devices(struct prober *p)
2019-04-30 13:33:34 +00:00
{
// Need to free all devices.
for (size_t i = 0; i < p->num_devices; i++) {
struct prober_device *pdev = &p->devices[i];
2019-04-30 13:33:34 +00:00
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
2019-06-20 17:27:21 +00:00
for (size_t j = 0; j < pdev->num_v4ls; j++) {
struct prober_v4l *v4l = &pdev->v4ls[j];
free((char *)v4l->path);
2019-06-20 17:27:21 +00:00
v4l->path = NULL;
}
if (pdev->v4ls != NULL) {
free(pdev->v4ls);
pdev->v4ls = NULL;
pdev->num_v4ls = 0;
}
#endif
2019-06-20 17:27:21 +00:00
#ifdef XRT_OS_LINUX
2019-04-30 13:33:34 +00:00
for (size_t j = 0; j < pdev->num_hidraws; j++) {
struct prober_hidraw *hidraw = &pdev->hidraws[j];
free((char *)hidraw->path);
2019-04-30 13:33:34 +00:00
hidraw->path = NULL;
}
if (pdev->hidraws != NULL) {
free(pdev->hidraws);
pdev->hidraws = NULL;
pdev->num_hidraws = 0;
}
#endif
2019-04-30 13:33:34 +00:00
}
if (p->devices != NULL) {
free(p->devices);
p->devices = NULL;
p->num_devices = 0;
}
}
static void
teardown(struct prober *p)
2019-04-30 13:33:34 +00:00
{
2019-08-31 11:49:32 +00:00
// First remove the variable tracking.
u_var_remove_root((void *)p);
2019-08-31 11:49:32 +00:00
2019-04-30 13:33:34 +00:00
// 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);
2019-04-30 13:33:34 +00:00
// 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);
2019-04-30 13:33:34 +00:00
#endif
#ifdef XRT_HAVE_LIBUSB
p_libusb_teardown(p);
#endif
u_config_json_close(&p->json);
free(p->disabled_drivers);
2019-04-30 13:33:34 +00:00
}
/*
*
* Member functions.
*
*/
static int
probe(struct xrt_prober *xp)
2019-04-30 13:33:34 +00:00
{
struct prober *p = (struct prober *)xp;
2019-04-30 13:33:34 +00:00
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
2019-04-30 13:33:34 +00:00
ret = p_libusb_probe(p);
if (ret != 0) {
P_ERROR(p, "Failed to enumerate libusb devices\n");
return -1;
}
#endif
2019-04-30 13:33:34 +00:00
#ifdef XRT_HAVE_LIBUVC
2019-04-30 13:33:34 +00:00
ret = p_libuvc_probe(p);
if (ret != 0) {
P_ERROR(p, "Failed to enumerate libuvc devices\n");
return -1;
}
2019-06-20 17:02:28 +00:00
#endif
2019-04-30 13:33:34 +00:00
return 0;
}
static int
dump(struct xrt_prober *xp)
2019-04-30 13:33:34 +00:00
{
struct prober *p = (struct prober *)xp;
2019-04-30 13:33:34 +00:00
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];
2019-04-30 13:33:34 +00:00
p_dump_device(p, pdev, (int)i);
}
return 0;
}
2019-05-31 16:55:19 +00:00
static void
2021-01-14 14:13:48 +00:00
handle_found_device(
struct prober *p, struct xrt_device **xdevs, size_t num_xdevs, bool *have_hmd, struct xrt_device *xdev)
2019-05-31 16:55:19 +00:00
{
P_DEBUG(p, "Found '%s' %p", xdev->str, (void *)xdev);
2019-05-31 16:55:19 +00:00
size_t i = 0;
for (; i < num_xdevs; i++) {
if (xdevs[i] == NULL) {
break;
}
}
2019-05-31 16:55:19 +00:00
if (i + 1 > num_xdevs) {
P_ERROR(p, "Too many devices, closing '%s'", xdev->str);
2019-05-31 16:55:19 +00:00
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;
2019-05-31 16:55:19 +00:00
}
xdevs[i] = xdev;
2019-05-31 16:55:19 +00:00
}
static void
2021-01-14 14:13:48 +00:00
add_from_devices(struct prober *p, struct xrt_device **xdevs, size_t num_xdevs, bool *have_hmd)
2019-04-30 13:33:34 +00:00
{
// Build a list of all current probed devices.
2021-01-14 14:13:48 +00:00
struct xrt_prober_device **dev_list = U_TYPED_ARRAY_CALLOC(struct xrt_prober_device *, p->num_devices);
2019-04-30 13:33:34 +00:00
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];
2019-04-30 13:33:34 +00:00
for (size_t k = 0; k < p->num_entries; k++) {
struct xrt_prober_entry *entry = p->entries[k];
2021-01-14 14:13:48 +00:00
if (pdev->base.vendor_id != entry->vendor_id || pdev->base.product_id != entry->product_id) {
2019-04-30 13:33:34 +00:00
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;
}
2021-01-14 14:13:48 +00:00
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]));
2019-04-30 13:33:34 +00:00
if (num_found <= 0) {
2019-05-31 16:55:19 +00:00
continue;
2019-04-30 13:33:34 +00:00
}
2021-01-14 14:13:48 +00:00
for (int created_idx = 0; created_idx < num_found; ++created_idx) {
if (new_xdevs[created_idx] == NULL) {
2021-01-14 14:13:48 +00:00
P_DEBUG(p,
"Leaving device creation loop "
"early: found function reported %i "
"created, but only %i non-null",
num_found, created_idx);
continue;
}
2021-01-14 14:13:48 +00:00
handle_found_device(p, xdevs, num_xdevs, have_hmd, new_xdevs[created_idx]);
}
2019-04-30 13:33:34 +00:00
}
}
// Free the temporary list.
free(dev_list);
}
2019-04-30 13:33:34 +00:00
static void
2021-01-14 14:13:48 +00:00
add_from_auto_probers(struct prober *p, struct xrt_device **xdevs, size_t num_xdevs, bool *have_hmd)
{
2019-04-30 13:33:34 +00:00
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) {
2019-05-31 16:55:19 +00:00
continue;
2019-04-30 13:33:34 +00:00
}
2019-05-31 16:55:19 +00:00
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]);
}
2019-04-30 13:33:34 +00:00
}
}
2020-10-15 17:11:25 +00:00
static void
2021-01-14 14:13:48 +00:00
add_from_remote(struct prober *p, struct xrt_device **xdevs, size_t num_xdevs, bool *have_hmd)
2020-10-15 17:11:25 +00:00
{
if (num_xdevs < 3) {
return;
}
#ifdef XRT_BUILD_DRIVER_REMOTE
int port = 4242;
if (!u_config_json_get_remote_port(&p->json, &port)) {
2020-10-15 17:11:25 +00:00
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
2021-01-14 14:13:48 +00:00
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);
}
2019-04-30 13:33:34 +00:00
// 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;
}
2019-05-31 16:55:19 +00:00
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);
2019-05-31 16:55:19 +00:00
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.
2019-05-31 16:55:19 +00:00
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]);
2019-05-31 16:55:19 +00:00
}
return 0;
2019-04-30 13:33:34 +00:00
}
static int
open_hid_interface(struct xrt_prober *xp,
struct xrt_prober_device *xpdev,
2019-04-30 13:33:34 +00:00
int interface,
struct os_hid_device **out_hid_dev)
2019-04-30 13:33:34 +00:00
{
struct prober_device *pdev = (struct prober_device *)xpdev;
2019-04-30 13:33:34 +00:00
int ret;
#ifdef XRT_OS_LINUX
2019-04-30 13:33:34 +00:00
for (size_t j = 0; j < pdev->num_hidraws; j++) {
struct prober_hidraw *hidraw = &pdev->hidraws[j];
2019-04-30 13:33:34 +00:00
if (hidraw->interface != interface) {
continue;
}
ret = os_hid_open_hidraw(hidraw->path, out_hid_dev);
if (ret != 0) {
2021-01-14 14:13:48 +00:00
U_LOG_E("Failed to open device '%s' got '%i'", hidraw->path, ret);
2019-04-30 13:33:34 +00:00
return ret;
}
return 0;
}
#endif // XRT_OS_LINUX
2019-04-30 13:33:34 +00:00
2020-12-16 17:12:56 +00:00
U_LOG_E(
"Could not find the requested "
"hid interface (%i) on the device!",
interface);
2019-04-30 13:33:34 +00:00
return -1;
}
2020-11-26 19:16:02 +00:00
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)
{
2021-01-14 14:13:48 +00:00
XRT_MAYBE_UNUSED struct prober_device *pdev = (struct prober_device *)xpdev;
#if defined(XRT_BUILD_DRIVER_VF)
2020-11-26 19:16:02 +00:00
const char *path = debug_get_option_vf_path();
if (path != NULL) {
struct xrt_fs *xfs = vf_fs_open_file(xfctx, path);
2020-11-26 19:16:02 +00:00
if (xfs) {
*out_xfs = xfs;
return 0;
}
}
#endif
2019-11-10 01:36:34 +00:00
#if defined(XRT_HAVE_V4L2)
if (pdev->num_v4ls == 0) {
return -1;
}
2020-09-06 12:31:10 +00:00
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
2021-01-14 14:13:48 +00:00
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);
}
2021-01-14 14:13:48 +00:00
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;
2021-01-14 14:13:48 +00:00
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;
2021-01-14 14:13:48 +00:00
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;
}
2019-04-30 13:33:34 +00:00
static void
destroy(struct xrt_prober **xp)
2019-04-30 13:33:34 +00:00
{
struct prober *p = (struct prober *)*xp;
2019-04-30 13:33:34 +00:00
if (p == NULL) {
return;
}
teardown(p);
free(p);
*xp = NULL;
}