1 /* Copyright 2020-2021 IGEL Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 #include "lease-manager.h"
19 #include "drm-lease.h"
32 #include <sys/sysmacros.h>
35 #include <xf86drmMode.h>
37 /* Number of resources, to be included in a DRM lease for each connector.
38 * Each connector needs both a CRTC and conector object:. */
39 #define DRM_OBJECTS_PER_CONNECTOR (2)
41 #define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0]))
44 struct lease_handle base;
53 /* for lease transfer completion */
55 pthread_t transition_tid;
56 bool transition_running;
63 drmModeResPtr drm_resource;
64 drmModePlaneResPtr drm_plane_resource;
65 uint32_t available_crtcs;
67 char **connector_names;
70 struct lease **leases;
74 static const char *const connector_type_names[] = {
75 [DRM_MODE_CONNECTOR_Unknown] = "Unknown",
76 [DRM_MODE_CONNECTOR_VGA] = "VGA",
77 [DRM_MODE_CONNECTOR_DVII] = "DVI-I",
78 [DRM_MODE_CONNECTOR_DVID] = "DVI-D",
79 [DRM_MODE_CONNECTOR_DVIA] = "DVI-A",
80 [DRM_MODE_CONNECTOR_Composite] = "Composite",
81 [DRM_MODE_CONNECTOR_SVIDEO] = "SVIDEO",
82 [DRM_MODE_CONNECTOR_LVDS] = "LVDS",
83 [DRM_MODE_CONNECTOR_Component] = "Component",
84 [DRM_MODE_CONNECTOR_9PinDIN] = "DIN",
85 [DRM_MODE_CONNECTOR_DisplayPort] = "DP",
86 [DRM_MODE_CONNECTOR_HDMIA] = "HDMI-A",
87 [DRM_MODE_CONNECTOR_HDMIB] = "HDMI-B",
88 [DRM_MODE_CONNECTOR_TV] = "TV",
89 [DRM_MODE_CONNECTOR_eDP] = "eDP",
90 [DRM_MODE_CONNECTOR_VIRTUAL] = "Virtual",
91 [DRM_MODE_CONNECTOR_DSI] = "DSI",
92 [DRM_MODE_CONNECTOR_DPI] = "DPI",
93 [DRM_MODE_CONNECTOR_WRITEBACK] = "Writeback",
96 static char *drm_create_connector_name(drmModeConnectorPtr connector)
98 uint32_t type = connector->connector_type;
99 uint32_t id = connector->connector_type_id;
101 if (type >= ARRAY_LENGTH(connector_type_names))
102 type = DRM_MODE_CONNECTOR_Unknown;
104 /* If the type is "Unknown", use the connector_id as the identify to
105 * guarantee that the name will be unique. */
106 if (type == DRM_MODE_CONNECTOR_Unknown)
107 id = connector->connector_id;
110 if (asprintf(&name, "%s-%d", connector_type_names[type], id) < 0)
116 static char *drm_create_default_lease_name(struct lm *lm, int cindex)
118 char *connector_name = lm->connector_names[cindex];
121 if (asprintf(&name, "card%d-%s", minor(lm->dev_id), connector_name) < 0)
127 static int drm_get_encoder_crtc_index(struct lm *lm, drmModeEncoderPtr encoder)
129 uint32_t crtc_id = encoder->crtc_id;
133 // The CRTC index only makes sense if it is less than the number of
134 // bits in the encoder possible_crtcs bitmap, which is 32.
135 assert(lm->drm_resource->count_crtcs < 32);
137 for (int i = 0; i < lm->drm_resource->count_crtcs; i++) {
138 if (lm->drm_resource->crtcs[i] == crtc_id)
144 static int drm_get_active_crtc_index(struct lm *lm,
145 drmModeConnectorPtr connector)
147 drmModeEncoder *encoder =
148 drmModeGetEncoder(lm->drm_fd, connector->encoder_id);
152 int crtc_idx = drm_get_encoder_crtc_index(lm, encoder);
153 drmModeFreeEncoder(encoder);
157 static int drm_get_crtc_index(struct lm *lm, drmModeConnectorPtr connector)
160 // try the active CRTC first
161 int crtc_index = drm_get_active_crtc_index(lm, connector);
162 if (crtc_index != -1)
165 // If not try the first available CRTC on the connector/encoder
166 for (int i = 0; i < connector->count_encoders; i++) {
167 drmModeEncoder *encoder =
168 drmModeGetEncoder(lm->drm_fd, connector->encoders[i]);
172 uint32_t usable_crtcs =
173 lm->available_crtcs & encoder->possible_crtcs;
174 int crtc = ffs(usable_crtcs);
175 drmModeFreeEncoder(encoder);
178 crtc_index = crtc - 1;
179 lm->available_crtcs &= ~(1 << crtc_index);
185 static void drm_find_available_crtcs(struct lm *lm)
187 // Assume all CRTCS are available by default,
188 lm->available_crtcs = ~0;
190 // then remove any that are in use. */
191 for (int i = 0; i < lm->drm_resource->count_encoders; i++) {
192 int enc_id = lm->drm_resource->encoders[i];
193 drmModeEncoderPtr enc = drmModeGetEncoder(lm->drm_fd, enc_id);
197 int crtc_idx = drm_get_encoder_crtc_index(lm, enc);
199 lm->available_crtcs &= ~(1 << crtc_idx);
201 drmModeFreeEncoder(enc);
205 static bool drm_find_connector(struct lm *lm, char *name, uint32_t *id)
207 int connectors = lm->drm_resource->count_connectors;
209 for (int i = 0; i < connectors; i++) {
210 if (strcmp(lm->connector_names[i], name))
213 *id = lm->drm_resource->connectors[i];
219 static void config_get_planes(struct lm *lm,
220 const struct connector_config *config,
221 int *nplanes, uint32_t **planes)
223 if (config && config->planes) {
224 *nplanes = config->nplanes;
225 *planes = config->planes;
227 *nplanes = (int)lm->drm_plane_resource->count_planes;
228 *planes = lm->drm_plane_resource->planes;
232 static bool lease_add_planes(struct lm *lm, struct lease *lease,
234 const struct connector_config *con_config)
238 uint32_t crtc_mask = (1 << crtc_index);
240 /* Only allow shared planes when plane list is explicitly set */
241 bool allow_shared = con_config && con_config->planes;
243 config_get_planes(lm, con_config, &nplanes, &planes);
245 for (int i = 0; i < nplanes; i++) {
246 uint32_t plane_id = planes[i];
247 drmModePlanePtr plane = drmModeGetPlane(lm->drm_fd, plane_id);
251 "Unknown plane id %d configured in lease: %s\n",
252 plane_id, lease->base.name);
256 if (plane->possible_crtcs & crtc_mask) {
257 bool shared_plane = plane->possible_crtcs != crtc_mask;
258 if (allow_shared || !shared_plane)
259 lease->object_ids[lease->nobject_ids++] =
262 drmModeFreePlane(plane);
268 * Wait for a client to update the DRM framebuffer on the CRTC managed by
269 * a lease. Once the framebuffer has been updated, it is safe to close
270 * the fd associated with the previous lease client, freeing the previous
271 * framebuffer if there are no other references to it. */
272 static void wait_for_fb_update(struct lease *lease, uint32_t old_fb)
274 uint32_t current_fb = old_fb;
276 struct pollfd drm_poll = {
277 .fd = lease->lease_fd,
281 while (current_fb == old_fb) {
283 if (poll(&drm_poll, 1, -1) < 0) {
289 crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
290 current_fb = crtc->buffer_id;
291 drmModeFreeCrtc(crtc);
295 struct transition_ctx {
301 static void transition_done(void *arg)
303 struct transition_ctx *ctx = arg;
304 close(ctx->close_fd);
308 static void *finish_transition_task(void *arg)
310 struct transition_ctx *ctx = arg;
311 pthread_cleanup_push(transition_done, ctx);
312 wait_for_fb_update(ctx->lease, ctx->old_fb);
313 pthread_cleanup_pop(true);
317 static void close_after_lease_transition(struct lease *lease, int close_fd)
319 struct transition_ctx *ctx = calloc(1, sizeof(*ctx));
323 drmModeCrtcPtr crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
326 ctx->close_fd = close_fd;
327 ctx->old_fb = crtc->buffer_id;
329 drmModeFreeCrtc(crtc);
331 int ret = pthread_create(&lease->transition_tid, NULL,
332 finish_transition_task, ctx);
334 lease->transition_running = (ret == 0);
337 static void cancel_lease_transition_thread(struct lease *lease)
340 if (lease->transition_running) {
341 pthread_cancel(lease->transition_tid);
342 pthread_join(lease->transition_tid, NULL);
345 lease->transition_running = false;
348 static void lease_free(struct lease *lease)
350 free(lease->base.name);
351 free(lease->object_ids);
355 static struct lease *lease_create(struct lm *lm,
356 const struct lease_config *config)
360 if (!config->lease_name) {
361 ERROR_LOG("Mising lease name\n");
365 lease = calloc(1, sizeof(struct lease));
367 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
371 lease->base.name = strdup(config->lease_name);
372 if (!lease->base.name) {
373 DEBUG_LOG("Can't create lease name: %s\n", strerror(errno));
378 config->nconnectors > 0 ? config->nconnectors : config->ncids;
379 int nobjects = lm->drm_plane_resource->count_planes +
380 nconnectors * DRM_OBJECTS_PER_CONNECTOR;
382 lease->object_ids = calloc(nobjects, sizeof(uint32_t));
383 if (!lease->object_ids) {
384 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
388 for (int i = 0; i < nconnectors; i++) {
390 struct connector_config *con_config = NULL;
392 if (config->nconnectors > 0)
393 con_config = &config->connectors[i];
396 char *connector_name = con_config->name;
397 bool optional = con_config->optional;
400 drm_find_connector(lm, connector_name, &cid);
402 bool missing_mandatory = !found && !optional;
403 bool missing_optional = !found && optional;
405 if (missing_mandatory) {
406 ERROR_LOG("Lease: %s, "
407 "mandatory connector %s not found\n",
408 config->lease_name, connector_name);
410 } else if (missing_optional) {
411 WARN_LOG("Lease: %s, "
412 "unknown DRM connector: %s\n",
413 config->lease_name, connector_name);
417 cid = config->connector_ids[i];
420 drmModeConnectorPtr connector =
421 drmModeGetConnector(lm->drm_fd, cid);
423 if (connector == NULL) {
424 ERROR_LOG("Can't find connector id: %d\n", cid);
428 int crtc_index = drm_get_crtc_index(lm, connector);
430 drmModeFreeConnector(connector);
432 if (crtc_index < 0) {
433 DEBUG_LOG("No crtc found for connector: %d, lease %s\n",
434 cid, lease->base.name);
438 if (!lease_add_planes(lm, lease, crtc_index, con_config))
441 uint32_t crtc_id = lm->drm_resource->crtcs[crtc_index];
442 lease->crtc_id = crtc_id;
443 lease->object_ids[lease->nobject_ids++] = crtc_id;
444 lease->object_ids[lease->nobject_ids++] = cid;
446 lease->is_granted = false;
447 lease->lease_fd = -1;
456 static void destroy_default_lease_configs(int num_configs,
457 struct lease_config *configs)
459 for (int i = 0; i < num_configs; i++) {
460 free(configs[i].connector_ids);
461 free(configs[i].lease_name);
467 static int create_default_lease_configs(struct lm *lm,
468 struct lease_config **configs)
470 struct lease_config *def_configs;
471 int num_configs = lm->drm_resource->count_connectors;
476 def_configs = calloc(num_configs, sizeof(*def_configs));
478 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
482 for (int i = 0; i < num_configs; i++) {
483 uint32_t cid = lm->drm_resource->connectors[i];
485 def_configs[i].connector_ids = malloc(sizeof(uint32_t));
486 if (!def_configs[i].connector_ids) {
487 DEBUG_LOG("Memory allocation failed: %s\n",
492 def_configs[i].lease_name =
493 drm_create_default_lease_name(lm, i);
495 def_configs[i].connector_ids[0] = cid;
496 def_configs[i].ncids = 1;
499 *configs = def_configs;
503 destroy_default_lease_configs(num_configs, def_configs);
507 static struct lm *drm_device_get_resources(const char *device)
509 struct lm *lm = calloc(1, sizeof(struct lm));
511 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
514 lm->drm_fd = open(device, O_RDWR);
515 if (lm->drm_fd < 0) {
516 ERROR_LOG("Cannot open DRM device (%s): %s\n", device,
521 /* Enable universal planes so that ALL planes, even primary and cursor
522 * planes can be assigned from lease configurations. */
523 if (drmSetClientCap(lm->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
524 DEBUG_LOG("drmSetClientCap failed\n");
528 lm->drm_resource = drmModeGetResources(lm->drm_fd);
529 if (!lm->drm_resource) {
530 ERROR_LOG("Invalid DRM device(%s)\n", device);
531 DEBUG_LOG("drmModeGetResources failed: %s\n", strerror(errno));
535 lm->drm_plane_resource = drmModeGetPlaneResources(lm->drm_fd);
536 if (!lm->drm_plane_resource) {
537 DEBUG_LOG("drmModeGetPlaneResources failed: %s\n",
543 if (fstat(lm->drm_fd, &st) < 0 || !S_ISCHR(st.st_mode)) {
544 DEBUG_LOG("%s is not a valid device file\n", device);
548 lm->dev_id = st.st_rdev;
550 lm->nconnectors = lm->drm_resource->count_connectors;
551 lm->connector_names = calloc(lm->nconnectors, sizeof(char *));
553 for (int i = 0; i < lm->nconnectors; i++) {
554 drmModeConnectorPtr connector;
555 uint32_t cid = lm->drm_resource->connectors[i];
557 connector = drmModeGetConnector(lm->drm_fd, cid);
558 lm->connector_names[i] = drm_create_connector_name(connector);
559 drmModeFreeConnector(connector);
561 if (!lm->connector_names[i]) {
562 DEBUG_LOG("Can't create name for connector %d: %s\n",
563 cid, strerror(errno));
573 static struct lm *drm_find_drm_device(const char *device)
575 drmDevicePtr devices[64];
577 struct lm *lm = NULL;
580 return drm_device_get_resources(device);
582 ndevs = drmGetDevices2(0, devices, 64);
584 for (int i = 0; i < ndevs; i++) {
585 lm = drm_device_get_resources(
586 devices[i]->nodes[DRM_NODE_PRIMARY]);
591 drmFreeDevices(devices, ndevs);
596 static int lm_create_leases(struct lm *lm, int num_leases,
597 const struct lease_config *configs)
599 lm->leases = calloc(num_leases, sizeof(struct lease *));
601 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
605 drm_find_available_crtcs(lm);
607 for (int i = 0; i < num_leases; i++) {
608 struct lease *lease = lease_create(lm, &configs[i]);
612 lm->leases[lm->nleases] = lease;
615 if (lm->nleases == 0)
621 struct lm *lm_create_with_config(const char *device, int num_leases,
622 struct lease_config *configs)
624 struct lease_config *default_configs = NULL;
625 struct lm *lm = drm_find_drm_device(device);
628 ERROR_LOG("No available DRM device found\n");
632 if (configs == NULL || num_leases == 0) {
633 num_leases = create_default_lease_configs(lm, &default_configs);
634 if (num_leases < 0) {
636 ERROR_LOG("DRM connector enumeration failed\n");
639 configs = default_configs;
642 if (lm_create_leases(lm, num_leases, configs) < 0) {
648 destroy_default_lease_configs(num_leases, default_configs);
652 struct lm *lm_create(const char *device)
654 return lm_create_with_config(device, 0, NULL);
657 void lm_destroy(struct lm *lm)
661 for (int i = 0; i < lm->nleases; i++) {
662 struct lease_handle *lease_handle = &lm->leases[i]->base;
663 lm_lease_revoke(lm, lease_handle);
664 lm_lease_close(lease_handle);
665 lease_free(lm->leases[i]);
670 for (int i = 0; i < lm->nconnectors; i++)
671 free(lm->connector_names[i]);
672 free(lm->connector_names);
674 drmModeFreeResources(lm->drm_resource);
675 drmModeFreePlaneResources(lm->drm_plane_resource);
680 int lm_get_lease_handles(struct lm *lm, struct lease_handle ***handles)
685 *handles = (struct lease_handle **)lm->leases;
689 int lm_lease_grant(struct lm *lm, struct lease_handle *handle)
694 struct lease *lease = (struct lease *)handle;
695 if (lease->is_granted) {
696 /* Lease is already claimed */
701 drmModeCreateLease(lm->drm_fd, lease->object_ids,
702 lease->nobject_ids, 0, &lease->lessee_id);
704 ERROR_LOG("drmModeCreateLease failed on lease %s: %s\n",
705 lease->base.name, strerror(errno));
709 lease->is_granted = true;
711 int old_lease_fd = lease->lease_fd;
712 lease->lease_fd = lease_fd;
714 if (old_lease_fd >= 0)
715 close_after_lease_transition(lease, old_lease_fd);
720 int lm_lease_transfer(struct lm *lm, struct lease_handle *handle)
725 struct lease *lease = (struct lease *)handle;
726 if (!lease->is_granted)
729 lm_lease_revoke(lm, handle);
730 if (lm_lease_grant(lm, handle) < 0) {
731 lm_lease_close(handle);
735 return lease->lease_fd;
738 void lm_lease_revoke(struct lm *lm, struct lease_handle *handle)
743 struct lease *lease = (struct lease *)handle;
745 if (!lease->is_granted)
748 drmModeRevokeLease(lm->drm_fd, lease->lessee_id);
749 cancel_lease_transition_thread(lease);
750 lease->is_granted = false;
753 void lm_lease_close(struct lease_handle *handle)
757 struct lease *lease = (struct lease *)handle;
758 if (lease->lease_fd >= 0)
759 close(lease->lease_fd);
760 lease->lease_fd = -1;