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 bool lease_add_planes(struct lm *lm, struct lease *lease, int crtc_index)
221 for (uint32_t i = 0; i < lm->drm_plane_resource->count_planes; i++) {
222 uint32_t plane_id = lm->drm_plane_resource->planes[i];
223 drmModePlanePtr plane = drmModeGetPlane(lm->drm_fd, plane_id);
227 // Exclude planes that can be used with multiple CRTCs for now
228 if (plane->possible_crtcs == (1u << crtc_index)) {
229 lease->object_ids[lease->nobject_ids++] = plane_id;
231 drmModeFreePlane(plane);
237 * Wait for a client to update the DRM framebuffer on the CRTC managed by
238 * a lease. Once the framebuffer has been updated, it is safe to close
239 * the fd associated with the previous lease client, freeing the previous
240 * framebuffer if there are no other references to it. */
241 static void wait_for_fb_update(struct lease *lease, uint32_t old_fb)
243 uint32_t current_fb = old_fb;
245 struct pollfd drm_poll = {
246 .fd = lease->lease_fd,
250 while (current_fb == old_fb) {
252 if (poll(&drm_poll, 1, -1) < 0) {
258 crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
259 current_fb = crtc->buffer_id;
260 drmModeFreeCrtc(crtc);
264 struct transition_ctx {
270 static void transition_done(void *arg)
272 struct transition_ctx *ctx = arg;
273 close(ctx->close_fd);
277 static void *finish_transition_task(void *arg)
279 struct transition_ctx *ctx = arg;
280 pthread_cleanup_push(transition_done, ctx);
281 wait_for_fb_update(ctx->lease, ctx->old_fb);
282 pthread_cleanup_pop(true);
286 static void close_after_lease_transition(struct lease *lease, int close_fd)
288 struct transition_ctx *ctx = calloc(1, sizeof(*ctx));
292 drmModeCrtcPtr crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
295 ctx->close_fd = close_fd;
296 ctx->old_fb = crtc->buffer_id;
298 drmModeFreeCrtc(crtc);
300 int ret = pthread_create(&lease->transition_tid, NULL,
301 finish_transition_task, ctx);
303 lease->transition_running = (ret == 0);
306 static void cancel_lease_transition_thread(struct lease *lease)
309 if (lease->transition_running) {
310 pthread_cancel(lease->transition_tid);
311 pthread_join(lease->transition_tid, NULL);
314 lease->transition_running = false;
317 static void lease_free(struct lease *lease)
319 free(lease->base.name);
320 free(lease->object_ids);
324 static struct lease *lease_create(struct lm *lm,
325 const struct lease_config *config)
329 if (!config->lease_name) {
330 ERROR_LOG("Mising lease name\n");
334 lease = calloc(1, sizeof(struct lease));
336 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
340 lease->base.name = strdup(config->lease_name);
341 if (!lease->base.name) {
342 DEBUG_LOG("Can't create lease name: %s\n", strerror(errno));
347 config->nconnectors > 0 ? config->nconnectors : config->ncids;
348 int nobjects = lm->drm_plane_resource->count_planes +
349 nconnectors * DRM_OBJECTS_PER_CONNECTOR;
351 lease->object_ids = calloc(nobjects, sizeof(uint32_t));
352 if (!lease->object_ids) {
353 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
357 for (int i = 0; i < nconnectors; i++) {
360 if (config->nconnectors > 0) {
361 char *connector_name = config->connectors[i].name;
363 if (!drm_find_connector(lm, connector_name, &cid)) {
364 WARN_LOG("Lease: %s, "
365 "unknown DRM connector: %s\n",
366 config->lease_name, connector_name);
370 cid = config->connector_ids[i];
373 drmModeConnectorPtr connector =
374 drmModeGetConnector(lm->drm_fd, cid);
376 if (connector == NULL) {
377 ERROR_LOG("Can't find connector id: %d\n", cid);
381 int crtc_index = drm_get_crtc_index(lm, connector);
383 drmModeFreeConnector(connector);
385 if (crtc_index < 0) {
386 DEBUG_LOG("No crtc found for connector: %d, lease %s\n",
387 cid, lease->base.name);
391 if (!lease_add_planes(lm, lease, crtc_index))
394 uint32_t crtc_id = lm->drm_resource->crtcs[crtc_index];
395 lease->crtc_id = crtc_id;
396 lease->object_ids[lease->nobject_ids++] = crtc_id;
397 lease->object_ids[lease->nobject_ids++] = cid;
399 lease->is_granted = false;
400 lease->lease_fd = -1;
409 static void destroy_default_lease_configs(int num_configs,
410 struct lease_config *configs)
412 for (int i = 0; i < num_configs; i++) {
413 free(configs[i].connector_ids);
414 free(configs[i].lease_name);
420 static int create_default_lease_configs(struct lm *lm,
421 struct lease_config **configs)
423 struct lease_config *def_configs;
424 int num_configs = lm->drm_resource->count_connectors;
429 def_configs = calloc(num_configs, sizeof(*def_configs));
431 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
435 for (int i = 0; i < num_configs; i++) {
436 uint32_t cid = lm->drm_resource->connectors[i];
438 def_configs[i].connector_ids = malloc(sizeof(uint32_t));
439 if (!def_configs[i].connector_ids) {
440 DEBUG_LOG("Memory allocation failed: %s\n",
445 def_configs[i].lease_name =
446 drm_create_default_lease_name(lm, i);
448 def_configs[i].connector_ids[0] = cid;
449 def_configs[i].ncids = 1;
452 *configs = def_configs;
456 destroy_default_lease_configs(num_configs, def_configs);
460 static struct lm *drm_device_get_resources(const char *device)
462 struct lm *lm = calloc(1, sizeof(struct lm));
464 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
467 lm->drm_fd = open(device, O_RDWR);
468 if (lm->drm_fd < 0) {
469 ERROR_LOG("Cannot open DRM device (%s): %s\n", device,
474 lm->drm_resource = drmModeGetResources(lm->drm_fd);
475 if (!lm->drm_resource) {
476 ERROR_LOG("Invalid DRM device(%s)\n", device);
477 DEBUG_LOG("drmModeGetResources failed: %s\n", strerror(errno));
481 lm->drm_plane_resource = drmModeGetPlaneResources(lm->drm_fd);
482 if (!lm->drm_plane_resource) {
483 DEBUG_LOG("drmModeGetPlaneResources failed: %s\n",
489 if (fstat(lm->drm_fd, &st) < 0 || !S_ISCHR(st.st_mode)) {
490 DEBUG_LOG("%s is not a valid device file\n", device);
494 lm->dev_id = st.st_rdev;
496 lm->nconnectors = lm->drm_resource->count_connectors;
497 lm->connector_names = calloc(lm->nconnectors, sizeof(char *));
499 for (int i = 0; i < lm->nconnectors; i++) {
500 drmModeConnectorPtr connector;
501 uint32_t cid = lm->drm_resource->connectors[i];
503 connector = drmModeGetConnector(lm->drm_fd, cid);
504 lm->connector_names[i] = drm_create_connector_name(connector);
505 drmModeFreeConnector(connector);
507 if (!lm->connector_names[i]) {
508 DEBUG_LOG("Can't create name for connector %d: %s\n",
509 cid, strerror(errno));
519 static struct lm *drm_find_drm_device(const char *device)
521 drmDevicePtr devices[64];
523 struct lm *lm = NULL;
526 return drm_device_get_resources(device);
528 ndevs = drmGetDevices2(0, devices, 64);
530 for (int i = 0; i < ndevs; i++) {
531 lm = drm_device_get_resources(
532 devices[i]->nodes[DRM_NODE_PRIMARY]);
537 drmFreeDevices(devices, ndevs);
542 static int lm_create_leases(struct lm *lm, int num_leases,
543 const struct lease_config *configs)
545 lm->leases = calloc(num_leases, sizeof(struct lease *));
547 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
551 drm_find_available_crtcs(lm);
553 for (int i = 0; i < num_leases; i++) {
554 struct lease *lease = lease_create(lm, &configs[i]);
558 lm->leases[lm->nleases] = lease;
561 if (lm->nleases == 0)
567 struct lm *lm_create_with_config(const char *device, int num_leases,
568 struct lease_config *configs)
570 struct lease_config *default_configs = NULL;
571 struct lm *lm = drm_find_drm_device(device);
574 ERROR_LOG("No available DRM device found\n");
578 if (configs == NULL || num_leases == 0) {
579 num_leases = create_default_lease_configs(lm, &default_configs);
580 if (num_leases < 0) {
582 ERROR_LOG("DRM connector enumeration failed\n");
585 configs = default_configs;
588 if (lm_create_leases(lm, num_leases, configs) < 0) {
594 destroy_default_lease_configs(num_leases, default_configs);
598 struct lm *lm_create(const char *device)
600 return lm_create_with_config(device, 0, NULL);
603 void lm_destroy(struct lm *lm)
607 for (int i = 0; i < lm->nleases; i++) {
608 struct lease_handle *lease_handle = &lm->leases[i]->base;
609 lm_lease_revoke(lm, lease_handle);
610 lm_lease_close(lease_handle);
611 lease_free(lm->leases[i]);
616 for (int i = 0; i < lm->nconnectors; i++)
617 free(lm->connector_names[i]);
618 free(lm->connector_names);
620 drmModeFreeResources(lm->drm_resource);
621 drmModeFreePlaneResources(lm->drm_plane_resource);
626 int lm_get_lease_handles(struct lm *lm, struct lease_handle ***handles)
631 *handles = (struct lease_handle **)lm->leases;
635 int lm_lease_grant(struct lm *lm, struct lease_handle *handle)
640 struct lease *lease = (struct lease *)handle;
641 if (lease->is_granted) {
642 /* Lease is already claimed */
647 drmModeCreateLease(lm->drm_fd, lease->object_ids,
648 lease->nobject_ids, 0, &lease->lessee_id);
650 ERROR_LOG("drmModeCreateLease failed on lease %s: %s\n",
651 lease->base.name, strerror(errno));
655 lease->is_granted = true;
657 int old_lease_fd = lease->lease_fd;
658 lease->lease_fd = lease_fd;
660 if (old_lease_fd >= 0)
661 close_after_lease_transition(lease, old_lease_fd);
666 int lm_lease_transfer(struct lm *lm, struct lease_handle *handle)
671 struct lease *lease = (struct lease *)handle;
672 if (!lease->is_granted)
675 lm_lease_revoke(lm, handle);
676 if (lm_lease_grant(lm, handle) < 0) {
677 lm_lease_close(handle);
681 return lease->lease_fd;
684 void lm_lease_revoke(struct lm *lm, struct lease_handle *handle)
689 struct lease *lease = (struct lease *)handle;
691 if (!lease->is_granted)
694 drmModeRevokeLease(lm->drm_fd, lease->lessee_id);
695 cancel_lease_transition_thread(lease);
696 lease->is_granted = false;
699 void lm_lease_close(struct lease_handle *handle)
703 struct lease *lease = (struct lease *)handle;
704 if (lease->lease_fd >= 0)
705 close(lease->lease_fd);
706 lease->lease_fd = -1;