[igt-dev] [PATCH i-g-t v1 1/1] Introduce new method of device selection

Zbigniew Kempczyński
2019-07-11T10:31:03Z

Change adds device selection based on implemented filters.
Different filters can be added to address different device
selection possibilities.

New device selection uses --device filter or IGT_DEVICE environent
variable. Selection of many devices can be done by using --device
argument multiple times. As IGT_DEVICE enviroment can be set
once ';' is recognized as filter separator.

Tool 'lsgpu' which uses device scanning feature was added.

Signed-off-by: Zbigniew Kempczyński <zbigniew.kempczynski@intel.com>
Cc: Arkadiusz Hiler <arkadiusz.hiler@intel.com>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: Petri Latvala <petri.latvala@intel.com>
---
docs/multi-device-selection.txt | 115 +++
lib/Makefile.sources | 2 +
lib/drmtest.c | 151 +++-
lib/drmtest.h | 9 +
lib/igt_core.c | 13 +
lib/igt_device_scan.c | 1425 +++++++++++++++++++++++++++++++
lib/igt_device_scan.h | 72 ++
lib/meson.build | 1 +
tools/Makefile.sources | 1 +
tools/lsgpu.c | 285 +++++++
tools/meson.build | 1 +
11 files changed, 2073 insertions(+), 2 deletions(-)
create mode 100644 docs/multi-device-selection.txt
create mode 100644 lib/igt_device_scan.c
create mode 100644 lib/igt_device_scan.h
create mode 100644 tools/lsgpu.c

diff --git a/docs/multi-device-selection.txt b/docs/multi-device-selection.txt
new file mode 100644
index 00000000..5b13cfcc
--- /dev/null
+++ b/docs/multi-device-selection.txt
@@ -0,0 +1,115 @@
+Multi-device scanning and selection
+-----------------------------------
+
+1. Scanning
+
+Device selection is build around scanning buses using udev (PCI and platform),
+gathering information about matching devices and using filters on discovered
+data.
+
+PCI scanning is easy and gives all information regarding devices (module doesn't
+need to be loaded). Problems occurs when platform devices are taken into
+account. Some of them have appropriate entries in device tree (like
+Raspberry Pi vc4 driver), so displaying them without loaded module is possible.
+Such devices expose OF_COMPATIBLE_X properties which are matched against
+appropriate driver. Some devices are registered inside platform tree at driver
+loading time so discovering such devices is not possible unless driver is loaded.
+
+
+2. Filtering
+
+After scanning all gathered information are stored in devices array. Filtering
+means creating a view that matches filter on devices array. Filters defined
+on non-discoverable devices loads module (if not already loaded) and execute
+device discovery again.
+
+Using 'lsgpu' tool we can get list of defined filter types ('-l' switch):
+...
+pci pci:[vendor=%04x/name][,device=%04x][,card=%d]
+ vendor is hex number or name
+...
+
+pci is filter name, it calls filter on devices array to select only PCI
+devices. For example:
+
+pci:vendor=8086
+find first pci card on the bus with 8086 PCI device ID
+
+pci:vendor=intel
+same as above but uses vendor name (see lsgpu -v)
+
+pci:vendor=8086,card=0
+same as above, card=0 is default
+
+pci:vendor=intel,device=1234,card=1
+finds second Intel card which device ID is 0x1234
+
+
+3. lsgpu
+
+To play with devices 'lsgpu' tool was written. It is mostly wrapper to igt
+devices scan code (igt_device_scan.c).
+
+If run without arguments it displays all recognized cards.
+Usage examples:
+
+# lsgpu -ra
+displays devices in 'simple' mode with only few properties and attributes
+
+# lsgpu -d
+displays devices in 'detail' mode, with all properties and attributes. Some
+attributes are skipped because acquiring value can take even seconds (there's
+special blacklist hash table inside the code)
+
+# lsgpu -v
+displays recognized vendors
+
+# lsgpu -l
+displays defined filter types
+
+# lsgpu -m 'vgem:card=0'
+matches first vgem card at index 0. I
+
+
+# lsgpu --device 'pci:card=0;vgem:card=0;vkms:card=0'
+and
+# lsgpu --device 'pci:card=0' --device 'vgem:card=0' --device 'vkms:card=0'
+and
+# export IGT_DEVICE='pci:card=0;vgem:card=0;vkms:card=0'
+# lsgpu
+matches multiple cards. IGT_DEVICE uses ';' separated filter syntax, it is also
+permitted in --device (function splits argument against ';').
+
+
+4. Compatibility with current device open API
+
+Practically all IGT tests use:
+int __drm_open_driver(int chipset);
+int __drm_open_driver_render(int chipset);
+
+Above functions were extended to use --device / env IGT_DEVICE filters
+(if given during IGT test run). If more than single device were passed
+first matching filter against chipset is selected. This allows test like
+prime_vgem to work, ex:
+
+# ./prime_vgem --r basic-read --device 'pci:card=0' --device 'vgem:card=0'
+# ./prime_vgem --r basic-read --device 'vgem:card=0' --device 'pci:card=0'
+
+
+5. New device selection API
+
+Following prototypes define new API:
+
+/* Multi device API */
+int __drm_open_card_with_nth_filter(int num, int chipset);
+int __drm_open_render_with_nth_filter(int num, int chipset);
+
+When test require DRIVER_INTEL it has to be sure that user didn't passed
+another device (vc4 for example). So if user passed --device vc4:card=0
+this open will fail.
+
+struct igt_device_card;
+int __drm_open_card(struct igt_device_card *card);
+int __drm_open_render(struct igt_device_card *card);
+
+These functions allows to open device regardless chip requirement.
diff --git a/lib/Makefile.sources b/lib/Makefile.sources
index e16de86e..c383a817 100644
--- a/lib/Makefile.sources
+++ b/lib/Makefile.sources
@@ -25,6 +25,8 @@ lib_source_list = \
igt_debugfs.h \
igt_device.c \
igt_device.h \
+ igt_device_scan.c \
+ igt_device_scan.h \
igt_aux.c \
igt_aux.h \
igt_color_encoding.c \
diff --git a/lib/drmtest.c b/lib/drmtest.c
index 25f20353..5a9cadc1 100644
--- a/lib/drmtest.c
+++ b/lib/drmtest.c
@@ -55,6 +55,7 @@
#include "igt_gt.h"
#include "igt_kmod.h"
#include "igt_sysfs.h"
+#include "igt_device_scan.h"
#include "version.h"
#include "config.h"
#include "intel_reg.h"
@@ -296,25 +297,171 @@ static int __open_driver(const char *base, int offset, unsigned int chipset)
return __search_and_open(base, offset, chipset);
}

+static int __open_driver_exact(const char *name, unsigned int chipset)
+{
+ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+ int fd;
+
+ fd = open_device(name, chipset);
+ if (fd != -1)
+ return fd;
+
+ pthread_mutex_lock(&mutex);
+ for (const struct module *m = modules; m->module; m++) {
+ if (chipset & m->bit) {
+ if (m->modprobe)
+ m->modprobe(m->module);
+ else
+ modprobe(m->module);
+ }
+ }
+ pthread_mutex_unlock(&mutex);
+
+ return open_device(name, chipset);
+}
+
+/**
+ * __find_card_with_chipset
+ * @chipset: chipset for compare with card match
+ * @card: pointer where card information will be stored
+ *
+ * For compatibility mode when multiple --device argument were passed
+ * this function tries to be smart enough to handle tests which opens
+ * more than single device. It iterates over filter list and
+ * compares chipset to card chipset for filter matched.
+ *
+ * Returns:
+ * True if card according to filters added and chipset was found,
+ * false othwerwise.
+ */
+static bool __find_card_with_chipset(int chipset, struct igt_device_card *card)
+{
+ int i, n = igt_device_filter_count();
+ const char *filter;
+ bool match;
+
+ for (i = 0; i < n; i++) {
+ filter = igt_device_filter_get(i);
+ match = igt_device_card_match(filter, card);
+ if (match && (card->chipset == chipset || chipset == DRIVER_ANY))
+ return true;
+ }
+
+ return false;
+}
+
/**
* __drm_open_driver:
* @chipset: OR'd flags for each chipset to search, eg. #DRIVER_INTEL
*
- * Open the first DRM device we can find, searching up to 16 device nodes
+ * Function opens device with following order:
+ * 1. when --device arguments are present device scanning will be executed,
+ * then matching considers all device which matches filter argument will be selected.
+ * 2. compatibility mode - open the first DRM device we can find,
+ * searching up to 16 device nodes.
*
* Returns:
* An open DRM fd or -1 on error
*/
int __drm_open_driver(int chipset)
{
+ int n = igt_device_filter_count();
+
+ if (n) {
+ bool found;
+ struct igt_device_card card;
+
+ found = __find_card_with_chipset(chipset, &card);
+ if (!found || !strlen(card.card))
+ return -1;
+
+ return __open_driver_exact(card.card, chipset);
+ }
+
return __open_driver("/dev/dri/card", 0, chipset);
}

