DRM leases is a method developed by Keith Packard to allow other application
manage the output of a display/VR, while a DRM master is already owning the
outputs resources. A more thorough explanation and terminology can be found at
[1].

This patch adds support for DRM lease. libdrm is lease-aware in 2.4.89,
while the kernel support has landed in Linus's master tree (4.15).

The easiest way to choose which output to lease is to have a 'lease' entry for
the output in the config file. My assumption is based on the fact that one would
not want to destroy/close the application running on a particular output. Upon
leasing an output to a client that output will no longer be managed by weston.
When the application is done with the lease it will send a revoke request,
moment in which weston will reclaim the output.

For instance the following configuration file can lease HDMI-A-1 output to other
application.

[output]
name=HDMI-A-1
lease=on

The patch relies on previous drm-lease-unstable-v1 extension protocol
posted previously [4].

[1] https://keithp.com/blogs/DRM-lease/
[2] 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v4.15-rc9&id=62884cd386b876638720ef88374b31a84ca7ee5f
[3] 
https://cgit.freedesktop.org/mesa/drm/commit/?id=c4171535389d72e9135c9615cecd07b346fd6d7e
[4] 
https://lists.freedesktop.org/archives/wayland-devel/2018-January/036652.html

Signed-off-by: Marius Vlad <[email protected]>
---
 Makefile.am                |   2 +
 compositor/main.c          |  10 +++
 configure.ac               |   4 +-
 libweston/compositor-drm.c | 204 ++++++++++++++++++++++++++++++++++++++++++++-
 libweston/compositor.c     |   1 +
 libweston/compositor.h     |   3 +
 man/weston.ini.man         |   8 ++
 7 files changed, 230 insertions(+), 2 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index e224d60..9a5fcb9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -178,6 +178,8 @@ nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES =             
                \
        protocol/viewporter-server-protocol.h           \
        protocol/linux-dmabuf-unstable-v1-protocol.c    \
        protocol/linux-dmabuf-unstable-v1-server-protocol.h             \
+       protocol/drm-lease-unstable-v1-protocol.c       \
+       protocol/drm-lease-unstable-v1-server-protocol.h        \
        protocol/relative-pointer-unstable-v1-protocol.c                \
        protocol/relative-pointer-unstable-v1-server-protocol.h         \
        protocol/pointer-constraints-unstable-v1-protocol.c             \
diff --git a/compositor/main.c b/compositor/main.c
index 7feb4cb..bf1712c 100644
--- a/compositor/main.c
+++ b/compositor/main.c
@@ -1186,6 +1186,7 @@ drm_backend_output_configure(struct wl_listener 
*listener, void *data)
                modeline = s;
                s = NULL;
        }
+
        free(s);
 
        if (api->set_mode(output, mode, modeline) < 0) {
@@ -1208,6 +1209,15 @@ drm_backend_output_configure(struct wl_listener 
*listener, void *data)
 
        api->set_seat(output, seat);
        free(seat);
+#ifdef HAVE_DRM_LEASE
+       char *lease;
+       weston_config_section_get_string(section, "lease", &lease, "off");
+       if (!strncmp(lease, "on", 2)) {
+               output->lease = true;
+               weston_log("Enabling lease on output %s\n", output->name);
+       }
+       free(lease);
+#endif
 
        weston_output_enable(output);
 }
diff --git a/configure.ac b/configure.ac
index 6f295dc..444666b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -209,9 +209,11 @@ if test x$enable_drm_compositor = xyes; then
   PKG_CHECK_MODULES(DRM_COMPOSITOR_GBM, [gbm >= 10.2],
                    [AC_DEFINE([HAVE_GBM_FD_IMPORT], 1, [gbm supports dmabuf 
import])],
                    [AC_MSG_WARN([gbm does not support dmabuf import, will omit 
that capability])])
+  PKG_CHECK_MODULES(DRM_LEASE, [libdrm >= 2.4.89],
+                   [AC_DEFINE([HAVE_DRM_LEASE], 1, [libdrm support lease 
capability])],
+                   [AC_MSG_WARN([libdrm doesn't have leases support, will omit 
that capability])])
 fi
 
-
 PKG_CHECK_MODULES(LIBINPUT_BACKEND, [libinput >= 0.8.0])
 PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES])
 
diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index fe59bf5..98c2e6b 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -62,6 +62,7 @@
 #include "presentation-time-server-protocol.h"
 #include "linux-dmabuf.h"
 #include "linux-dmabuf-unstable-v1-server-protocol.h"
+#include "drm-lease-unstable-v1-server-protocol.h"
 
 #ifndef DRM_CAP_TIMESTAMP_MONOTONIC
 #define DRM_CAP_TIMESTAMP_MONOTONIC 0x6
