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));
346 int nconnectors = config->cnames > 0 ? config->cnames : config->ncids;
347 int nobjects = lm->drm_plane_resource->count_planes +
348 nconnectors * DRM_OBJECTS_PER_CONNECTOR;
350 lease->object_ids = calloc(nobjects, sizeof(uint32_t));
351 if (!lease->object_ids) {
352 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
356 for (int i = 0; i < nconnectors; i++) {
359 if (config->cnames > 0) {
360 char *connector_name = config->connector_names[i];
362 if (!drm_find_connector(lm, connector_name, &cid)) {
363 WARN_LOG("Lease: %s, "
364 "unknown DRM connector: %s\n",
365 config->lease_name, connector_name);
369 cid = config->connector_ids[i];
372 drmModeConnectorPtr connector =
373 drmModeGetConnector(lm->drm_fd, cid);
375 if (connector == NULL) {
376 ERROR_LOG("Can't find connector id: %d\n", cid);
380 int crtc_index = drm_get_crtc_index(lm, connector);
382 drmModeFreeConnector(connector);
384 if (crtc_index < 0) {
385 DEBUG_LOG("No crtc found for connector: %d, lease %s\n",
386 cid, lease->base.name);
390 if (!lease_add_planes(lm, lease, crtc_index))
393 uint32_t crtc_id = lm->drm_resource->crtcs[crtc_index];
394 lease->crtc_id = crtc_id;
395 lease->object_ids[lease->nobject_ids++] = crtc_id;
396 lease->object_ids[lease->nobject_ids++] = cid;
398 lease->is_granted = false;
399 lease->lease_fd = -1;
408 static void destroy_default_lease_configs(int num_configs,
409 struct lease_config *configs)
411 for (int i = 0; i < num_configs; i++) {
412 free(configs[i].connector_ids);
413 free(configs[i].lease_name);
419 static int create_default_lease_configs(struct lm *lm,
420 struct lease_config **configs)
422 struct lease_config *def_configs;
423 int num_configs = lm->drm_resource->count_connectors;
428 def_configs = calloc(num_configs, sizeof(*def_configs));
430 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
434 for (int i = 0; i < num_configs; i++) {
435 uint32_t cid = lm->drm_resource->connectors[i];
437 def_configs[i].connector_ids = malloc(sizeof(uint32_t));
438 if (!def_configs[i].connector_ids) {
439 DEBUG_LOG("Memory allocation failed: %s\n",
444 def_configs[i].lease_name =
445 drm_create_default_lease_name(lm, i);
447 def_configs[i].connector_ids[0] = cid;
448 def_configs[i].ncids = 1;
451 *configs = def_configs;
455 destroy_default_lease_configs(num_configs, def_configs);
459 static struct lm *drm_device_get_resources(const char *device)
461 struct lm *lm = calloc(1, sizeof(struct lm));
463 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
466 lm->drm_fd = open(device, O_RDWR);
467 if (lm->drm_fd < 0) {
468 ERROR_LOG("Cannot open DRM device (%s): %s\n", device,
473 lm->drm_resource = drmModeGetResources(lm->drm_fd);
474 if (!lm->drm_resource) {
475 ERROR_LOG("Invalid DRM device(%s)\n", device);
476 DEBUG_LOG("drmModeGetResources failed: %s\n", strerror(errno));
480 lm->drm_plane_resource = drmModeGetPlaneResources(lm->drm_fd);
481 if (!lm->drm_plane_resource) {
482 DEBUG_LOG("drmModeGetPlaneResources failed: %s\n",
488 if (fstat(lm->drm_fd, &st) < 0 || !S_ISCHR(st.st_mode)) {
489 DEBUG_LOG("%s is not a valid device file\n", device);
493 lm->dev_id = st.st_rdev;
495 lm->nconnectors = lm->drm_resource->count_connectors;
496 lm->connector_names = calloc(lm->nconnectors, sizeof(char *));
498 for (int i = 0; i < lm->nconnectors; i++) {
499 drmModeConnectorPtr connector;
500 uint32_t cid = lm->drm_resource->connectors[i];
502 connector = drmModeGetConnector(lm->drm_fd, cid);
503 lm->connector_names[i] = drm_create_connector_name(connector);
504 drmModeFreeConnector(connector);
506 if (!lm->connector_names[i]) {
507 DEBUG_LOG("Can't create name for connector %d: %s\n",
508 cid, strerror(errno));
518 static struct lm *drm_find_drm_device(const char *device)
520 drmDevicePtr devices[64];
522 struct lm *lm = NULL;
525 return drm_device_get_resources(device);
527 ndevs = drmGetDevices2(0, devices, 64);
529 for (int i = 0; i < ndevs; i++) {
530 lm = drm_device_get_resources(
531 devices[i]->nodes[DRM_NODE_PRIMARY]);
536 drmFreeDevices(devices, ndevs);
541 static int lm_create_leases(struct lm *lm, int num_leases,
542 const struct lease_config *configs)
544 lm->leases = calloc(num_leases, sizeof(struct lease *));
546 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
550 drm_find_available_crtcs(lm);
552 for (int i = 0; i < num_leases; i++) {
553 struct lease *lease = lease_create(lm, &configs[i]);
557 lm->leases[lm->nleases] = lease;
560 if (lm->nleases == 0)
566 struct lm *lm_create_with_config(const char *device, int num_leases,
567 struct lease_config *configs)
569 struct lease_config *default_configs = NULL;
570 struct lm *lm = drm_find_drm_device(device);
573 ERROR_LOG("No available DRM device found\n");
577 if (configs == NULL || num_leases == 0) {
578 num_leases = create_default_lease_configs(lm, &default_configs);
579 if (num_leases < 0) {
581 ERROR_LOG("DRM connector enumeration failed\n");
584 configs = default_configs;
587 if (lm_create_leases(lm, num_leases, configs) < 0) {
593 destroy_default_lease_configs(num_leases, default_configs);
597 struct lm *lm_create(const char *device)
599 return lm_create_with_config(device, 0, NULL);
602 void lm_destroy(struct lm *lm)
606 for (int i = 0; i < lm->nleases; i++) {
607 struct lease_handle *lease_handle = &lm->leases[i]->base;
608 lm_lease_revoke(lm, lease_handle);
609 lm_lease_close(lease_handle);
610 lease_free(lm->leases[i]);
615 for (int i = 0; i < lm->nconnectors; i++)
616 free(lm->connector_names[i]);
617 free(lm->connector_names);
619 drmModeFreeResources(lm->drm_resource);
620 drmModeFreePlaneResources(lm->drm_plane_resource);
625 int lm_get_lease_handles(struct lm *lm, struct lease_handle ***handles)
630 *handles = (struct lease_handle **)lm->leases;
634 int lm_lease_grant(struct lm *lm, struct lease_handle *handle)
639 struct lease *lease = (struct lease *)handle;
640 if (lease->is_granted) {
641 /* Lease is already claimed */
646 drmModeCreateLease(lm->drm_fd, lease->object_ids,
647 lease->nobject_ids, 0, &lease->lessee_id);
649 ERROR_LOG("drmModeCreateLease failed on lease %s: %s\n",
650 lease->base.name, strerror(errno));
654 lease->is_granted = true;
656 int old_lease_fd = lease->lease_fd;
657 lease->lease_fd = lease_fd;
659 if (old_lease_fd >= 0)
660 close_after_lease_transition(lease, old_lease_fd);
665 int lm_lease_transfer(struct lm *lm, struct lease_handle *handle)
670 struct lease *lease = (struct lease *)handle;
671 if (!lease->is_granted)
674 lm_lease_revoke(lm, handle);
675 if (lm_lease_grant(lm, handle) < 0) {
676 lm_lease_close(handle);
680 return lease->lease_fd;
683 void lm_lease_revoke(struct lm *lm, struct lease_handle *handle)
688 struct lease *lease = (struct lease *)handle;
690 if (!lease->is_granted)
693 drmModeRevokeLease(lm->drm_fd, lease->lessee_id);
694 cancel_lease_transition_thread(lease);
695 lease->is_granted = false;
698 void lm_lease_close(struct lease_handle *handle)
702 struct lease *lease = (struct lease *)handle;
703 if (lease->lease_fd >= 0)
704 close(lease->lease_fd);
705 lease->lease_fd = -1;