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 struct lease **leases;
71 static const char *const connector_type_names[] = {
72 [DRM_MODE_CONNECTOR_Unknown] = "Unknown",
73 [DRM_MODE_CONNECTOR_VGA] = "VGA",
74 [DRM_MODE_CONNECTOR_DVII] = "DVI-I",
75 [DRM_MODE_CONNECTOR_DVID] = "DVI-D",
76 [DRM_MODE_CONNECTOR_DVIA] = "DVI-A",
77 [DRM_MODE_CONNECTOR_Composite] = "Composite",
78 [DRM_MODE_CONNECTOR_SVIDEO] = "SVIDEO",
79 [DRM_MODE_CONNECTOR_LVDS] = "LVDS",
80 [DRM_MODE_CONNECTOR_Component] = "Component",
81 [DRM_MODE_CONNECTOR_9PinDIN] = "DIN",
82 [DRM_MODE_CONNECTOR_DisplayPort] = "DP",
83 [DRM_MODE_CONNECTOR_HDMIA] = "HDMI-A",
84 [DRM_MODE_CONNECTOR_HDMIB] = "HDMI-B",
85 [DRM_MODE_CONNECTOR_TV] = "TV",
86 [DRM_MODE_CONNECTOR_eDP] = "eDP",
87 [DRM_MODE_CONNECTOR_VIRTUAL] = "Virtual",
88 [DRM_MODE_CONNECTOR_DSI] = "DSI",
89 [DRM_MODE_CONNECTOR_DPI] = "DPI",
90 [DRM_MODE_CONNECTOR_WRITEBACK] = "Writeback",
93 static char *drm_create_default_lease_name(struct lm *lm,
94 drmModeConnectorPtr connector)
96 uint32_t type = connector->connector_type;
97 uint32_t id = connector->connector_type_id;
99 if (type >= ARRAY_LENGTH(connector_type_names))
100 type = DRM_MODE_CONNECTOR_Unknown;
102 /* If the type is "Unknown", use the connector_id as the identify to
103 * guarantee that the name will be unique. */
104 if (type == DRM_MODE_CONNECTOR_Unknown)
105 id = connector->connector_id;
108 if (asprintf(&name, "card%d-%s-%d", minor(lm->dev_id),
109 connector_type_names[type], id) < 0)
115 static int drm_get_encoder_crtc_index(struct lm *lm, drmModeEncoderPtr encoder)
117 uint32_t crtc_id = encoder->crtc_id;
121 // The CRTC index only makes sense if it is less than the number of
122 // bits in the encoder possible_crtcs bitmap, which is 32.
123 assert(lm->drm_resource->count_crtcs < 32);
125 for (int i = 0; i < lm->drm_resource->count_crtcs; i++) {
126 if (lm->drm_resource->crtcs[i] == crtc_id)
132 static int drm_get_active_crtc_index(struct lm *lm,
133 drmModeConnectorPtr connector)
135 drmModeEncoder *encoder =
136 drmModeGetEncoder(lm->drm_fd, connector->encoder_id);
140 int crtc_idx = drm_get_encoder_crtc_index(lm, encoder);
141 drmModeFreeEncoder(encoder);
145 static int drm_get_crtc_index(struct lm *lm, drmModeConnectorPtr connector)
148 // try the active CRTC first
149 int crtc_index = drm_get_active_crtc_index(lm, connector);
150 if (crtc_index != -1)
153 // If not try the first available CRTC on the connector/encoder
154 for (int i = 0; i < connector->count_encoders; i++) {
155 drmModeEncoder *encoder =
156 drmModeGetEncoder(lm->drm_fd, connector->encoders[i]);
160 uint32_t usable_crtcs =
161 lm->available_crtcs & encoder->possible_crtcs;
162 int crtc = ffs(usable_crtcs);
163 drmModeFreeEncoder(encoder);
166 crtc_index = crtc - 1;
167 lm->available_crtcs &= ~(1 << crtc_index);
173 static void drm_find_available_crtcs(struct lm *lm)
175 // Assume all CRTCS are available by default,
176 lm->available_crtcs = ~0;
178 // then remove any that are in use. */
179 for (int i = 0; i < lm->drm_resource->count_encoders; i++) {
180 int enc_id = lm->drm_resource->encoders[i];
181 drmModeEncoderPtr enc = drmModeGetEncoder(lm->drm_fd, enc_id);
185 int crtc_idx = drm_get_encoder_crtc_index(lm, enc);
187 lm->available_crtcs &= ~(1 << crtc_idx);
189 drmModeFreeEncoder(enc);
193 static bool lease_add_planes(struct lm *lm, struct lease *lease, int crtc_index)
195 for (uint32_t i = 0; i < lm->drm_plane_resource->count_planes; i++) {
196 uint32_t plane_id = lm->drm_plane_resource->planes[i];
197 drmModePlanePtr plane = drmModeGetPlane(lm->drm_fd, plane_id);
201 // Exclude planes that can be used with multiple CRTCs for now
202 if (plane->possible_crtcs == (1u << crtc_index)) {
203 lease->object_ids[lease->nobject_ids++] = plane_id;
205 drmModeFreePlane(plane);
211 * Wait for a client to update the DRM framebuffer on the CRTC managed by
212 * a lease. Once the framebuffer has been updated, it is safe to close
213 * the fd associated with the previous lease client, freeing the previous
214 * framebuffer if there are no other references to it. */
215 static void wait_for_fb_update(struct lease *lease, uint32_t old_fb)
217 uint32_t current_fb = old_fb;
219 struct pollfd drm_poll = {
220 .fd = lease->lease_fd,
224 while (current_fb == old_fb) {
226 if (poll(&drm_poll, 1, -1) < 0) {
232 crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
233 current_fb = crtc->buffer_id;
234 drmModeFreeCrtc(crtc);
238 struct transition_ctx {
244 static void transition_done(void *arg)
246 struct transition_ctx *ctx = arg;
247 close(ctx->close_fd);
251 static void *finish_transition_task(void *arg)
253 struct transition_ctx *ctx = arg;
254 pthread_cleanup_push(transition_done, ctx);
255 wait_for_fb_update(ctx->lease, ctx->old_fb);
256 pthread_cleanup_pop(true);
260 static void close_after_lease_transition(struct lease *lease, int close_fd)
262 struct transition_ctx *ctx = calloc(1, sizeof(*ctx));
266 drmModeCrtcPtr crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
269 ctx->close_fd = close_fd;
270 ctx->old_fb = crtc->buffer_id;
272 drmModeFreeCrtc(crtc);
274 int ret = pthread_create(&lease->transition_tid, NULL,
275 finish_transition_task, ctx);
277 lease->transition_running = (ret == 0);
280 static void cancel_lease_transition_thread(struct lease *lease)
283 if (lease->transition_running) {
284 pthread_cancel(lease->transition_tid);
285 pthread_join(lease->transition_tid, NULL);
288 lease->transition_running = false;
291 static void lease_free(struct lease *lease)
293 free(lease->base.name);
294 free(lease->object_ids);
298 static struct lease *lease_create(struct lm *lm,
299 const struct lease_config *config)
303 if (!config->lease_name) {
304 ERROR_LOG("Mising lease name\n");
308 lease = calloc(1, sizeof(struct lease));
310 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
314 lease->base.name = strdup(config->lease_name);
315 if (!lease->base.name) {
316 DEBUG_LOG("Can't create lease name: %s\n", strerror(errno));
320 int nobjects = lm->drm_plane_resource->count_planes +
321 config->ncids * DRM_OBJECTS_PER_CONNECTOR;
323 lease->object_ids = calloc(nobjects, sizeof(uint32_t));
324 if (!lease->object_ids) {
325 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
329 for (int i = 0; i < config->ncids; i++) {
330 drmModeConnectorPtr connector =
331 drmModeGetConnector(lm->drm_fd, config->connector_ids[i]);
333 if (connector == NULL) {
334 ERROR_LOG("Can't find connector id: %d\n",
335 config->connector_ids);
339 uint32_t connector_id = connector->connector_id;
341 int crtc_index = drm_get_crtc_index(lm, connector);
343 drmModeFreeConnector(connector);
345 if (crtc_index < 0) {
346 DEBUG_LOG("No crtc found for connector: %d, lease %s\n",
347 connector_id, lease->base.name);
351 if (!lease_add_planes(lm, lease, crtc_index))
354 uint32_t crtc_id = lm->drm_resource->crtcs[crtc_index];
355 lease->crtc_id = crtc_id;
356 lease->object_ids[lease->nobject_ids++] = crtc_id;
357 lease->object_ids[lease->nobject_ids++] = connector_id;
359 lease->is_granted = false;
360 lease->lease_fd = -1;
369 static void destroy_default_lease_configs(int num_configs,
370 struct lease_config *configs)
372 for (int i = 0; i < num_configs; i++) {
373 free(configs[i].connector_ids);
374 free(configs[i].lease_name);
380 static int create_default_lease_configs(struct lm *lm,
381 struct lease_config **configs)
383 struct lease_config *def_configs;
384 int num_configs = lm->drm_resource->count_connectors;
389 def_configs = calloc(num_configs, sizeof(*def_configs));
391 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
395 for (int i = 0; i < num_configs; i++) {
396 uint32_t cid = lm->drm_resource->connectors[i];
398 def_configs[i].connector_ids = malloc(sizeof(uint32_t));
399 if (!def_configs[i].connector_ids) {
400 DEBUG_LOG("Memory allocation failed: %s\n",
405 drmModeConnectorPtr connector;
406 connector = drmModeGetConnector(lm->drm_fd, cid);
407 def_configs[i].lease_name =
408 drm_create_default_lease_name(lm, connector);
410 if (!def_configs[i].lease_name) {
412 "Can't create lease name for connector %d: %s\n",
413 cid, strerror(errno));
417 drmModeFreeConnector(connector);
419 def_configs[i].connector_ids[0] = cid;
420 def_configs[i].ncids = 1;
423 *configs = def_configs;
427 destroy_default_lease_configs(num_configs, def_configs);
431 static struct lm *drm_device_get_resources(const char *device)
433 struct lm *lm = calloc(1, sizeof(struct lm));
435 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
438 lm->drm_fd = open(device, O_RDWR);
439 if (lm->drm_fd < 0) {
440 ERROR_LOG("Cannot open DRM device (%s): %s\n", device,
445 lm->drm_resource = drmModeGetResources(lm->drm_fd);
446 if (!lm->drm_resource) {
447 ERROR_LOG("Invalid DRM device(%s)\n", device);
448 DEBUG_LOG("drmModeGetResources failed: %s\n", strerror(errno));
452 lm->drm_plane_resource = drmModeGetPlaneResources(lm->drm_fd);
453 if (!lm->drm_plane_resource) {
454 DEBUG_LOG("drmModeGetPlaneResources failed: %s\n",
460 if (fstat(lm->drm_fd, &st) < 0 || !S_ISCHR(st.st_mode)) {
461 DEBUG_LOG("%s is not a valid device file\n", device);
465 lm->dev_id = st.st_rdev;
473 static int lm_create_leases(struct lm *lm, int num_leases,
474 const struct lease_config *configs)
476 lm->leases = calloc(num_leases, sizeof(struct lease *));
478 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
482 drm_find_available_crtcs(lm);
484 for (int i = 0; i < num_leases; i++) {
485 struct lease *lease = lease_create(lm, &configs[i]);
489 lm->leases[lm->nleases] = lease;
492 if (lm->nleases == 0)
498 struct lm *lm_create_with_config(const char *device, int num_leases,
499 struct lease_config *configs)
501 struct lease_config *default_configs = NULL;
502 struct lm *lm = drm_device_get_resources(device);
507 if (configs == NULL) {
508 num_leases = create_default_lease_configs(lm, &default_configs);
509 if (num_leases < 0) {
511 ERROR_LOG("DRM connector enumeration failed\n");
514 configs = default_configs;
517 if (lm_create_leases(lm, num_leases, configs) < 0) {
523 destroy_default_lease_configs(num_leases, default_configs);
527 struct lm *lm_create(const char *device)
529 return lm_create_with_config(device, 0, NULL);
532 void lm_destroy(struct lm *lm)
536 for (int i = 0; i < lm->nleases; i++) {
537 struct lease_handle *lease_handle = &lm->leases[i]->base;
538 lm_lease_revoke(lm, lease_handle);
539 lm_lease_close(lease_handle);
540 lease_free(lm->leases[i]);
544 drmModeFreeResources(lm->drm_resource);
545 drmModeFreePlaneResources(lm->drm_plane_resource);
550 int lm_get_lease_handles(struct lm *lm, struct lease_handle ***handles)
555 *handles = (struct lease_handle **)lm->leases;
559 int lm_lease_grant(struct lm *lm, struct lease_handle *handle)
564 struct lease *lease = (struct lease *)handle;
565 if (lease->is_granted) {
566 /* Lease is already claimed */
571 drmModeCreateLease(lm->drm_fd, lease->object_ids,
572 lease->nobject_ids, 0, &lease->lessee_id);
574 ERROR_LOG("drmModeCreateLease failed on lease %s: %s\n",
575 lease->base.name, strerror(errno));
579 lease->is_granted = true;
581 int old_lease_fd = lease->lease_fd;
582 lease->lease_fd = lease_fd;
584 if (old_lease_fd >= 0)
585 close_after_lease_transition(lease, old_lease_fd);
590 int lm_lease_transfer(struct lm *lm, struct lease_handle *handle)
595 struct lease *lease = (struct lease *)handle;
596 if (!lease->is_granted)
599 lm_lease_revoke(lm, handle);
600 if (lm_lease_grant(lm, handle) < 0) {
601 lm_lease_close(handle);
605 return lease->lease_fd;
608 void lm_lease_revoke(struct lm *lm, struct lease_handle *handle)
613 struct lease *lease = (struct lease *)handle;
615 if (!lease->is_granted)
618 drmModeRevokeLease(lm->drm_fd, lease->lessee_id);
619 cancel_lease_transition_thread(lease);
620 lease->is_granted = false;
623 void lm_lease_close(struct lease_handle *handle)
627 struct lease *lease = (struct lease *)handle;
628 if (lease->lease_fd >= 0)
629 close(lease->lease_fd);
630 lease->lease_fd = -1;