@@ -386,6 +387,30 @@ static struct gl_renderer_interface *gl_renderer;
 
 static const char default_seat[] = "seat0";
 
+#ifdef HAVE_DRM_LEASE
+/* hold current leases given */
+static struct wl_list leases;
+
+struct drm_display {
+       uint32_t connector_id;
+       uint32_t crtc_id;
+       uint32_t plane_id;
+};
+
+struct drm_lease {
+       int leased_fd;
+       uint32_t leased_id;
+       struct wl_list list;
+};
+
+struct drm_lease_data {
+       struct drm_backend *drm_backend;
+       struct udev_device *drm_device;
+};
+
+static struct drm_lease_data drm_lease_data;
+#endif
+
 static inline struct drm_output *
 to_drm_output(struct weston_output *base)
 {
@@ -4399,6 +4424,26 @@ drm_restore(struct weston_compositor *ec)
        weston_launcher_restore(ec->launcher);
 }
 
+#ifdef HAVE_DRM_LEASE
+static void
+drm_lease_destroy(struct drm_backend *b)
+{
+       struct drm_lease *lease, *lease_iter;
+
+       wl_list_for_each_safe(lease, lease_iter, &leases, list) {
+               if (drmModeRevokeLease(b->drm.fd, lease->leased_id) < 0) {
+                       weston_log("Failed to revoke lease id %u\n",
+                                  lease->leased_id);
+                       continue;
+               }
+
+               weston_log("Lease id %u revoked\n", lease->leased_id);
+               wl_list_remove(&lease->list);
+               free(lease);
+       }
+}
+#endif
+
 static void
 drm_destroy(struct weston_compositor *ec)
 {
@@ -4412,7 +4457,9 @@ drm_destroy(struct weston_compositor *ec)
        b->shutting_down = true;
 
        destroy_sprites(b);
-
+#ifdef HAVE_DRM_LEASE
+       drm_lease_destroy(b);
+#endif
        weston_compositor_shutdown(ec);
 
        if (b->gbm)
@@ -4837,6 +4884,158 @@ static const struct weston_drm_output_api api = {
        drm_output_set_seat,
 };
 
+#ifdef HAVE_DRM_LEASE
+static void
+drm_lease_save_objects(const struct drm_display *drm,
+                      uint32_t *objects, uint32_t *nobjects)
+{
+       uint32_t i = 0;
+
+       if (drm->connector_id)
+               objects[i++] = drm->connector_id;
+
+       if (drm->crtc_id)
+               objects[i++] = drm->crtc_id;
+
+       /* if universal plane */
+       if (drm->plane_id)
+               objects[i++] = drm->plane_id;
+
+       *nobjects = i;
+}
+static void
+drm_lease_create(struct wl_client *client, struct wl_resource *resource)
+{
+       struct drm_lease_data *user_data = wl_resource_get_user_data(resource);
+       struct weston_compositor *compositor = 
user_data->drm_backend->compositor;
+       int drm_fd = user_data->drm_backend->drm.fd;
+
+       struct drm_output *output = NULL;
+       struct drm_output *choosen_output = NULL;
+
+       uint32_t objects[3];
+       uint32_t nobjects;
+
+       struct drm_lease *lease;
+       struct drm_display display = {};
+
+       wl_list_for_each(output, &compositor->output_list, base.link) {
+               struct weston_output *wet_output = &output->base;
+               if (wet_output->lease) {
+                       display.crtc_id = output->crtc_id;
+                       display.connector_id = output->connector_id;
+                       display.plane_id = output->scanout_plane->plane_id;
+
+                       choosen_output = output;
+                       break;
+               }
+       }
+
+       if (!choosen_output) {
+               weston_log("No valid output found to lease!\n");
+               zwp_drm_lease_v1_send_failed(resource);
+               return;
+       }
+
+       drm_lease_save_objects(&display, objects, &nobjects);
+       lease = zalloc(sizeof(*lease));
+
+       /* create lease */
+       lease->leased_fd = drmModeCreateLease(drm_fd, objects, nobjects, 0,
+                                             &lease->leased_id);
+       if (lease->leased_fd < 0) {
+               weston_log("Failed to create the lease!");
+               free(lease);
+               zwp_drm_lease_v1_send_failed(resource);
+               return;
+       }
+
+       weston_log("Lease leased_id = %u created, on output %s\n",
+                  lease->leased_id, choosen_output->base.name);
+
+       wl_list_insert(&leases, &lease->list);
+       drm_output_destroy(&choosen_output->base);
+
+       zwp_drm_lease_v1_send_created(resource, lease->leased_fd,
+                                     lease->leased_id);
+}
+
+
+static void
+drm_lease_revoke(struct wl_client *client, struct wl_resource *resource, 
uint32_t id)
+{
+       struct drm_lease *lease, *lease_iter;
+       struct drm_lease_data *user_data = wl_resource_get_user_data(resource);
+       struct weston_compositor *compositor = 
user_data->drm_backend->compositor;
+       int drm_fd = user_data->drm_backend->drm.fd;
+
+       wl_list_for_each_safe(lease, lease_iter, &leases, list) {
+               if (lease->leased_id == id) {
+                       if (drmModeRevokeLease(drm_fd, lease->leased_id) < 0) {
+                               goto fail;
+                       }
+
+                       wl_list_remove(&lease->list);
+                       zwp_drm_lease_v1_send_revoked(resource);
+
+                       free(lease);
+
+                       /* re-initialize outputs */
+                       update_outputs(user_data->drm_backend, 
user_data->drm_device);
+
+                       wl_signal_emit(&compositor->wake_signal, compositor);
+                       wl_event_source_timer_update(compositor->idle_source,
+                                                    compositor->idle_time * 
1000);
+                       return;
+               }
+       }
+
+fail:
+       weston_log("No valid lease found to revoke for id %u\n", id);
+       zwp_drm_lease_v1_send_failed(resource);
+}
+
+static const struct zwp_drm_lease_v1_interface drm_lease_implementation = {
+       drm_lease_create,
+       drm_lease_revoke,
+};
+
+static void
+__drm_lease_init(struct wl_client *client, void *data, uint32_t ver, uint32_t 
id)
+{
+       struct drm_lease_data *lease_data = (struct drm_lease_data *) data;
+       struct wl_resource *resource;
+
+       resource = wl_resource_create(client, &zwp_drm_lease_v1_interface,
+                                     ver, id);
+       if (resource == NULL) {
+               wl_client_post_no_memory(client);
+               return;
+       }
+
+       wl_resource_set_implementation(resource, &drm_lease_implementation,
+                                      lease_data, NULL);
+}
+
+static int
+drm_lease_init(struct drm_backend *drm_backend, struct udev_device *drm_device)
+{
+       struct weston_compositor *compositor = drm_backend->compositor;
+
+       wl_list_init(&leases);
+
+       drm_lease_data.drm_backend = drm_backend;
+       drm_lease_data.drm_device = drm_device;
+
+       if (!wl_global_create(compositor->wl_display,
+                             &zwp_drm_lease_v1_interface, 1, &drm_lease_data,
+                             __drm_lease_init))
+               return -1;
+
+       return 0;
+}
+#endif
+
 static struct drm_backend *
 drm_backend_create(struct weston_compositor *compositor,
                   struct weston_drm_backend_config *config)
@@ -4945,6 +5144,9 @@ drm_backend_create(struct weston_compositor *compositor,
                weston_log("failed to create output for %s\n", b->drm.filename);
                goto err_udev_input;
        }
+#ifdef HAVE_DRM_LEASE
+       drm_lease_init(b, drm_device);
+#endif
 
        /* A this point we have some idea of whether or not we have a working
         * cursor plane. */
diff --git a/libweston/compositor.c b/libweston/compositor.c
index aec937b..529a61c 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -4773,6 +4773,7 @@ weston_output_init(struct weston_output *output,
        output->name = strdup(name);
        wl_list_init(&output->link);
        output->enabled = false;
+       output->lease = false;
 
        /* Add some (in)sane defaults which can be used
         * for checking if an output was properly configured
diff --git a/libweston/compositor.h b/libweston/compositor.h
index dffcba8..da54180 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -239,6 +239,9 @@ struct weston_output {
 
        int (*enable)(struct weston_output *output);
        int (*disable)(struct weston_output *output);
+
+       /**< this output can be leased to other application */
+       bool lease;
 };
 
 enum weston_pointer_motion_mask {
diff --git a/man/weston.ini.man b/man/weston.ini.man
index f237fd6..a2f603c 100644
--- a/man/weston.ini.man
+++ b/man/weston.ini.man
@@ -436,6 +436,14 @@ multiheaded environment with a single compositor for 
multiple output and input
 configurations. The default seat is called "default" and will always be
 present. This seat can be constrained like any other.
 .RE
+.TP 7
+.BI "lease=" on/off
+Specifies that this output can leased to other applications. If an application
+requests a lease, this output will be destroyed and the application has full
+control over the output. Upon revoking the lease weston will take control of 
the
+output.
+.RE
+.TP 7
 .SH "INPUT-METHOD SECTION"
 .TP 7
 .BI "path=" "/usr/libexec/weston-keyboard"
-- 
2.9.3

_______________________________________________
wayland-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to