-static int __drm_open_driver_render(int chipset)
+int __drm_open_driver_render(int chipset)
{
+ int n = igt_device_filter_count();
+
+ if (n) {
+ bool found;
+ struct igt_device_card card;
+
+ found = __find_card_with_chipset(chipset, &card);
+ if (!found || !strlen(card.render))
+ return -1;
+
+ return __open_driver_exact(card.render, chipset);
+ }
+
return __open_driver("/dev/dri/renderD", 128, chipset);
}

+static int __drm_open_with_nth_filter(int num, int chipset, bool open_render)
+{
+ struct igt_device_card card;
+ const char *filter, *devname;
+ bool match;
+ int n = igt_device_filter_count();
+
+ if (!n || num < 0 || num >= n) {
+ igt_warn("No device filter num == %d passed\n", num);
+ return -1;
+ }
+
+ filter = igt_device_filter_get(num);
+ match = igt_device_card_match(filter, &card);
+ if (!match) {
+ igt_warn("No device match filter: %s\n", filter);
+ return -1;
+ }
+
+ if (chipset != card.chipset) {
+ igt_warn("Filtered device doesn't match chipset (%d != %d)\n",
+ chipset, card.chipset);
+ return -1;
+ }
+ if (!strlen(card.card))
+ return -1;
+
+ devname = open_render ? card.render : card.card;
+ if (!strlen(devname)) {
+ igt_warn("No %s node matching filter: %s\n",
+ open_render ? "render" : "card", filter);
+ return -1;
+ }
+ return __open_driver_exact(devname, card.chipset);
+}
+
+int __drm_open_card_with_nth_filter(int num, int chipset)
+{
+ return __drm_open_with_nth_filter(num, chipset, false);
+}
+
+int __drm_open_render_with_nth_filter(int num, int chipset)
+{
+ return __drm_open_with_nth_filter(num, chipset, true);
+}
+
+int __drm_open_card(struct igt_device_card *card)
+{
+ if (!card || !strlen(card->card))
+ return -1;
+
+ return __open_driver_exact(card->card, card->chipset);
+}
+
+int __drm_open_render(struct igt_device_card *card)
+{
+ if (!card || !strlen(card->render))
+ return -1;
+
+ return __open_driver_exact(card->render, card->chipset);
+}
+
+
static int at_exit_drm_fd = -1;
static int at_exit_drm_render_fd = -1;

diff --git a/lib/drmtest.h b/lib/drmtest.h
index 6c4c3899..8f5888a9 100644
--- a/lib/drmtest.h
+++ b/lib/drmtest.h
@@ -45,6 +45,7 @@
#define DRIVER_AMDGPU (1 << 4)
#define DRIVER_V3D (1 << 5)
#define DRIVER_PANFROST (1 << 6)
+
/*
* Exclude DRVER_VGEM from DRIVER_ANY since if you run on a system
* with vgem as well as a supported driver, you can end up with a
@@ -76,6 +77,14 @@ int drm_open_driver(int chipset);
int drm_open_driver_master(int chipset);
int drm_open_driver_render(int chipset);
int __drm_open_driver(int chipset);
+int __drm_open_driver_render(int chipset);
+
+/* Multi device API */
+int __drm_open_card_with_nth_filter(int num, int chipset);
+int __drm_open_render_with_nth_filter(int num, int chipset);
+struct igt_device_card;
+int __drm_open_card(struct igt_device_card *card);
+int __drm_open_render(struct igt_device_card *card);

void gem_quiescent_gpu(int fd);

diff --git a/lib/igt_core.c b/lib/igt_core.c
index 1cbb09f9..9b851175 100644
--- a/lib/igt_core.c
+++ b/lib/igt_core.c
@@ -71,6 +71,7 @@
#include "igt_sysrq.h"
#include "igt_rc.h"
#include "igt_list.h"
+#include "igt_device_scan.h"

#define UNW_LOCAL_ONLY
#include <libunwind.h>
@@ -304,6 +305,7 @@ enum {
OPT_DEBUG,
OPT_INTERACTIVE_DEBUG,
OPT_SKIP_CRC,
+ OPT_DEVICE,
OPT_HELP = 'h'
};

@@ -624,6 +626,7 @@ static void print_usage(const char *help_str, bool output_on_stderr)
" --skip-crc-compare\n"
" --help-description\n"
" --describe\n"
+ " --device filter\n"
" --help|-h\n");
if (help_str)
fprintf(f, "%s\n", help_str);
@@ -725,6 +728,11 @@ static void common_init_env(void)
if (env) {
__set_forced_driver(env);
}
+
+ env = getenv("IGT_DEVICE");
+ if (env) {
+ igt_device_filter_add(env);
+ }
}

