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, excluding planes, to be included in each DRM lease.
38 * Each lease needs at least a CRTC and conector. */
39 #define DRM_LEASE_MIN_RES (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_lease_name(struct lm *lm, drmModeConnectorPtr connector)
95 uint32_t type = connector->connector_type;
96 uint32_t id = connector->connector_type_id;
98 if (type >= ARRAY_LENGTH(connector_type_names))
99 type = DRM_MODE_CONNECTOR_Unknown;
101 /* If the type is "Unknown", use the connector_id as the identify to
102 * guarantee that the name will be unique. */
103 if (type == DRM_MODE_CONNECTOR_Unknown)
104 id = connector->connector_id;
107 if (asprintf(&name, "card%d-%s-%d", minor(lm->dev_id),
108 connector_type_names[type], id) < 0)
114 static int drm_get_encoder_crtc_index(struct lm *lm, drmModeEncoderPtr encoder)
116 uint32_t crtc_id = encoder->crtc_id;
120 // The CRTC index only makes sense if it is less than the number of
121 // bits in the encoder possible_crtcs bitmap, which is 32.
122 assert(lm->drm_resource->count_crtcs < 32);
124 for (int i = 0; i < lm->drm_resource->count_crtcs; i++) {
125 if (lm->drm_resource->crtcs[i] == crtc_id)
131 static int drm_get_active_crtc_index(struct lm *lm,
132 drmModeConnectorPtr connector)
134 drmModeEncoder *encoder =
135 drmModeGetEncoder(lm->drm_fd, connector->encoder_id);
139 int crtc_idx = drm_get_encoder_crtc_index(lm, encoder);
140 drmModeFreeEncoder(encoder);
144 static int drm_get_crtc_index(struct lm *lm, drmModeConnectorPtr connector)
147 // try the active CRTC first
148 int crtc_index = drm_get_active_crtc_index(lm, connector);
149 if (crtc_index != -1)
152 // If not try the first available CRTC on the connector/encoder
153 for (int i = 0; i < connector->count_encoders; i++) {
154 drmModeEncoder *encoder =
155 drmModeGetEncoder(lm->drm_fd, connector->encoders[i]);
159 uint32_t usable_crtcs =
160 lm->available_crtcs & encoder->possible_crtcs;
161 int crtc = ffs(usable_crtcs);
162 drmModeFreeEncoder(encoder);
165 crtc_index = crtc - 1;
166 lm->available_crtcs &= ~(1 << crtc_index);
172 static void drm_find_available_crtcs(struct lm *lm)
174 // Assume all CRTCS are available by default,
175 lm->available_crtcs = ~0;
177 // then remove any that are in use. */
178 for (int i = 0; i < lm->drm_resource->count_encoders; i++) {
179 int enc_id = lm->drm_resource->encoders[i];
180 drmModeEncoderPtr enc = drmModeGetEncoder(lm->drm_fd, enc_id);
184 int crtc_idx = drm_get_encoder_crtc_index(lm, enc);
186 lm->available_crtcs &= ~(1 << crtc_idx);
188 drmModeFreeEncoder(enc);
192 static bool lease_add_planes(struct lm *lm, struct lease *lease, int crtc_index)
194 for (uint32_t i = 0; i < lm->drm_plane_resource->count_planes; i++) {
195 uint32_t plane_id = lm->drm_plane_resource->planes[i];
196 drmModePlanePtr plane = drmModeGetPlane(lm->drm_fd, plane_id);
200 // Exclude planes that can be used with multiple CRTCs for now
201 if (plane->possible_crtcs == (1u << crtc_index)) {
202 lease->object_ids[lease->nobject_ids++] = plane_id;
204 drmModeFreePlane(plane);
210 * Wait for a client to update the DRM framebuffer on the CRTC managed by
211 * a lease. Once the framebuffer has been updated, it is safe to close
212 * the fd associated with the previous lease client, freeing the previous
213 * framebuffer if there are no other references to it. */
214 static void wait_for_fb_update(struct lease *lease, uint32_t old_fb)
216 uint32_t current_fb = old_fb;
218 struct pollfd drm_poll = {
219 .fd = lease->lease_fd,
223 while (current_fb == old_fb) {
225 if (poll(&drm_poll, 1, -1) < 0) {
231 crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
232 current_fb = crtc->buffer_id;
233 drmModeFreeCrtc(crtc);
237 struct transition_ctx {
243 static void transition_done(void *arg)
245 struct transition_ctx *ctx = arg;
246 close(ctx->close_fd);
250 static void *finish_transition_task(void *arg)
252 struct transition_ctx *ctx = arg;
253 pthread_cleanup_push(transition_done, ctx);
254 wait_for_fb_update(ctx->lease, ctx->old_fb);
255 pthread_cleanup_pop(true);
259 static void close_after_lease_transition(struct lease *lease, int close_fd)
261 struct transition_ctx *ctx = calloc(1, sizeof(*ctx));
265 drmModeCrtcPtr crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
268 ctx->close_fd = close_fd;
269 ctx->old_fb = crtc->buffer_id;
271 drmModeFreeCrtc(crtc);
273 int ret = pthread_create(&lease->transition_tid, NULL,
274 finish_transition_task, ctx);
276 lease->transition_running = (ret == 0);
279 static void cancel_lease_transition_thread(struct lease *lease)
282 if (lease->transition_running) {
283 pthread_cancel(lease->transition_tid);
284 pthread_join(lease->transition_tid, NULL);
287 lease->transition_running = false;
290 static void lease_free(struct lease *lease)
292 free(lease->base.name);
293 free(lease->object_ids);
297 static struct lease *lease_create(struct lm *lm, drmModeConnectorPtr connector)
299 struct lease *lease = calloc(1, sizeof(struct lease));
301 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
305 lease->base.name = drm_create_lease_name(lm, connector);
306 if (!lease->base.name) {
307 DEBUG_LOG("Can't create lease name: %s\n", strerror(errno));
311 int nobjects = lm->drm_plane_resource->count_planes + DRM_LEASE_MIN_RES;
312 lease->object_ids = calloc(nobjects, sizeof(uint32_t));
313 if (!lease->object_ids) {
314 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
318 int crtc_index = drm_get_crtc_index(lm, connector);
319 if (crtc_index < 0) {
320 DEBUG_LOG("No crtc found for connector: %s\n",
325 if (!lease_add_planes(lm, lease, crtc_index))
328 uint32_t crtc_id = lm->drm_resource->crtcs[crtc_index];
329 lease->crtc_id = crtc_id;
330 lease->object_ids[lease->nobject_ids++] = crtc_id;
331 lease->object_ids[lease->nobject_ids++] = connector->connector_id;
333 lease->is_granted = false;
334 lease->lease_fd = -1;
343 struct lm *lm_create(const char *device)
345 struct lm *lm = calloc(1, sizeof(struct lm));
347 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
350 lm->drm_fd = open(device, O_RDWR);
351 if (lm->drm_fd < 0) {
352 ERROR_LOG("Cannot open DRM device (%s): %s\n", device,
357 lm->drm_resource = drmModeGetResources(lm->drm_fd);
358 if (!lm->drm_resource) {
359 ERROR_LOG("Invalid DRM device(%s)\n", device);
360 DEBUG_LOG("drmModeGetResources failed: %s\n", strerror(errno));
364 lm->drm_plane_resource = drmModeGetPlaneResources(lm->drm_fd);
365 if (!lm->drm_plane_resource) {
366 DEBUG_LOG("drmModeGetPlaneResources failed: %s\n",
372 if (fstat(lm->drm_fd, &st) < 0 || !S_ISCHR(st.st_mode)) {
373 DEBUG_LOG("%s is not a valid device file\n", device);
377 lm->dev_id = st.st_rdev;
379 int num_leases = lm->drm_resource->count_connectors;
381 lm->leases = calloc(num_leases, sizeof(struct lease *));
383 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
387 drm_find_available_crtcs(lm);
389 for (int i = 0; i < num_leases; i++) {
390 uint32_t connector_id = lm->drm_resource->connectors[i];
391 drmModeConnectorPtr connector =
392 drmModeGetConnector(lm->drm_fd, connector_id);
397 struct lease *lease = lease_create(lm, connector);
398 drmModeFreeConnector(connector);
403 lm->leases[lm->nleases] = lease;
406 if (lm->nleases == 0)
416 void lm_destroy(struct lm *lm)
420 for (int i = 0; i < lm->nleases; i++) {
421 struct lease_handle *lease_handle = &lm->leases[i]->base;
422 lm_lease_revoke(lm, lease_handle);
423 lm_lease_close(lease_handle);
424 lease_free(lm->leases[i]);
428 drmModeFreeResources(lm->drm_resource);
429 drmModeFreePlaneResources(lm->drm_plane_resource);
434 int lm_get_lease_handles(struct lm *lm, struct lease_handle ***handles)
439 *handles = (struct lease_handle **)lm->leases;
443 int lm_lease_grant(struct lm *lm, struct lease_handle *handle)
448 struct lease *lease = (struct lease *)handle;
449 if (lease->is_granted) {
450 /* Lease is already claimed */
455 drmModeCreateLease(lm->drm_fd, lease->object_ids,
456 lease->nobject_ids, 0, &lease->lessee_id);
458 ERROR_LOG("drmModeCreateLease failed on lease %s: %s\n",
459 lease->base.name, strerror(errno));
463 lease->is_granted = true;
465 int old_lease_fd = lease->lease_fd;
466 lease->lease_fd = lease_fd;
468 if (old_lease_fd >= 0)
469 close_after_lease_transition(lease, old_lease_fd);
474 int lm_lease_transfer(struct lm *lm, struct lease_handle *handle)
479 struct lease *lease = (struct lease *)handle;
480 if (!lease->is_granted)
483 lm_lease_revoke(lm, handle);
484 if (lm_lease_grant(lm, handle) < 0) {
485 lm_lease_close(handle);
489 return lease->lease_fd;
492 void lm_lease_revoke(struct lm *lm, struct lease_handle *handle)
497 struct lease *lease = (struct lease *)handle;
499 if (!lease->is_granted)
502 drmModeRevokeLease(lm->drm_fd, lease->lessee_id);
503 cancel_lease_transition_thread(lease);
504 lease->is_granted = false;
507 void lm_lease_close(struct lease_handle *handle)
511 struct lease *lease = (struct lease *)handle;
512 close(lease->lease_fd);
513 lease->lease_fd = -1;