[igt-dev] [PATCH i-g-t 3/5] tests/vkms: Add a small library for VKMS testing

Marius Vlad marius.vlad at collabora.com
Fri Sep 1 09:28:18 UTC 2023


From: Jim Shargo <jshargo at chromium.org>

And with it, this adds a basic test that makes use of it,
creating a device and with it a pipeline.

One should create a vkms device, add pipeline elements, like encoders,
crtc, and connectors, compose the pipeline by attaching each element
using the _attach_ functions, set the connector as connected and
finally, enable the vkms device.

This works in-tandem with the vkms driver which requires to enable the
device and set the connector as connected.

Signed-off-by: Jim Shargo <jshargo at chromium.org>
Signed-off-by: Marius Vlad <marius.vlad at collabora.com>
---
 lib/igt.h                  |   1 +
 lib/igt_vkms.c             | 532 +++++++++++++++++++++++++++++++++++++
 lib/igt_vkms.h             |  66 +++++
 lib/meson.build            |   1 +
 tests/meson.build          |  13 +
 tests/vkms/vkms_configfs.c |  83 ++++++
 6 files changed, 696 insertions(+)
 create mode 100644 lib/igt_vkms.c
 create mode 100644 lib/igt_vkms.h
 create mode 100644 tests/vkms/vkms_configfs.c

diff --git a/lib/igt.h b/lib/igt.h
index 6108d9615..d3771c1f1 100644
--- a/lib/igt.h
+++ b/lib/igt.h
@@ -41,6 +41,7 @@
 #include "igt_pm.h"
 #include "igt_stats.h"
 #include "igt_dsc.h"
+#include "igt_vkms.h"
 #ifdef HAVE_CHAMELIUM
 #include "igt_alsa.h"
 #include "igt_audio.h"