static int common_init(int *argc, char **argv,
@@ -743,6 +751,7 @@ static int common_init(int *argc, char **argv,
{"debug", optional_argument, NULL, OPT_DEBUG},
{"interactive-debug", optional_argument, NULL, OPT_INTERACTIVE_DEBUG},
{"skip-crc-compare", no_argument, NULL, OPT_SKIP_CRC},
+ {"device", required_argument, NULL, OPT_DEVICE},
{"help", no_argument, NULL, OPT_HELP},
{0, 0, 0, 0}
};
@@ -865,6 +874,10 @@ static int common_init(int *argc, char **argv,
case OPT_SKIP_CRC:
igt_skip_crc_compare = true;
goto out;
+ case OPT_DEVICE:
+ assert(optarg);
+ igt_device_filter_add(optarg);
+ break;
case OPT_HELP:
print_usage(help_str, false);
ret = -1;
diff --git a/lib/igt_device_scan.c b/lib/igt_device_scan.c
new file mode 100644
index 00000000..6cf0aea6
--- /dev/null
+++ b/lib/igt_device_scan.c
@@ -0,0 +1,1425 @@
+/*
+ * Copyright © 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include "igt.h"
+#include "igt_kmod.h"
+#include "igt_sysfs.h"
+#include "igt_device.h"
+#include "igt_device_scan.h"
+#include <glib.h>
+#include <libudev.h>
+#include <linux/limits.h>
+#include <sys/stat.h>
+
+//#define DEBUG_DEVICE_SCAN
+#ifdef DEBUG_DEVICE_SCAN
+#define DBG(...) \
+{ \
+ struct timeval tm; \
+ gettimeofday(&tm, NULL); \
+ printf("%10ld.%03ld: ", tm.tv_sec, tm.tv_usec); \
+ printf(__VA_ARGS__); \
+ }
+
+#else
+#define DBG(...) {}
+#endif
+
+#define IGT_BUS_PCI_DRIVERS_DIR "/sys/bus/pci/drivers"
+#define IGT_DRM_PATH "/dev/dri"
+
+/* Scanning PCI is simple and well defined. Platform devices
+ * are listed in device tree but until driver is really loaded
+ * we only 'expect' their existence.
+ *
+ * From this reason platform scanning was separated to two
+ * distinct scan types - non-discoverable and discoverable.
+ *
+ * 1. Non-discoverable scan type checks modalias attribute.
+ * Such devices will occur within platform subsystem only
+ * after driver is loaded. Example are 'platform:vgem'
+ * 'platform:vkms' and 'platform:exynos'.
+ *
+ * 2. Discoverable scan type checks OF_COMPATIBLE_0 property.
+ * This simple check should be enough to display platform
+ * gpu defined in device tree even if driver wasn't already
+ * loaded.
+ */
+enum igt_devices_scan_type {
+ IGT_SCAN_PCI = 1,
+ IGT_SCAN_PLATFORM_NON_DISCOVERABLE = 2,
+ IGT_SCAN_PLATFORM_DISCOVERABLE = 3,
+};
+
+static GHashTable *blacklist_keys_ht; //sysattrs we don't want to read
+static GHashTable *gpu_pci_class_ht; //gpu pci classes we know
+static GHashTable *gpu_vendor_ht; //search id -> vendor_spec mapping
+static GHashTable *filter_definition_ht; //supported filters (pci=..., etc.)
+
+/* Generic name->value struct, used to fill hash tables */
+struct name_value {
+ const char *name;
+ gpointer *value;
+};
+
+/* Vendor specific data */
+struct vendor_spec {
+ enum igt_device_type dev_type;
+ const char *vendor;
+ const char *modname;
+ const char *modopts;
+ const char *match_driver; //reported driver name not always equal module name
+ int chipset;
+};
+
+/* Single igt_device found in PCI / platform subsystems */
+struct igt_device {
+ char *pretty_name;
+ struct vendor_spec *vs;
+ GHashTable *props_ht;
+ GHashTable *attrs_ht;
+ char *syspath;
+ char *drvpath;
+ char *drm_card_path;
+ char *drm_render_path;
+ char *vendor;
+ char *device;
+ enum igt_device_type dev_type;
+};
+
+/* Scanned devices struct */
+struct igt_devices {
+ GPtrArray *devs; //all gpu devices array
+ GPtrArray *view; //filtered view
+ bool devs_scanned;
+};
+
+/* Scanned devices holder */
+static struct igt_devices igt_devs;
+
+/* PCI device classes we take into account during scan.
+ * Attribute PCI_CLASS is compared with key beneath. */
+struct name_value gpu_pci_class_list[] = {
+ { "30000", (gpointer) "VGA compatibile controller" },
+ { "30100", (gpointer) "XGA compatibile controller" },
+ { "30200", (gpointer) "3D controller" },
+ { "38000", (gpointer) "Display controller" },
+ { NULL, },
+};
+
+static struct vendor_spec v_intel = { .dev_type = DEV_PCI,
+ .vendor = "Intel",
+ .modname = "i915",
+ .chipset = DRIVER_INTEL
+ };
+static struct vendor_spec v_amd = { .dev_type = DEV_PCI,
+ .vendor = "AMD",
+ .modname = "amdgpu",
+ .chipset = DRIVER_AMDGPU
+ };
+static struct vendor_spec v_nvidia = { .dev_type = DEV_PCI,
+ .vendor = "NVIDIA",
+ .modname = "nvidia",
+ .chipset = DRIVER_ANY
+ };
+static struct vendor_spec v_vgem = { .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
+ .vendor = "Virtual-GEM",
+ .modname = "vgem",
+ .chipset = DRIVER_VGEM
+ };
+static struct vendor_spec v_vkms = { .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
+ .vendor = "Virtual-KMS",
+ .modname = "vkms",
+ .chipset = DRIVER_ANY
+ };
+static struct vendor_spec v_exynos = { .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
+ .vendor = "Samsung",
+ .modname = "exynosdrm",
+ .match_driver = "exynos-drm",
+ .chipset = DRIVER_ANY
+ };
+static struct vendor_spec v_vc4 = { .dev_type = DEV_PLATFORM_DISCOVERABLE,
+ .vendor = "Broadcom",
+ .modname = "vc4",
+ .match_driver = "vc4-drm",
+ .chipset = DRIVER_VC4
+ };
+
+/* Mapping vendor id => vendor_spec should be unique (vendor matching
+ * is written around this).
+ *
+ * Keys must be defined as follows:
+ * PCI devices: PCI_SLOT_ID,
+ * Non-discoverable platform devices: MODALIAS,
+ * Discoverable platform devices: OF_COMPATIBLE_0.
+*/
+struct name_value gpu_vendor_list[] = {
+ { "8086", (gpointer) &v_intel },
+ { "1002", (gpointer) &v_amd },
+ { "10DE", (gpointer) &v_nvidia },
+ { "platform:vgem", (gpointer) &v_vgem },
+ { "platform:vkms", (gpointer) &v_vkms },
+ { "platform:exynos-drm", (gpointer) &v_exynos },
+ { "brcm,bcm2835-vc4", (gpointer) &v_vc4 },
+ { NULL, },
+};
+
+/* Generic hash table fill function, requires name / value ptrs array */
+static void fill_ht(GHashTable **ht, struct name_value *data)
+{
+ if (*ht)
+ return;
+
+ *ht = g_hash_table_new(g_str_hash, g_str_equal);
+ igt_assert(*ht);
+
+ while (data->name) {
+ g_hash_table_insert(*ht,
+ (gpointer) data->name,
+ data->value);
+ data++;
+ }
+}
+
+#define get_vendor_spec(prop) \
+ g_hash_table_lookup(gpu_vendor_ht, prop)
+
+/* Go through whole vendor hash table and compare against vendor field.
+ * Used mostly with vendor=... filter parameter when PCI id is not matched.
+*/
+static bool is_pci_vendor_name(const char *name)
+{
+ bool ret = false;
+ GList *keys = g_hash_table_get_keys(gpu_vendor_ht);
+
+ if (!name)
+ return false;
+
+ while (keys) {
+ char *k = (char *) keys->data;
+ struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht, k);
+ keys = g_list_next(keys);
+
+ if (vs->dev_type != DEV_PCI)
+ continue;
+
+ if (!strcasecmp(name, vs->vendor)) {
+ ret = true;
+ break;
+ }
+ }
+ g_list_free(keys);
+
+ return ret;
+}
+
+/* Reading sysattr values can take time (even seconds),
+ * we want to avoid reading such keys.
+*/
+static void populate_blacklist_keys(void)
+{
+ const char *keys[] = { "config", "modalias",
+ "resource",
+ "resource0", "resource1", "resource2",
+ "resource3", "resource4", "resource5",
+ "resource0_wc", "resource1_wc", "resource2_wc",
+ "resource3_wc", "resource4_wc", "resource5_wc",
+ "driver",
+ "uevent", NULL};
+ const char *key;
+ int i = 0;
+
+ if (blacklist_keys_ht)
+ return;
+
+ blacklist_keys_ht = g_hash_table_new(g_str_hash, g_str_equal);
+ igt_assert(blacklist_keys_ht);
+
+ while ((key = keys[i++]))
+ g_hash_table_add(blacklist_keys_ht, (gpointer) key);
+}
+
+#define is_on_blacklist(key) \
+ g_hash_table_contains(blacklist_keys_ht, key)
+
+static struct igt_device *igt_device_new(void)
+{
+ struct igt_device *dev;
+ dev = calloc(1, sizeof(struct igt_device));
+ if (!dev)
+ return NULL;
+
+ dev->attrs_ht = g_hash_table_new_full(g_str_hash, g_str_equal,
+ free, free);
+ dev->props_ht = g_hash_table_new_full(g_str_hash, g_str_equal,
+ free, free);
+
+ if (dev->attrs_ht && dev->props_ht)
+ return dev;
+
+ return NULL;
+}
+
+static void igt_device_add_prop(struct igt_device *dev,
+ const char *key, const char *value)
+{
+ if (!key || !value)
+ return;
+
+ g_hash_table_insert(dev->props_ht, strdup(key), strdup(value));
+}
+
+static void igt_device_add_attr(struct igt_device *dev,
+ const char *key, const char *value)
+{
+ const char *v = value;
+
+ if (!key)
+ return;
+
+ /* It's possible we have symlink at key filename, but udev
+ * library resolves only few of them */
+ if (!v) {
+ struct stat st;
+ char path[PATH_MAX];
+ char linkto[PATH_MAX];
+ int len;
+
+ snprintf(path, sizeof(path), "%s/%s", dev->syspath, key);
+ if (lstat(path, &st) != 0)
+ return;
+
+ len = readlink(path, linkto, sizeof(linkto));
+ if (len <= 0 || len == (ssize_t) sizeof(linkto))
+ return;
+ linkto[len] = '\0';
+ v = strrchr(linkto, '/');
+ if (v == NULL)
+ return;
+ v++;
+ }
+
+ g_hash_table_insert(dev->attrs_ht, strdup(key), strdup(v));
+}
+
+static void scan_props(struct udev_device *dev, struct igt_device *idev)
+{
+ struct udev_list_entry *entry;
+
+ entry = udev_device_get_properties_list_entry(dev);
+ while (entry) {
+ const char *name = udev_list_entry_get_name(entry);
+ const char *value = udev_list_entry_get_value(entry);
+ igt_device_add_prop(idev, name, value);
+ entry = udev_list_entry_get_next(entry);
+ DBG("prop: %s, val: %s\n", name, value);
+ }
+}
+
+static void scan_attrs(struct udev_device *dev, struct igt_device *idev)
+{
+ struct udev_list_entry *entry;
+
+ entry = udev_device_get_sysattr_list_entry(dev);
+ while (entry) {
+ const char *key = udev_list_entry_get_name(entry);
+ const char *value;
+
+ if (is_on_blacklist(key)) {
+ entry = udev_list_entry_get_next(entry);
+ continue;
+ }
+
+ value = udev_device_get_sysattr_value(dev, key);
+ igt_device_add_attr(idev, key, value);
+ entry = udev_list_entry_get_next(entry);
+ DBG("attr: %s, val: %s\n", key, value);
+ }
+}
+
+#define get_prop(dev, prop) (char *) g_hash_table_lookup(dev->props_ht, prop)
+#define get_attr(dev, attr) (char *) g_hash_table_lookup(dev->attrs_ht, attr)
+#define get_prop_pci_id(dev) get_prop(dev, "PCI_ID")
+#define get_prop_pci_slot(dev) get_prop(dev, "PCI_SLOT_NAME")
+#define get_prop_devpath(dev) get_prop(dev, "DEVPATH")
+#define get_prop_driver(dev) get_prop(dev, "DRIVER")
+#define get_prop_modalias(dev) get_prop(dev, "MODALIAS")
+#define get_prop_of_compatible_0(dev) get_prop(dev, "OF_COMPATIBLE_0")
+
+/* Gets PCI_ID property, splits to xxxx:yyyy and stores
+ * xxxx to dev->vendor and yyyy to dev->device for
+ * faster access.
+ */
+static void set_vendor_device(struct igt_device *dev)
+{
+ const char *pci_id = get_prop_pci_id(dev);
+ if (!pci_id || strlen(pci_id) != 9)
+ return;
+ dev->vendor = strndup(pci_id, 4);
+ dev->device = strndup(pci_id + 5, 4);
+}
+
+static void find_drm_paths(struct igt_device *dev, bool platform_check)
+{
+ char dirname[PATH_MAX];
+ DIR *dir;
+ const char *driver = get_prop_driver(dev);
+ const char *devpath = get_prop_devpath(dev);
+ struct dirent *e;
+
+ /* There's no DRIVER, so no /dev/dri/cardX and /dev/dri/renderD
+ * files exists. If there's no DEVPATH there's something wrong
+ * as well. */
+ if (!platform_check && (!driver || !devpath))
+ return;
+
+ snprintf(dirname, PATH_MAX, "%s/drm", dev->syspath);
+ dir = opendir(dirname);
+ if (!dir)
+ return;
+
+ while((e = readdir(dir))) {
+ int n;
+ if (sscanf(e->d_name, "card%d", &n) == 1)
+ dev->drm_card_path = g_strdup_printf("%s/%s",
+ IGT_DRM_PATH,
+ e->d_name);
+ else if (sscanf(e->d_name, "renderD%d", &n) == 1)
+ dev->drm_render_path = g_strdup_printf("%s/%s",
+ IGT_DRM_PATH,
+ e->d_name);
+ }
+ closedir(dir);
+
+ if (dev->drm_card_path)
+ DBG("card: %s\n", dev->drm_card_path);
+ if (dev->drm_render_path)
+ DBG("rend: %s\n", dev->drm_render_path);
+}
+
+static bool prepare_scan(void)
+{
+ if (!igt_devs.devs)
+ igt_devs.devs = g_ptr_array_sized_new(4);
+ if (!igt_devs.view)
+ igt_devs.view = g_ptr_array_sized_new(4);
+
+ if (!igt_devs.devs || !igt_devs.view)
+ return false;
+
+ return true;
+}
+
+static bool is_valid_pci_gpu(const char *prop)
+{
+ return g_hash_table_contains(gpu_pci_class_ht, prop);
+}
+
+static bool is_valid_platform_gpu(const char *prop)
+{
+ struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht, prop);
+ if (!vs)
+ return false;
+
+ return vs->dev_type == DEV_PLATFORM_NON_DISCOVERABLE ||
+ vs->dev_type == DEV_PLATFORM_DISCOVERABLE;
+}
+
+static void add_pci_gpu(struct igt_device *dev)
+{
+ find_drm_paths(dev, false);
+
+ g_ptr_array_add(igt_devs.devs, dev);
+ g_ptr_array_add(igt_devs.view, dev);
+}
+
+static void add_platform_non_discoverable_gpu(struct igt_device *dev)
+{
+ find_drm_paths(dev, true);
+ if (!get_prop_driver(dev))
+ igt_device_add_prop(dev, "DRIVER", dev->vs->modname);
+
+ g_ptr_array_add(igt_devs.devs, dev);
+ g_ptr_array_add(igt_devs.view, dev);
+}
+
+static void add_platform_discoverable_gpu(struct igt_device *dev)
+{
+ find_drm_paths(dev, true);
+ g_ptr_array_add(igt_devs.devs, dev);
+ g_ptr_array_add(igt_devs.view, dev);
+}
+
+/* Classifier table.
+ * Scanning udev devices looks same for pci and platform devices, but differs
+ * in logic regarding adding devices to the device array.
+*/
+struct {
+ bool (*is_valid)(const char *prop); //check is device belongs to the class
+ void (*add)(struct igt_device *dev); //add device to the array and aux stuff
+ unsigned int dev_type;
+ const char *subsys;
+ const char *findprop;
+} cls[] = {
+ [IGT_SCAN_PCI] = { .subsys = "pci",
+ .findprop = "PCI_CLASS",
+ .dev_type = DEV_PCI,
+ .is_valid = is_valid_pci_gpu,
+ .add = add_pci_gpu,
+ },
+ [IGT_SCAN_PLATFORM_NON_DISCOVERABLE] = { .subsys = "platform",
+ .findprop = "MODALIAS",
+ .dev_type = DEV_PLATFORM_NON_DISCOVERABLE,
+ .is_valid = is_valid_platform_gpu,
+ .add = add_platform_non_discoverable_gpu,
+ },
+ [IGT_SCAN_PLATFORM_DISCOVERABLE] = { .subsys = "platform",
+ .findprop = "OF_COMPATIBLE_0",
+ .dev_type = DEV_PLATFORM_DISCOVERABLE,
+ .is_valid = is_valid_platform_gpu,
+ .add = add_platform_discoverable_gpu,
+ },
+};
+
+static void scan_devices(enum igt_devices_scan_type scantype)
+{
+ struct udev *udev;
+ struct udev_device *dev;
+ struct udev_enumerate *enumerate;
+ struct udev_list_entry *devices, *dev_list_entry;
+ int ret;
+
+ udev = udev_new();
+ igt_assert(udev);
+
+ enumerate = udev_enumerate_new(udev);
+ igt_assert(enumerate);
+
+ DBG("Scanning subsystem %s\n", cls[scantype].subsys);
+ ret = udev_enumerate_add_match_subsystem(enumerate, cls[scantype].subsys);
+ igt_assert(!ret);
+
+ ret = udev_enumerate_scan_devices(enumerate);
+ igt_assert(!ret);
+
+ devices = udev_enumerate_get_list_entry(enumerate);
+ if (!devices)
+ return;
+
+ udev_list_entry_foreach(dev_list_entry, devices) {
+ struct igt_device *idev;
+ const char *path;
+ const char *prop;
+
+ path = udev_list_entry_get_name(dev_list_entry);
+ dev = udev_device_new_from_syspath(udev, path);
+ prop = udev_device_get_property_value(dev, cls[scantype].findprop);
+ if (!prop || !cls[scantype].is_valid(prop)) {
+ udev_device_unref(dev);
+ continue;
+ }
+ idev = igt_device_new();
+ igt_assert(idev);
+
+ idev->dev_type = cls[scantype].dev_type;
+ idev->syspath = strdup(udev_device_get_syspath(dev));
+ scan_props(dev, idev);
+ scan_attrs(dev, idev);
+
+ switch (idev->dev_type) {
+ case DEV_PCI:
+ set_vendor_device(idev);
+ idev->pretty_name = strdup(get_prop_pci_slot(idev));
+ idev->vs = get_vendor_spec(idev->vendor);
+ break;
+ default:
+ idev->pretty_name = strdup(prop);
+ idev->vs = get_vendor_spec(prop);
+ }
+ igt_assert(idev->vs);
+
+ cls[scantype].add(idev);
+ udev_device_unref(dev);
+ }
+ udev_enumerate_unref(enumerate);
+ udev_unref(udev);
+}
+
+struct name_value filter_definition_list[];
+static void populate_gpu_data(void)
+{
+ fill_ht(&gpu_pci_class_ht, &gpu_pci_class_list[0]);
+ fill_ht(&gpu_vendor_ht, &gpu_vendor_list[0]);
+ fill_ht(&filter_definition_ht, &filter_definition_list[0]);
+}
+
+static void igt_device_free(struct igt_device *dev)
+{
+ free(dev->pretty_name);
+ free(dev->device);
+ free(dev->drm_card_path);
+ free(dev->drm_render_path);
+ free(dev->drvpath);
+ free(dev->syspath);
+ free(dev->vendor);
+ g_hash_table_destroy(dev->attrs_ht);
+ g_hash_table_destroy(dev->props_ht);
+}
+
+void igt_devices_scan(bool force)
+{
+ if (force && igt_devs.devs_scanned) {
+ for (int i = 0; i < igt_devs.devs->len; i++) {
+ struct igt_device *dev = g_ptr_array_index(igt_devs.devs, i);
+ igt_device_free(dev);
+ free(dev);
+ }
+ igt_devs.devs_scanned = false;
+ g_ptr_array_free(igt_devs.view, true);
+ g_ptr_array_free(igt_devs.devs, true);
+ igt_devs.view = NULL;
+ igt_devs.devs = NULL;
+ }
+
+ if (igt_devs.devs_scanned)
+ return;
+
+ populate_blacklist_keys(); //keys from sysattr we skip
+ populate_gpu_data();
+
+ prepare_scan();
+ scan_devices(IGT_SCAN_PCI);
+ scan_devices(IGT_SCAN_PLATFORM_NON_DISCOVERABLE);
+ scan_devices(IGT_SCAN_PLATFORM_DISCOVERABLE);
+ igt_devs.devs_scanned = true;
+}
+
+#define pr_simple(k, v) printf(" %-16s: %s\n", k, v)
+#define pr_simple_prop(dev, key) pr_simple(key, get_prop(dev, key))
+#define pr_simple_attr(dev, key) pr_simple(key, get_attr(dev, key))
+
+static void print_vendor(struct igt_device *dev)
+{
+ struct vendor_spec *vs = dev->vs;
+ char *info = alloca(256);
+
+ snprintf(info, 256, "%s (%s, module: %s%s)", dev->vendor,
+ vs ? vs->vendor : "unknown",
+ vs ? vs->modname : "unknown",
+ dev->drm_card_path ? "" : " [not binded/loaded]");
+ pr_simple("vendor", info);
+}
+
+static void igt_devs_print_simple(GPtrArray *view,
+ bool show_props, bool show_attrs)
+{
+ struct igt_device *dev;
+ int i;
+
+ if (!view)
+ return;
+
+ for (i = 0; i < view->len; i++) {
+ dev = g_ptr_array_index(view, i);
+ printf("%s\n", dev->pretty_name);
+ pr_simple("syspath", dev->syspath);
+ pr_simple("drm card", dev->drm_card_path);
+ pr_simple("drm render", dev->drm_render_path);
+
+ if (show_props) {
+ if (get_prop_pci_id(dev))
+ pr_simple_prop(dev, "PCI_ID");
+ pr_simple_prop(dev, "DRIVER");
+ }
+
+ /* We show attrs only for PCI devices */
+ if (show_attrs && dev->dev_type == DEV_PCI) {
+ print_vendor(dev);
+ pr_simple("device", dev->device);
+ }
+ }
+}
+
+#define pr_detail(k, v) printf("%-32s: %s\n", k, v)
+
+static void print_ht(GHashTable *ht)
+{
+ GList *keys = g_hash_table_get_keys(ht);
+ keys = g_list_sort(keys, (GCompareFunc) strcmp);
+ while (keys) {
+ char *k = (char *) keys->data;
+ char *v = g_hash_table_lookup(ht, k);
+ pr_detail(k, v);
+ keys = g_list_next(keys);
+ }
+ g_list_free(keys);
+}
+
+static void igt_devs_print_detail(GPtrArray *view,
+ bool show_props, bool show_attrs)
+{
+ struct igt_device *dev;
+ int i;
+ (void) show_props;
+ (void) show_attrs;
+
+ if (!view)
+ return;
+
+ for (i = 0; i < view->len; i++) {
+ dev = g_ptr_array_index(view, i);
+ printf("========== %s ==========\n", dev->pretty_name);
+ printf("[drm]\n");
+ pr_detail("card device", dev->drm_card_path);
+ pr_detail("render device", dev->drm_render_path);
+ pr_detail("syspath", dev->syspath);
+
+ printf("\n[properties]\n");
+ print_ht(dev->props_ht);
+ printf("\n[attributes]\n");
+ print_ht(dev->attrs_ht);
+ }
+}
+
+static struct print_func {
+ void (*prn)(GPtrArray *, bool, bool);
+} print_functions[] = {
+ [IGT_PRINT_SIMPLE] = { .prn = igt_devs_print_simple },
+ [IGT_PRINT_DETAIL] = { .prn = igt_devs_print_detail },
+};
+
+void igt_devices_print(enum igt_devices_print_type printtype,
+ bool show_props,
+ bool show_attrs)
+{
+ print_functions[printtype].prn(igt_devs.view,
+ show_props, show_attrs);
+}
+
+void igt_devices_print_vendors(void)
+{
+ struct name_value *list = &gpu_vendor_list[0];
+ printf("Recognized vendors:\n");
+
+ printf("%-16s %-32s %-32s %-16s\n", "Device type", "id",
+ "vendor", "module");
+ while (list->name) {
+ char *k = (char *) list->name;
+ struct vendor_spec *vs = g_hash_table_lookup(gpu_vendor_ht, k);
+ if (vs->dev_type == DEV_PCI)
+ printf("%-16s %-32s %-32s %-16s\n", "pci",
+ k, vs->vendor, vs->modname);
+ else if (vs->dev_type == DEV_PLATFORM_NON_DISCOVERABLE)
+ printf("%-16s %-32s %-32s %-16s\n", "non-discoverable",
+ k, vs->vendor, vs->modname);
+ else if (vs->dev_type == DEV_PLATFORM_DISCOVERABLE)
+ printf("%-16s %-32s %-32s %-16s\n", "discoverable",
+ k, vs->vendor, vs->modname);
+ list++;
+ }
+}
+
+static bool sys_write_attr(const char *dirname, const char *attr,
+ const char *str)
+{
+ int dir;
+
+ dir = open(dirname, O_RDONLY);
+ if (dir < 0)
+ return false;
+
+ if (igt_sysfs_printf(dir, attr, "%s", str) < 0) {
+ printf("Error, can't write to %s, err: %s\n",
+ attr, strerror(errno));
+ close(dir);
+ return false;
+ }
+
+ close(dir);
+ return true;
+}
+
+static bool ensure_driver_is_loaded(const char *modname, const char *modopts)
+{
+ if (!modname)
+ return false;
+
+ if (!igt_kmod_is_loaded(modname)) {
+ int ret;
+ igt_debug("Loading module %s\n", modname);
+ ret = igt_kmod_load(modname, modopts);
+ if (ret) {
+ igt_warn("Can't load module %s\n", modname);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+#define ensure_device_has_driver_loaded(dev) \
+ ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts)
+
+static struct igt_device *igt_devices_find_pci_id(const char *pci_slot)
+{
+ struct igt_device *dev;
+ int i;
+ GPtrArray *arr = igt_devs.devs;
+
+ if (!pci_slot)
+ return NULL;
+
+ for (i = 0; i < arr->len; i++) {
+ dev = g_ptr_array_index(arr, i);
+ if (dev->dev_type != DEV_PCI)
+ continue;
+ if (!strcmp(pci_slot, get_prop_pci_slot(dev)))
+ return dev;
+ }
+
+ return NULL;
+}
+
+bool igt_device_pci_unbind_module(const char *pci_slot)
+{
+ char path[PATH_MAX];
+ struct igt_device *dev = igt_devices_find_pci_id(pci_slot);
+
+ if (!dev)
+ return false;
+
+ if (dev->dev_type != DEV_PCI)
+ return false;
+
+ igt_assert(dev->vs);
+
+ if (!ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts))
+ return false;
+
+ igt_info("Unbinding driver %s on %s\n", dev->vs->modname, pci_slot);
+ snprintf(path, PATH_MAX, "%s/%s", IGT_BUS_PCI_DRIVERS_DIR,
+ dev->vs->modname);
+
+ return sys_write_attr(path, "unbind", pci_slot);
+}
+
+bool igt_device_pci_bind_module(const char *pci_slot)
+{
+ char path[PATH_MAX];
+ struct igt_device *dev = igt_devices_find_pci_id(pci_slot);
+
+ if (!dev)
+ return false;
+
+ if (dev->dev_type != DEV_PCI)
+ return false;
+
+ igt_assert(dev->vs);
+
+ if (!ensure_driver_is_loaded(dev->vs->modname, dev->vs->modopts))
+ return false;
+
+ igt_info("Binding driver %s to %s\n", dev->vs->modname, pci_slot);
+ snprintf(path, PATH_MAX, "%s/%s", IGT_BUS_PCI_DRIVERS_DIR,
+ dev->vs->modname);
+
+ return sys_write_attr(path, "bind", pci_slot);
+}
+
+static bool pci_rebind_module(struct igt_device *dev)
+{
+ if (dev && dev->dev_type != DEV_PCI)
+ return false;
+
+ igt_device_pci_unbind_module(get_prop_pci_slot(dev));
+ igt_device_pci_bind_module(get_prop_pci_slot(dev));
+
+ return true;
+}
+
+void igt_devices_bind_modules(void)
+{
+ struct igt_device *dev;
+ int i;
+ GPtrArray *arr;
+ printf("Binding modules...\n");
+
+ arr = igt_devs.devs;
+ for (i = 0; i < arr->len; i++) {
+ dev = g_ptr_array_index(arr, i);
+ if (!dev->drm_card_path) {
+ pci_rebind_module(dev);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+#define FILTER_SPEC_NAME_LEN 32
+#define FILTER_SPEC_DATA_LEN 256
+struct filter_spec {
+ char name[FILTER_SPEC_NAME_LEN];
+ char data[FILTER_SPEC_DATA_LEN];
+};
+
+struct filter_func {
+ GPtrArray *(*filter_function)(struct filter_spec *fspec,
+ struct filter_func *ffunc);
+ const char *help;
+ const char *detail;
+
+ struct vendor_spec *vs; //pointer to vs (if any)
+};
+
+union filter_data {
+ struct {
+ char *card;
+ } drm;
+ struct {
+ char *vendor;
+ char *device;
+ char *card;
+ } pci;
+ struct {
+ char *card;
+ } module;
+ struct {
+ char *card;
+ } compatible;
+};
+
+static GHashTable *split_filter_data(const char *data)
+{
+ GHashTable *ht = g_hash_table_new_full(g_str_hash, g_str_equal,
+ free, free);
+ gchar **s;
+ gchar **strv = g_strsplit(data, ",", -1);
+
+ s = strv;
+ while (*s) {
+ char *k, *v;
+ v = strchr(*s, '=');
+ if (!v) {
+ s++;
+ continue;
+ }
+ k = strndup(*s, v - (*s));
+ v = strdup(v + 1);
+ g_hash_table_insert(ht, k, v);
+ s++;
+ }
+ g_strfreev(strv);
+
+ return ht;
+}
+
+static bool get_filter_spec(const char *filter, struct filter_spec *spec)
+{
+ if (!filter)
+ return false;
+
+ if (sscanf(filter, "%31[^:]:%255s", spec->name, spec->data) == 2)
+ return true;
+
+ return false;
+}
+
+static GPtrArray *filter_drm(struct filter_spec *fspec,
+ struct filter_func *ffunc)
+{
+ GHashTable *ht;
+ GPtrArray *devs, *view;
+ union filter_data fdata;
+ int card = -1;
+ char cardstr[NAME_MAX];
+
+ (void) ffunc;
+ DBG("filter drm\n");
+
+ ht = split_filter_data(fspec->data);
+ fdata.drm.card = g_hash_table_lookup(ht, "card");
+
+ view = igt_devs.view;
+ devs = igt_devs.devs;
+
+ if (fdata.drm.card) {
+ sscanf(fdata.drm.card, "%d", &card);
+ if (card < 0) {
+ g_hash_table_destroy(ht);
+ return view;
+ }
+ }
+
+ snprintf(cardstr, NAME_MAX, "%s/card%d", IGT_DRM_PATH, card);
+ for (int i = 0; i < devs->len; i++) {
+ struct igt_device *dev = g_ptr_array_index(devs, i);
+ if (dev->drm_card_path && !strcmp(cardstr, dev->drm_card_path)) {
+ g_ptr_array_add(view, dev);
+ break;
+ }
+ }
+
+ DBG("Filter drm view size: %d\n", view->len);
+
+ g_hash_table_destroy(ht);
+
+ return view;
+}
+
+static GPtrArray *filter_pci(struct filter_spec *fspec,
+ struct filter_func *ffunc)
+{
+ GHashTable *ht;
+ GPtrArray *devs, *view;
+ union filter_data fdata;
+
+ DBG("filter pci\n");
+
+ (void) ffunc;
+ ht = split_filter_data(fspec->data);
+ fdata.pci.vendor = g_hash_table_lookup(ht, "vendor");
+ fdata.pci.device = g_hash_table_lookup(ht, "device");
+ fdata.pci.card = g_hash_table_lookup(ht, "card");
+
+ view = igt_devs.view;
+ devs = igt_devs.devs;
+
+ for (int i = 0; i < devs->len; i++) {
+ struct igt_device *dev = g_ptr_array_index(devs, i);
+
+ if (dev->dev_type != DEV_PCI)
+ continue;
+
+ /* Skip if 'vendor' doesn't match (hex or name) */
+ if (fdata.pci.vendor &&
+ strcasecmp(fdata.pci.vendor, dev->vendor) &&
+ !is_pci_vendor_name(fdata.pci.vendor))
+ continue;
+
+ /* Skip if 'device' doesn't match */
+ if (fdata.pci.device &&
+ strcasecmp(fdata.pci.device, dev->device))
+ continue;
+
+ g_ptr_array_add(view, dev);
+ }
+
+ /* Leave only n-th element if card is set */
+ if (fdata.pci.card) {
+ int card = -1;
+ sscanf(fdata.pci.card, "%d", &card);
+
+ if (card >= 0 && card < view->len) {
+ struct igt_device *dev = g_ptr_array_index(view, card);
+ g_ptr_array_remove_range(view, 0, view->len);
+ g_ptr_array_add(view, dev);
+ } else {
+ g_ptr_array_remove_range(view, 0, view->len);
+ }
+ }
+ DBG("Filter pci view size: %d\n", view->len);
+
+ g_hash_table_destroy(ht);
+
+ return view;
+}
+
+/*
+ * Refresh view finding first card matching with
+ * vs->match_driver or vs->modname if vs->match_driver is not set
+ */
+static GPtrArray *filter_module(struct filter_spec *fspec,
+ struct filter_func *ffunc)
+{
+ GHashTable *ht;
+ GPtrArray *devs, *view;
+ union filter_data fdata;
+ int card = -1;
+ const char *driver = ffunc->vs->match_driver ?: ffunc->vs->modname;
+
+ DBG("filter module [%s]\n", driver);
+
+ ht = split_filter_data(fspec->data);
+ fdata.module.card = g_hash_table_lookup(ht, "card");
+
+ view = igt_devs.view;
+ devs = igt_devs.devs;
+
+ if (fdata.module.card) {
+ sscanf(fdata.module.card, "%d", &card);
+ if (card < 0) {
+ g_hash_table_destroy(ht);
+ return view;
+ }
+ }
+
+ for (int i = 0; i < devs->len; i++) {
+ const char *modname;
+ struct igt_device *dev = g_ptr_array_index(devs, i);
+
+ modname = get_prop_driver(dev);
+ if (!modname)
+ continue;
+
+ if (!strcmp(modname, driver)) {
+ if (!card) {
+ g_ptr_array_add(view, dev);
+ break;
+ }
+ card--;
+ }
+ }
+
+ DBG("Filter view size: %d\n", view->len);
+
+ g_hash_table_destroy(ht);
+
+ return view;
+}
+
+static const char *find_vendor_id_by_vendor_spec(struct vendor_spec *vs)
+{
+ struct name_value *list = &gpu_vendor_list[0];
+
+ while (list->name) {
+ if ((struct vendor_spec *) list->value == vs)
+ return list->name;
+ list++;
+ }
+ return NULL;
+}
+
+static GPtrArray *filter_compatible(struct filter_spec *fspec,
+ struct filter_func *ffunc)
+{
+ GHashTable *ht;
+ GPtrArray *devs, *view;
+ union filter_data fdata;
+ int card = -1;
+ const char *of_compatible = find_vendor_id_by_vendor_spec(ffunc->vs);
+
+ DBG("filter compatible[%s]\n", of_compatible);
+
+ ht = split_filter_data(fspec->data);
+ fdata.module.card = g_hash_table_lookup(ht, "card");
+
+ view = igt_devs.view;
+ devs = igt_devs.devs;
+
+ if (fdata.module.card) {
+ sscanf(fdata.module.card, "%d", &card);
+ if (card < 0) {
+ g_hash_table_destroy(ht);
+ return view;
+ }
+ }
+
+ for (int i = 0; i < devs->len; i++) {
+ const char *compat;
+ struct igt_device *dev = g_ptr_array_index(devs, i);
+
+ if (!of_compatible)
+ break;
+
+ compat = get_prop_of_compatible_0(dev);
+ if (compat && !strcmp(compat, of_compatible)) {
+ if (!card) {
+ g_ptr_array_add(view, dev);
+ break;
+ }
+ card--;
+ }
+ }
+
+ DBG("Filter view size: %d\n", view->len);
+
+ g_hash_table_destroy(ht);
+
+ return view;
+}
+
+
+static struct filter_func f_drm = { .filter_function = filter_drm,
+ .help = "drm:[card=%d]",
+ .detail = "card is N-card number (from /dev/dri/cardN)\n",
+ };
+
+static struct filter_func f_pci = { .filter_function = filter_pci,
+ .help = "pci:[vendor=%04x/name][,device=%04x][,card=%d]",
+ .detail = "vendor is hex number or name",
+ };
+
+static struct filter_func f_vgem = { .filter_function = filter_module,
+ .help = "vgem:[card=%d]",
+ .detail = "card is n-th vgem card number\n",
+ .vs = &v_vgem,
+ };
+
+static struct filter_func f_vkms = { .filter_function = filter_module,
+ .help = "vkms:[card=%d]",
+ .detail = "card is n-th vkms card number\n",
+ .vs = &v_vkms,
+ };
+
+static struct filter_func f_exynos = { .filter_function = filter_module,
+ .help = "exynos:[card=%d]",
+ .detail = "card is n-th exynos-drm card number\n",
+ .vs = &v_exynos,
+ };
+
+static struct filter_func f_vc4 = { .filter_function = filter_compatible,
+ .help = "vc4:[card=%d]",
+ .detail = "card is n-th vc4 card number\n",
+ .vs = &v_vc4,
+ };
+
+struct name_value filter_definition_list[] = {
+ { "drm", (gpointer) &f_drm },
+ { "pci", (gpointer) &f_pci },
+ { "vgem", (gpointer) &f_vgem },
+ { "vkms", (gpointer) &f_vkms },
+ { "exynos", (gpointer) &f_exynos },
+ { "vc4", (gpointer) &f_vc4 },
+ { NULL, },
+};
+
+void igt_device_print_filter_types(void)
+{
+ GList *keys = g_hash_table_get_keys(filter_definition_ht);
+
+ printf("Filter types:\n---\n");
+ printf("%-8s %s\n---\n", "filter", "syntax");
+ while (keys) {
+ char *k = (char *) keys->data;
+ struct filter_func *v = g_hash_table_lookup(filter_definition_ht, k);
+ printf("%-8s %s\n", k, v->help);
+ printf("%-8s %s\n", "", v->detail);
+ keys = g_list_next(keys);
+ }
+ g_list_free(keys);
+}
+
+static GPtrArray *device_filters = NULL;
+
+#define DEVICE_FILTER_CHECK_ALLOC() \
+ do { \
+ if (!device_filters) \
+ device_filters = g_ptr_array_new_full(2, free); \
+ igt_assert(device_filters); \
+ } while(0)
+
+int igt_device_filter_count(void)
+{
+ DEVICE_FILTER_CHECK_ALLOC();
+
+ return device_filters->len;
+}
+
+int igt_device_filter_add(const char *filter)
+{
+ gchar **strv, **s;
+ int c = 0;
+
+ DEVICE_FILTER_CHECK_ALLOC();
+
+ strv = g_strsplit(filter, ";", -1);
+
+ s = strv;
+ while (*s) {
+ g_ptr_array_add(device_filters, strdup(*s));
+ s++;
+ }
+ g_strfreev(strv);
+
+ return c;
+}
+
+const char *igt_device_filter_get(int num)
+{
+ DEVICE_FILTER_CHECK_ALLOC();
+
+ if (num < 0 || num >= device_filters->len)
+ return NULL;
+
+ return g_ptr_array_index(device_filters, num);
+}
+
+#define dev_is_non_discoverable(vs) \
+ ((vs)->dev_type == DEV_PLATFORM_NON_DISCOVERABLE)
+
+bool igt_device_filter_apply(const char *filter)
+{
+ struct filter_spec fspec;
+ struct filter_func *ffunc;
+ bool ret;
+
+ if (!filter)
+ return false;
+
+ ret = get_filter_spec(filter, &fspec);
+ if (!ret) {
+ igt_warn("Can't split filter [%s]\n", filter);
+ return false;
+ }
+
+ ffunc = g_hash_table_lookup(filter_definition_ht, fspec.name);
+ if (!ffunc) {
+ igt_warn("No filter with name [%s]\n", fspec.name);
+ return false;
+ }
+
+ /* Clean view */
+ g_ptr_array_remove_range(igt_devs.view, 0, igt_devs.view->len);
+
+ /* Load module only for non-discoverable devices.
+ * For discoverables / PCI defer this to card match path
+ */
+ if (ffunc->vs && dev_is_non_discoverable(ffunc->vs) &&
+ !igt_kmod_is_loaded(ffunc->vs->modname)) {
+ if (!igt_kmod_load(ffunc->vs->modname, ffunc->vs->modopts)) {
+ igt_info("Module %s loaded, rescanning devices\n",
+ ffunc->vs->modname);
+ igt_devices_scan(true);
+ } else {
+ return false;
+ }
+ }
+
+ ffunc->filter_function(&fspec, ffunc);
+
+ return true;
+}
+
+bool igt_device_filter_apply_nth(int num)
+{
+ const char *filter = igt_device_filter_get(num);
+ if (!filter)
+ return false;
+
+ return igt_device_filter_apply(filter);
+}
+
+/* For PCI devices load or unbind / bind is called in card matching path.
+ * This is for 'lsgpu' tool which doesn't want to make magic driver
+ * loading job in filter apply path (it really want to display PCI devices
+ * in the state they really are).
+*/
+static bool ensure_device_is_ready(struct igt_device *dev,
+ const char *filter)
+{
+ if (dev->dev_type != DEV_PCI &&
+ dev->dev_type != DEV_PLATFORM_DISCOVERABLE)
+ return false;
+
+ if (dev->drm_card_path || dev->drm_render_path)
+ return true;
+
+ /* So, we have PCI device or discoverable device without
+ * card/render node, lets try at the beginning to simply
+ * load the module */
+ if (!ensure_device_has_driver_loaded(dev))
+ return false; //oops!
+
+ /* Rescan devices, filter again and get the device */
+ igt_devices_scan(true);
+ if (igt_device_filter_apply(filter) == false)
+ return false;
+
+ if (!igt_devs.view->len)
+ return false;
+
+ dev = g_ptr_array_index(igt_devs.view, 0);
+ if (dev->drm_card_path || dev->drm_render_path)
+ return true;
+
+ /* For platform device unbind/bind is not performed */
+ if (dev->dev_type == DEV_PLATFORM_DISCOVERABLE)
+ return false;
+
+ /* In this moment we likely already had driver loaded,
+ * but it wasn't binded to this device. */
+ igt_device_pci_unbind_module(get_prop_pci_slot(dev));
+ igt_device_pci_bind_module(get_prop_pci_slot(dev));
+ igt_devices_scan(true);
+ if (igt_device_filter_apply(filter) == false)
+ return false;
+
+ if (!igt_devs.view->len)
+ return false;
+
+ dev = g_ptr_array_index(igt_devs.view, 0);
+ if (dev->drm_card_path || dev->drm_render_path)
+ return true;
+
+ return false;
+}
+
+#define safe_strncpy(dst, src, size) \
+ if (src) strncpy((dst), (src), (size))
+/*
+ * Returns:
+ * false - no card pointer was passed or card wasn't matched,
+ * true - card matched and returned.
+ */
+bool igt_device_card_match(const char *filter, struct igt_device_card *card)
+{
+ struct igt_device *dev = NULL;
+
+ if (!card)
+ return false;
+ memset(card, 0, sizeof(*card));
+
+ igt_devices_scan(false);
+
+ if (igt_device_filter_apply(filter) == false)
+ return false;
+
+ if (!igt_devs.view->len)
+ return false;
+
+ dev = g_ptr_array_index(igt_devs.view, 0);
+ if (dev->dev_type == DEV_PCI ||
+ dev->dev_type == DEV_PLATFORM_DISCOVERABLE) {
+ if (!ensure_device_is_ready(dev, filter))
+ return false;
+ }
+
+ if (!igt_devs.view->len) //additional check because rescan could happen
+ return false;
+
+ /* We take first one if more than one card matches filter */
+ dev = g_ptr_array_index(igt_devs.view, 0);
+ card->dev_type = dev->dev_type;
+ card->chipset = dev->vs->chipset;
+ if (dev->dev_type == DEV_PCI) {
+ safe_strncpy(card->module, dev->vs->modname, NAME_MAX);
+ safe_strncpy(card->pci_slot, get_prop_pci_slot(dev),
+ PCI_SLOT_LENGTH);
+ } else {
+ safe_strncpy(card->module, get_prop_driver(dev), NAME_MAX);
+ }
+ safe_strncpy(card->card, dev->drm_card_path, NAME_MAX);
+ safe_strncpy(card->render, dev->drm_render_path, NAME_MAX);
+
+ return true;
+}
diff --git a/lib/igt_device_scan.h b/lib/igt_device_scan.h
new file mode 100644
index 00000000..0e2adf13
--- /dev/null
+++ b/lib/igt_device_scan.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __IGT_DEVICE_SCAN_H__
+#define __IGT_DEVICE_SCAN_H__
+
+#include <limits.h>
+#include <igt.h>
+
+enum igt_devices_print_type {
+ IGT_PRINT_SIMPLE,
+ IGT_PRINT_DETAIL,
+};
+
+enum igt_device_type {
+ DEV_PCI,
+ DEV_PLATFORM_NON_DISCOVERABLE,
+ DEV_PLATFORM_DISCOVERABLE,
+};
+
+#define PCI_SLOT_LENGTH 13
+struct igt_device_card {
+ enum igt_device_type dev_type;
+ unsigned int chipset; //contains DRIVER_XXX value
+ char module[NAME_MAX];
+ char card[NAME_MAX];
+ char render[NAME_MAX];
+ char pci_slot[PCI_SLOT_LENGTH];
+};
+
+void igt_devices_scan(bool force);
+void igt_devices_print(enum igt_devices_print_type printtype,
+ bool show_props, bool show_attrs);
+
+void igt_devices_print_vendors(void);
+
+bool igt_device_pci_unbind_module(const char *pci_slot);
+bool igt_device_pci_bind_module(const char *pci_slot);
+void igt_devices_bind_modules(void);
+
+void igt_device_print_filter_types(void);
+
+int igt_device_filter_count(void);
+int igt_device_filter_add(const char *filter);
+const char *igt_device_filter_get(int num);
+bool igt_device_filter_apply(const char *filter);
+bool igt_device_filter_apply_nth(int num);
+
+bool igt_device_card_match(const char *filter, struct igt_device_card *card);
+
+#endif /* __IGT_DEVICE_SCAN_H__ */
diff --git a/lib/meson.build b/lib/meson.build
index 157624e7..826ebbe3 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -10,6 +10,7 @@ lib_sources = [
'igt_color_encoding.c',
'igt_debugfs.c',
'igt_device.c',
+ 'igt_device_scan.c',
'igt_aux.c',
'igt_gpu_power.c',
'igt_gt.c',
diff --git a/tools/Makefile.sources b/tools/Makefile.sources
index 50706f41..0e67b654 100644
--- a/tools/Makefile.sources
+++ b/tools/Makefile.sources
@@ -33,6 +33,7 @@ tools_prog_lists = \
intel_watermark \
intel_gem_info \
intel_gvtg_test \
+ lsgpu \
$(NULL)

dist_bin_SCRIPTS = intel_gpu_abrt
diff --git a/tools/lsgpu.c b/tools/lsgpu.c
new file mode 100644
index 00000000..b784ca65
--- /dev/null
+++ b/tools/lsgpu.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright © 2019 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include "igt_device_scan.h"
+#include "igt.h"
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+
+enum {
+ OPT_SHOW_PROPS = 'r',
+ OPT_SHOW_ATTRS = 'a',
+ OPT_PRINT_SIMPLE = 'i',
+ OPT_PRINT_DETAIL = 'd',
+ OPT_LIST_VENDORS = 'v',
+ OPT_BIND_MODULES = 'B',
+ OPT_UNBIND_MODULE = 'u',
+ OPT_BIND_MODULE = 'b',
+ OPT_LIST_FILTERS = 'l',
+ OPT_SET_FILTER = 's',
+ OPT_MATCH_DEVICE = 'm',
+ OPT_DEVICE = 'D',
+ OPT_HELP = 'h'
+};
+
+bool g_show_props;
+bool g_show_attrs;
+bool g_show_vendors;
+bool g_bind_modules;
+char *g_pci_id;
+bool g_unbind_module;
+bool g_bind_module;
+bool g_list_filters;
+char *g_filter;
+char *g_match_filter;
+bool g_device;
+bool g_help;
+
+static const char *usage_str =
+ "usage: lsgpu [options]\n\n"
+ "Options:\n"
+ " -r, --show-props Show device properties\n"
+ " -a, --show-attrs Show device attributes\n"
+ " -i, --print-simple Print devices as simple list (default)\n"
+ " -d, --print-details Print devices with details\n"
+ " -v, --list-vendors List recognized vendors\n"
+ " -B, --bind-modules Bind modules to unbound PCI cards\n"
+ " -u, --unbind-module pci_id Unbind module from pci id device\n"
+ " -b, --bind-module pci_id Bind module to pci id device\n"
+ " -l, --list-filter-types List registered device filters types\n"
+ " -s, --set-filter filter Set filter for processing devices\n"
+ " -m, --match-device filter Find device matching to filter\n"
+ " -D, --device filter Device filter, can be given multiple times\n"
+ " -h, --help Show this help message and exit\n";
+
+static void test_device_open(struct igt_device_card *card)
+{
+ int fd;
+
+ if (!card)
+ return;
+
+ fd = __drm_open_card(card);
+ if (fd >= 0) {
+ printf("Device %s successfully opened\n", card->card);
+ close(fd);
+ } else {
+ if (strlen(card->card))
+ printf("Cannot open card %s device\n", card->card);
+ else
+ printf("Cannot open card device, empty name\n");
+ }
+
+ fd = __drm_open_render(card);
+ if (fd >= 0) {
+ printf("Device %s successfully opened\n", card->render);
+ close(fd);
+ } else {
+ if (strlen(card->render))
+ printf("Cannot open render %s device\n", card->render);
+ else
+ printf("Cannot open render device, empty name\n");
+ }
+}
+
+static void print_card(struct igt_device_card *card)
+{
+ if (!card)
+ return;
+
+ switch (card->dev_type) {
+ case DEV_PCI:
+ printf("dev type : %s\n", "PCI");
+ printf("PCI slot : %s\n", card->pci_slot);
+ break;
+ case DEV_PLATFORM_NON_DISCOVERABLE:
+ printf("dev type : %s\n", "PLATFORM (NON-DISCOVERABLE)");
+ break;
+ case DEV_PLATFORM_DISCOVERABLE:
+ printf("dev type : %s\n", "PLATFORM (DISCOVERABLE)");
+ break;
+ }
+ printf("drv chipset : %x\n", card->chipset);
+ printf("drm card : %s\n", card->card);
+ printf("drm render : %s\n", card->render);
+ printf("drv module : %s\n", card->module);
+}
+
+int main(int argc, char *argv[])
+{
+ static struct option long_options[] = {
+ {"show-props", no_argument, NULL, OPT_SHOW_PROPS},
+ {"show-attrs", no_argument, NULL, OPT_SHOW_ATTRS},
+ {"print-simple", no_argument, NULL, OPT_PRINT_SIMPLE},
+ {"print-detail", no_argument, NULL, OPT_PRINT_DETAIL},
+ {"list-vendors", no_argument, NULL, OPT_LIST_VENDORS},
+ {"bind-modules", no_argument, NULL, OPT_BIND_MODULES},
+ {"unbind-module", required_argument, NULL, OPT_UNBIND_MODULE},
+ {"bind-module", required_argument, NULL, OPT_BIND_MODULE},
+ {"list-filter-types", no_argument, NULL, OPT_LIST_FILTERS},
+ {"set-filter", required_argument, NULL, OPT_SET_FILTER},
+ {"match-device", required_argument, NULL, OPT_MATCH_DEVICE},
+ {"device", required_argument, NULL, OPT_DEVICE},
+ {"help", no_argument, NULL, OPT_HELP},
+ {0, 0, 0, 0}
+ };
+ int c, index = 0;
+ const char *env;
+ enum igt_devices_print_type printtype = IGT_PRINT_SIMPLE;
+
+ while ((c = getopt_long(argc, argv, "PpraidvBu:b:ls:m:D:h",
+ long_options, &index)) != -1) {
+ switch(c) {
+ case OPT_SHOW_PROPS:
+ g_show_props = true;
+ break;
+ case OPT_SHOW_ATTRS:
+ g_show_attrs = true;
+ break;
+ case OPT_PRINT_SIMPLE:
+ printtype = IGT_PRINT_SIMPLE;
+ break;
+ case OPT_PRINT_DETAIL:
+ printtype = IGT_PRINT_DETAIL;
+ break;
+ case OPT_LIST_VENDORS:
+ g_show_vendors = true;
+ break;
+ case OPT_BIND_MODULES:
+ g_bind_modules = true;
+ break;
+ case OPT_UNBIND_MODULE:
+ g_pci_id = strdup(optarg);
+ g_unbind_module = true;
+ break;
+ case OPT_BIND_MODULE:
+ g_pci_id = strdup(optarg);
+ g_bind_module = true;
+ break;
+ case OPT_SET_FILTER:
+ g_filter = strdup(optarg);
+ break;
+ case OPT_LIST_FILTERS:
+ g_list_filters = true;
+ break;
+ case OPT_MATCH_DEVICE:
+ g_match_filter = strdup(optarg);
+ break;
+ case OPT_DEVICE:
+ g_device = true;
+ igt_device_filter_add(optarg);
+ break;
+ case OPT_HELP:
+ g_help = true;
+ break;
+ }
+ }
+
+ if (g_help) {
+ printf("%s\n", usage_str);
+ exit(0);
+ }
+
+ env = getenv("IGT_DEVICE");
+ if (env) {
+ igt_device_filter_add(env);
+ g_device = true;
+ }
+
+ igt_devices_scan(false);
+ if (g_show_vendors) {
+ igt_devices_print_vendors();
+ return 0;
+ }
+ if (g_bind_modules) {
+ igt_devices_bind_modules();
+ return 0;
+ }
+
+ if (g_unbind_module) {
+ igt_device_pci_unbind_module(g_pci_id);
+ return 0;
+ }
+
+ if (g_bind_module) {
+ igt_device_pci_bind_module(g_pci_id);
+ return 0;
+ }
+
+ if (g_list_filters) {
+ igt_device_print_filter_types();
+ return 0;
+ }
+
+ if (g_filter) {
+ igt_device_filter_apply(g_filter);
+ }
+
+ if (g_match_filter) {
+ struct igt_device_card card;
+ if (igt_device_card_match(g_match_filter, &card)) {
+ print_card(&card);
+ } else {
+ printf("No device matching filter [%s] found.\n",
+ g_match_filter);
+ return 0;
+ }
+ test_device_open(&card);
+
+ return 0;
+ }
+
+ if (g_device) {
+ int n = igt_device_filter_count();
+ printf("=== Device filter list ===\n");
+ for (int i = 0; i < n; i++) {
+ printf("[%2d]: %s\n", i,
+ igt_device_filter_get(i));
+ igt_device_filter_apply_nth(i);
+ igt_devices_print(printtype, g_show_props, g_show_attrs);
+ }
+ printf("\n");
+
+ printf("=== Testing device open ===\n");
+ for (int i = 0; i < n; i++) {
+ struct igt_device_card card;
+ const char *filter = igt_device_filter_get(i);
+
+ if (!igt_device_card_match(filter, &card))
+ continue;
+ print_card(&card);
+ test_device_open(&card);
+ printf("---\n");
+ }
+
+ return 0;
+ }
+
+ igt_devices_print(printtype, g_show_props, g_show_attrs);
+
+ return 0;
+}
diff --git a/tools/meson.build b/tools/meson.build
index 6e72b263..9b3a2a69 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -36,6 +36,7 @@ tools_progs = [
'intel_gem_info',
'intel_gvtg_test',
'dpcd_reg',
+ 'lsgpu',
]
tool_deps = igt_deps

--
2.21.0