diff --git a/lib/igt_vkms.c b/lib/igt_vkms.c
new file mode 100644
index 000000000..7ae8dde55
--- /dev/null
+++ b/lib/igt_vkms.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Copyright 2023 Collabora, Ltd.
+ *
+ * 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_vkms.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "drmtest.h"
+#include "igt_configfs.h"
+#include "igt_core.h"
+
+/**
+ * SECTION:igt_vkms
+ * @short_description: Library with creating and dynamically configure a vkms device
+ * @title: vkms
+ * @include: igt_vkms.h
+ *
+ * This library contains helpers for creating a vkms device and configuring it
+ * dynamically.
+ *
+ * One should create a vkms device, add pipeline elements, like encoders, crtc,
+ * and connectors, compose the pipeline by attaching each element using the
+ * _attach_ functions, set the connector as connected and finally, enable the
+ * vkms device.
+ */
+
+
+static void create_device_from_directory(igt_vkms_t *device, const char *name)
+{
+	const char *vkms_root = igt_configfs_vkms_mount();
+	memset(device, 0, sizeof(*device));
+
+	snprintf(device->name, sizeof(device->name), "%s", name);
+	snprintf(device->device_dir, sizeof(device->device_dir), "%s/%s", vkms_root, name);
+}
+
+/**
+ * igt_vkms_destroy_all_devices:
+ *
+ * Iterates over all directories found in the ConfigFS mountpoint and calls
+ * igt_vkms_device_destroy, which cleans up any previous directories,
+ * symlinks and objects.
+ */
+void igt_vkms_destroy_all_devices(void)
+{
+	const char *vkms_root = igt_configfs_vkms_mount();
+	igt_vkms_t device = { 0 };
+	DIR *dir;
+	struct dirent *ent;
+	int ret = 0;
+
+	if (!vkms_root) {
+		igt_warn("Unable to find VKMS configfs directory.\n");
+		return;
+	}
+
+	dir = opendir(vkms_root);
+	if (!dir) {
+		igt_warn("Unable to open VKMS configfs directory '%s'. "
+			 "Got errno=%d (%s)\n", vkms_root, errno, strerror(errno));
+		return;
+	}
+
+	while ((ent = readdir(dir)) != NULL) {
+		if (strcmp(ent->d_name, ".") == 0 ||
+		    strcmp(ent->d_name, "..") == 0)
+			continue;
+
+		create_device_from_directory(&device, ent->d_name);
+		igt_vkms_device_destroy(&device);
+		if (ret)
+			igt_warn("Unable to reset device '%s/%s'\n", vkms_root,
+				 ent->d_name);
+	}
+
+	closedir(dir);
+}
+
+/**
+ * igt_vkms_device_create:
+ * @device: a statically allocated object that denotes a vkms device
+ * @name: a string that signifies the vkms device to be created
+ *
+ * This creates a directory in the ConfigFS root mountpoint directory, where
+ * the entire pipeline will be configured.
+ */
+void igt_vkms_device_create(igt_vkms_t *device, const char *name)
+{
+	const char *vkms_root = igt_configfs_vkms_mount();
+	DIR *dir;
+	int ret;
+
+	igt_assert_f(vkms_root, "Unable to find VKMS root '%s'.\n", name);
+
+	dir = opendir(vkms_root);
+	igt_assert_f(dir, "VKMS configfs directory not available at '%s'. "
+		     "Got errno=%d (%s)\n", vkms_root, errno, strerror(errno));
+	if (dir)
+		closedir(dir);
+
+	create_device_from_directory(device, name);
+
+	igt_debug("mkdir'ing VKMS device at '%s'\n", device->device_dir);
+
+	ret = mkdir(device->device_dir, 0777);
+	igt_assert_f(ret == 0,
+		     "Unable to mkdir device directory '%s'. Got errno=%d (%s)\n",
+		     device->device_dir, errno, strerror(errno));
+}
+
+static int igt_vkms_unlink_symlinks(const char *fpath, const struct stat *sb,
+				      int typeflag, struct FTW *ftwbuf)
+{
+	if (typeflag != FTW_SL)
+		return 0;
+
+	if (unlink(fpath)) {
+		igt_warn("Unable to unlink vkms object: '%s'. "
+	                 "Got errno=%d (%s)\n", fpath, errno, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+static int igt_vkms_delete_objects(const char *fpath, const struct stat *sb,
+				     int typeflag, struct FTW *ftwbuf)
+{
+	char *dirbase;
+	char path_buf[VKMS_CARD_OBJECT_DIR_SIZE];
+
+	memset(path_buf, 0x0, sizeof(path_buf));
+	snprintf(path_buf, sizeof(path_buf), "%s", fpath);
+	dirbase = basename(dirname(path_buf));
+
+	if (strcmp(dirbase, "planes") != 0 &&
+	    strcmp(dirbase, "encoders") != 0 &&
+	    strcmp(dirbase, "connectors") != 0 &&
+	    strcmp(dirbase, "crtcs") != 0) {
+		return 0;
+	}
+
+	if (rmdir(fpath)) {
+		igt_warn("Unable to rmdir vkms object: '%s'. Got errno=%d (%s)\n",
+			 fpath, errno, strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * igt_vkms_device_destroy:
+ * @device: the vkms device in question
+ *
+ * Removes any possible symlinks, objects and directories, and clears the
+ * device for later usage.
+ *
+ * Users can then create a new vkms device with igt_vkms_device_create().
+ */
+void igt_vkms_device_destroy(igt_vkms_t *device)
+{
+	int ret = 0;
+
+	igt_debug("Destroying device %s\n", device->device_dir);
+
+	/* Some notes on device destruction:
+	 *   - FTW_PHYS keeps us from following symlinks
+	 *   - FTW_DEPTH does a DFS of the tree, which lets us delete
+	 *   bottom-up.
+	 */
+	ret = nftw(device->device_dir, &igt_vkms_unlink_symlinks,
+		   /* nopenfd= */ 16, FTW_PHYS | FTW_DEPTH);
+	igt_assert_f(ret == 0,
+		     "Unable to remove symlinks for device directory '%s'\n",
+		     device->device_dir);
+
+	ret = nftw(device->device_dir, &igt_vkms_delete_objects,
+		   /* nopenfd= */ 16, FTW_PHYS | FTW_DEPTH);
+	igt_assert_f(ret == 0,
+		     "Unable to remove objects for device directory '%s'\n",
+		     device->device_dir);
+
+	ret = rmdir(device->device_dir);
+	igt_assert_f(ret == 0,
+		     "Unable to rmdir device directory '%s'. Got errno=%d (%s)\n",
+		     device->device_dir, errno, strerror(errno));
+
+	memset(device, 0, sizeof(*device));
+}
+
+/**
+ * igt_vkms_device_add_plane:
+ * @device: the device in question
+ * @name: the name of the plane, as a string
+ * @type: the type of the HW plane, as an integer, one of DRM_PLANE_TYPE_PRIMARY,
+ * DRM_PLANE_TYPE_OVERLAY or DRM_PLANE_TYPE_CURSOR.
+ *
+ * mkdirs and creates a plane using the provided name and the type.
+ */
+void igt_vkms_device_add_plane(igt_vkms_t *device, const char *name, int type)
+{
+	char path[VKMS_CARD_OBJECT_DIR_SIZE];
+	char writebuf[2];
+	int fd, ret;
+
+	memset(path, 0x0, sizeof(path));
+	snprintf(path, sizeof(path), "%s/planes/%s", device->device_dir, name);
+
+	ret = mkdir(path, 0777);
+	igt_assert_f(ret == 0,
+		     "Failed to mkdir VKMS plane '%s'. Got errno=%d (%s)\n",
+		     path, errno, strerror(errno));
+
+	strcat(path, "/type");
+	fd = open(path, O_WRONLY);
+	igt_assert_f(fd > 0,
+		     "Failed to open plane type for writing '%s'. Got errno=%d (%s)\n",
+		     path, errno, strerror(errno));
+
+	snprintf(writebuf, 2, "%d", type);
+	write(fd, writebuf, 1);
+	close(fd);
+}
+
+/**
+ * igt_vkms_device_add_connector:
+ * @device: the device in question
+ * @name: the name of the connector, as a string
+ *
+ * mkdirs and creates a connector using the provided name.
+ *
+ */
+void igt_vkms_device_add_connector(igt_vkms_t *device, const char *name)
+{
+	char path[VKMS_CARD_OBJECT_DIR_SIZE];
+	int ret;
+
+	memset(path, 0x0, sizeof(path));
+	snprintf(path, sizeof(path), "%s/connectors/%s", device->device_dir, name);
+
+	ret = mkdir(path, 0777);
+	igt_assert_f(ret == 0,
+		     "Failed to mkdir VKMS plane '%s'. Got errno=%d (%s)\n",
+		     path, errno, strerror(errno));
+}
+
+/**
+ * igt_vkms_device_add_encoder:
+ * @device: the device in question
+ * @name: the name of the encoder, as a string
+ *
+ * mkdirs and creates an encoder using the provided name.
+ */
+void igt_vkms_device_add_encoder(igt_vkms_t *device, const char *name)
+{
+	char path[VKMS_CARD_OBJECT_DIR_SIZE];
+	int ret;
+
+	memset(path, 0x0, sizeof(path));
+	snprintf(path, sizeof(path), "%s/encoders/%s", device->device_dir, name);
+
+	ret = mkdir(path, 0777);
+	igt_assert_f(ret == 0,
+		     "Failed to mkdir VKMS encoder '%s'. Got errno=%d (%s)\n",
+		     path, errno, strerror(errno));
+}
+
+/**
+ * igt_vkms_device_add_crtc:
+ * @device: the device in question
+ * @name: the name of the crtc, as a string
+ *
+ * mkdirs and creates a crtc using the provided name.
+ */
+void igt_vkms_device_add_crtc(igt_vkms_t *device, const char *name)
+{
+	char path[VKMS_CARD_OBJECT_DIR_SIZE];
+	int ret;
+
+	memset(path, 0x0, sizeof(path));
+	snprintf(path, sizeof(path), "%s/crtcs/%s", device->device_dir, name);
+
+	ret = mkdir(path, 0777);
+	igt_assert_f(ret == 0,
+		     "Failed to mkdir VKMS crtc '%s'. Got errno=%d (%s)\n",
+		     path, errno, strerror(errno));
+}
+
+/**
+ * igt_vkms_device_attach_plane_to_crtc:
+ * @device: the vkms device in question
+ * @plane_name: the plane name, as a string
+ * @crtc_name: the crtc name, as a string
+ *
+ * This function symlinks the crtc, as pointed by crtc_name to the planes
+ * possible_crtcs directory.
+ */
+void igt_vkms_device_attach_plane_to_crtc(igt_vkms_t *device, const char *plane_name,
+					  const char *crtc_name)
+{
+	char plane_crtcs_path[VKMS_CARD_OBJECT_DIR_SIZE];
+	char crtc_path[VKMS_CARD_OBJECT_DIR_SIZE];
+	int ret;
+
+	memset(crtc_path, 0x0, sizeof(crtc_path));
+	memset(plane_crtcs_path, 0x0, sizeof(plane_crtcs_path));
+
+	snprintf(plane_crtcs_path, sizeof(plane_crtcs_path),
+		 "%s/planes/%s/possible_crtcs/%s",
+		 device->device_dir, plane_name, crtc_name);
+	snprintf(crtc_path, sizeof(crtc_path), "%s/crtcs/%s",
+		 device->device_dir, crtc_name);
+
+	ret = symlink(crtc_path, plane_crtcs_path);
+	igt_assert_f(ret == 0,
+		"Failed to symlink VKMS crtc '%s' to plane '%s'. Got errno=%d (%s)\n",
+		crtc_name, plane_name, errno, strerror(errno));
+}
+
+/**
+ * igt_vkms_device_attach_crtc_to_encoder:
+ * @device: the vkms device in question
+ * @encoder_name: the encoder name, as a string
+ * @crtc_name: the crtc name, as a string
+ *
+ * This function symlinks the crtc, as pointed by crtc_name to the encoders
+ * possible_crtcs directory.
+ */
+void igt_vkms_device_attach_crtc_to_encoder(igt_vkms_t *device, const char *encoder_name,
+					    const char *crtc_name)
+{
+	char encoder_crtcs_path[VKMS_CARD_OBJECT_DIR_SIZE];
+	char crtc_path[VKMS_CARD_OBJECT_DIR_SIZE];
+	int ret;
+
+	memset(crtc_path, 0x0, sizeof(crtc_path));
+	memset(encoder_crtcs_path, 0x0, sizeof(encoder_crtcs_path));
+
+	snprintf(encoder_crtcs_path, sizeof(encoder_crtcs_path),
+		 "%s/encoders/%s/possible_crtcs/%s", device->device_dir,
+		 encoder_name, crtc_name);
+	snprintf(crtc_path, sizeof(crtc_path), "%s/crtcs/%s",
+		 device->device_dir, crtc_name);
+
+	ret = symlink(crtc_path, encoder_crtcs_path);
+	igt_assert_f(ret == 0,
+		"Failed to symlink VKMS crtc '%s' to encoder '%s'. Got errno=%d (%s)\n",
+		crtc_name, encoder_name, errno, strerror(errno));
+}
+
+/**
+ * igt_vkms_device_attach_encoder_to_connector:
+ * @device: the vkms device in question
+ * @connector_name: the connector name, as a string
+ * @encoder_name: the encoder name, as a string
+ *
+ * This function symlinks the encoder, pointed by encoder_name to the
+ * connectors possible_encoders directory.
+ */
+void igt_vkms_device_attach_encoder_to_connector(igt_vkms_t *device,
+						 const char *connector_name,
+						 const char *encoder_name)
+{
+	char connector_encoders_path[VKMS_CARD_OBJECT_DIR_SIZE];
+	char encoder_path[VKMS_CARD_OBJECT_DIR_SIZE];
+	int ret;
+
+	memset(encoder_path, 0x0, sizeof(encoder_path));
+	memset(connector_encoders_path, 0x0, sizeof(connector_encoders_path));
+
+	snprintf(connector_encoders_path, sizeof(connector_encoders_path),
+		 "%s/connectors/%s/possible_encoders/%s", device->device_dir,
+		 connector_name, encoder_name);
+	snprintf(encoder_path, sizeof(encoder_path), "%s/encoders/%s",
+		 device->device_dir, encoder_name);
+
+	ret = symlink(encoder_path, connector_encoders_path);
+	igt_assert_f(ret == 0,
+		    "Failed to symlink VKMS encoder '%s' to connector '%s'. "
+		     "Got errno=%d (%s)\n", encoder_name, connector_name,
+		     errno, strerror(errno));
+}
+
+/**
+ * igt_vkms_enable:
+ * @device: the vkms device in question
+ *
+ * This would enable the vkms device after setting up the pipeline. Use this
+ * function after you've set up the entire pipeline to probe and add the vkms
+ * device.
+ *
+ */
+void igt_vkms_enable(igt_vkms_t *device)
+{
+	char enabled_file[VKMS_CARD_OBJECT_DIR_SIZE];
+	int fd, ret;
+
+	memset(enabled_file, 0x0, sizeof(enabled_file));
+	snprintf(enabled_file, sizeof(enabled_file),
+		 "%s/enabled", device->device_dir);
+
+	fd = open(enabled_file, O_WRONLY);
+	igt_assert_f(fd > 0, "Unable to open '%s'\n", enabled_file);
+
+	ret = write(fd, "1", 1);
+	igt_assert_f(ret >= 0, "Unable to write '%s'. Got errno=%d (%s)\n",
+		     enabled_file, errno, strerror(errno));
+
+	ret = close(fd);
+	igt_assert_eq(ret, 0);
+}
+
+/**
+ * igt_vkms_connector_connect:
+ * @device: the vkms device in question
+ * @connector_name: the connector name, as a string
+ *
+ * By default the connector will be created as disconnected so you'd need to
+ * mark the connector as connected. This will generate connected hot-plug
+ * event.
+ *
+ * You can either call this before enabling the vkms device, or after.
+ */
+void igt_vkms_connector_connect(igt_vkms_t *device, const char *connector_name)
+{
+	char connector_path[VKMS_CARD_OBJECT_DIR_SIZE];
+	int fd, ret;
+
+	memset(connector_path, 0x0, sizeof(connector_path));
+	snprintf(connector_path, sizeof(connector_path),
+		 "%s/connectors/%s/connected", device->device_dir, connector_name);
+
+	fd = open(connector_path, O_WRONLY);
+	igt_assert_f(fd > 0, "Unable to open '%s'\n", connector_path);
+
+	ret = write(fd, "1", 1);
+	igt_assert_f(ret >= 0, "Unable to write '%s'. Got errno=%d (%s)\n",
+		     connector_path, errno, strerror(errno));
+
+	ret = close(fd);
+	igt_assert_eq(ret, 0);
+}
+
+/**
+ * igt_vkms_connector_disconnect:
+ * @device: the vkms device in question
+ * @connector_name: the connector name as a string
+ *
+ * Mark the connector, pointed by @connector_name as disconnected.
+ * This will generate a disconnected hot-plug event.
+ */
+void igt_vkms_connector_disconnect(igt_vkms_t *device, const char *connector_name)
+{
+	char connector_path[VKMS_CARD_OBJECT_DIR_SIZE];
+	int fd, ret;
+
+	memset(connector_path, 0x0, sizeof(connector_path));
+	snprintf(connector_path, sizeof(connector_path),
+		 "%s/connectors/%s/connected", device->device_dir, connector_name);
+
+	fd = open(connector_path, O_WRONLY);
+	igt_assert_f(fd > 0, "Unable to open '%s'\n", connector_path);
+
+	ret = write(fd, "1", 1);
+	igt_assert_f(ret >= 0, "Unable to write '%s'. Got errno=%d (%s)\n",
+		     connector_path, errno, strerror(errno));
+
+	ret = close(fd);
+	igt_assert_eq(ret, 0);
+}
+
+/**
+ * igt_vkms_is_enabled:
+ * @device:
+ *
+ * This checks out if the device has been indeed enabled by checking that
+ * underlying file has the value of '1'.
+ *
+ * Returns true if the device has been enabled, or false otherwise.
+ */
+bool igt_vkms_is_enabled(igt_vkms_t *device)
+{
+	char registration_file[VKMS_CARD_OBJECT_DIR_SIZE];
+	char is_enabled[2];
+	int fd, ret = 0;
+
+	memset(registration_file, 0x0, sizeof(registration_file));
+	snprintf(registration_file, sizeof(registration_file),
+		 "%s/enabled", device->device_dir);
+
+	fd = open(registration_file, O_RDONLY);
+	igt_assert_f(fd > 0, "Unable to open '%s'\n",
+		     registration_file);
+
+	ret = read(fd, is_enabled, sizeof(is_enabled));
+	igt_assert_eq(0, close(fd));
+	if (ret < 0) {
+		return false;
+	}
+
+	return strncmp("1", is_enabled, sizeof(char)) == 0;
+}
diff --git a/lib/igt_vkms.h b/lib/igt_vkms.h
new file mode 100644
index 000000000..174039ca0
--- /dev/null
+++ b/lib/igt_vkms.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Copyright 2023 Collabora, Ltd.
+ *
+ * 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_VKMS_H__
+#define __IGT_VKMS_H__
+
+#include <stdbool.h>
+#include <limits.h>
+
+#define VKMS_CARD_NAME_SIZE		(PATH_MAX / 4)
+#define VKMS_CARD_DIR_SIZE 		(PATH_MAX / 2)
+#define VKMS_CARD_OBJECT_DIR_SIZE	PATH_MAX
+
+
+void igt_vkms_destroy_all_devices(void);
+
+typedef struct igt_vkms {
+	char name[VKMS_CARD_NAME_SIZE];
+	char device_dir[VKMS_CARD_DIR_SIZE];
+} igt_vkms_t;
+
+void igt_vkms_device_create(igt_vkms_t *device, const char *name);
+void igt_vkms_device_destroy(igt_vkms_t *device);
+
+void igt_vkms_device_add_plane(igt_vkms_t *device, const char *name, int type);
+void igt_vkms_device_add_connector(igt_vkms_t *device, const char *name);
+void igt_vkms_device_add_encoder(igt_vkms_t *device, const char *name);
+void igt_vkms_device_add_crtc(igt_vkms_t *device, const char *name);
+
+void igt_vkms_device_attach_plane_to_crtc(igt_vkms_t *device, const char *plane_name,
+					  const char *crtc_name);
+void igt_vkms_device_attach_crtc_to_encoder(igt_vkms_t *device,
+					    const char *encoder_name,
+					    const char *crtc_name);
+void igt_vkms_device_attach_encoder_to_connector(igt_vkms_t *device,
+						 const char *connector_name,
+						 const char *encoder_name);
+
+void igt_vkms_enable(igt_vkms_t *device);
+void igt_vkms_connector_connect(igt_vkms_t *device, const char *connector_name);
+void igt_vkms_connector_disconnect(igt_vkms_t *device, const char *connector_name);
+bool igt_vkms_is_enabled(igt_vkms_t *device);
+
+#endif /* __IGT_VKMS_H__ */
diff --git a/lib/meson.build b/lib/meson.build
index d727529f0..600bbcc0c 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -20,6 +20,7 @@ lib_sources = [
 	'igt_color_encoding.c',
 	'igt_crc.c',
 	'igt_configfs.c',
+	'igt_vkms.c',
 	'igt_debugfs.c',
 	'igt_device.c',
 	'igt_device_scan.c',
diff --git a/tests/meson.build b/tests/meson.build
index c683e468d..db1545fe0 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -316,6 +316,10 @@ chamelium_progs = [
 	'kms_chamelium_hpd',
 ]
 
+vkms_progs = [
+  'vkms_configfs',
+]
+
 test_deps = [ igt_deps ]
 
 if libdrm_nouveau.found()
@@ -459,6 +463,15 @@ if chamelium.found()
 		     output : name + '.testlist')
 endif
 
+foreach prog : vkms_progs
+	test_executables += executable(prog, join_paths('vkms', prog + '.c'),
+				       dependencies : test_deps,
+				       install_dir : libexecdir,
+				       install_rpath : libexecdir_rpathdir,
+				       install : true)
+	test_list += prog
+endforeach
+
 subdir('amdgpu')
 
 subdir('v3d')
diff --git a/tests/vkms/vkms_configfs.c b/tests/vkms/vkms_configfs.c
new file mode 100644
index 000000000..974753318
--- /dev/null
+++ b/tests/vkms/vkms_configfs.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2023 Google LLC.
+ * Copyright 2023 Collabora, Ltd.
+ *
+ * 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 <string.h>
+#include <xf86drmMode.h>
+
+#include "drmtest.h"
+#include "igt.h"
+#include "igt_configfs.h"
+#include "igt_core.h"
+#include "igt_vkms.h"
+
+IGT_TEST_DESCRIPTION("Basic tests for VKMS, including configfs support");
+
+static void vkms_basic_test(igt_vkms_t *device)
+{
+	igt_warn("Path: %s\n", device->device_dir);
+
+	igt_vkms_device_add_plane(device, "primary", DRM_PLANE_TYPE_PRIMARY);
+	igt_vkms_device_add_crtc(device, "crtc");
+	igt_vkms_device_add_encoder(device, "encoder");
+	igt_vkms_device_add_connector(device, "connector");
+	igt_vkms_connector_connect(device, "connector");
+
+	igt_vkms_device_attach_plane_to_crtc(device, "primary", "crtc");
+	igt_vkms_device_attach_encoder_to_connector(device, "connector", "encoder");
+	igt_vkms_device_attach_crtc_to_encoder(device, "encoder", "crtc");
+
+	igt_vkms_enable(device);
+	igt_assert(igt_vkms_is_enabled(device));
+}
+
+
+igt_main
+{
+	igt_vkms_t device;
+
+	/* By clearing out all existing devices, we don't end up with confusing
+	 * name collision errors. */
+	igt_fixture
+	{
+		igt_require_vkms();
+		igt_vkms_destroy_all_devices();
+	}
+
+	igt_describe("Can create a minimal device.");
+	igt_subtest("basic_device")
+	{
+		igt_vkms_device_create(&device, "basic-device");
+		vkms_basic_test(&device);
+		igt_vkms_device_destroy(&device);
+	}
+
+	igt_fixture
+	{
+		igt_require_vkms();
+		/* avoid potential dangling directories/objects if some of the
+		 * tests failed */
+		if (strcmp(device.name, ""))
+			igt_vkms_device_destroy(&device);
+	}
+}
-- 
2.40.1



More information about the igt-dev mailing list