7147078dc43c468eac709459c01e0006afdca9fb
[src/drm-lease-manager.git] / drm-lease-manager / lease-manager.c
1 /* Copyright 2020-2021 IGEL Co., Ltd.
2  *
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
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15
16 #define _GNU_SOURCE
17 #include "lease-manager.h"
18
19 #include "drm-lease.h"
20 #include "log.h"
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <poll.h>
26 #include <pthread.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <sys/stat.h>
32 #include <sys/sysmacros.h>
33 #include <unistd.h>
34 #include <xf86drm.h>
35 #include <xf86drmMode.h>
36
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)
40
41 #define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0]))
42
43 struct lease {
44         struct lease_handle base;
45
46         bool is_granted;
47         uint32_t lessee_id;
48         int lease_fd;
49
50         uint32_t *object_ids;
51         int nobject_ids;
52
53         /* for lease transfer completion */
54         uint32_t crtc_id;
55         pthread_t transition_tid;
56         bool transition_running;
57 };
58
59 struct lm {
60         int drm_fd;
61         dev_t dev_id;
62
63         drmModeResPtr drm_resource;
64         drmModePlaneResPtr drm_plane_resource;
65         uint32_t available_crtcs;
66
67         char **connector_names;
68         int nconnectors;
69
70         struct lease **leases;
71         int nleases;
72 };
73
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",
94 };
95
96 static char *drm_create_connector_name(drmModeConnectorPtr connector)
97 {
98         uint32_t type = connector->connector_type;
99         uint32_t id = connector->connector_type_id;
100
101         if (type >= ARRAY_LENGTH(connector_type_names))
102                 type = DRM_MODE_CONNECTOR_Unknown;
103
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;
108
109         char *name;
110         if (asprintf(&name, "%s-%d", connector_type_names[type], id) < 0)
111                 return NULL;
112
113         return name;
114 }
115
116 static char *drm_create_default_lease_name(struct lm *lm, int cindex)
117 {
118         char *connector_name = lm->connector_names[cindex];
119
120         char *name;
121         if (asprintf(&name, "card%d-%s", minor(lm->dev_id), connector_name) < 0)
122                 return NULL;
123
124         return name;
125 }
126
127 static int drm_get_encoder_crtc_index(struct lm *lm, drmModeEncoderPtr encoder)
128 {
129         uint32_t crtc_id = encoder->crtc_id;
130         if (!crtc_id)
131                 return -1;
132
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);
136
137         for (int i = 0; i < lm->drm_resource->count_crtcs; i++) {
138                 if (lm->drm_resource->crtcs[i] == crtc_id)
139                         return i;
140         }
141         return -1;
142 }
143
144 static int drm_get_active_crtc_index(struct lm *lm,
145                                      drmModeConnectorPtr connector)
146 {
147         drmModeEncoder *encoder =
148             drmModeGetEncoder(lm->drm_fd, connector->encoder_id);
149         if (!encoder)
150                 return -1;
151
152         int crtc_idx = drm_get_encoder_crtc_index(lm, encoder);
153         drmModeFreeEncoder(encoder);
154         return crtc_idx;
155 }
156
157 static int drm_get_crtc_index(struct lm *lm, drmModeConnectorPtr connector)
158 {
159
160         // try the active CRTC first
161         int crtc_index = drm_get_active_crtc_index(lm, connector);
162         if (crtc_index != -1)
163                 return crtc_index;
164
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]);
169
170                 assert(encoder);
171
172                 uint32_t usable_crtcs =
173                     lm->available_crtcs & encoder->possible_crtcs;
174                 int crtc = ffs(usable_crtcs);
175                 drmModeFreeEncoder(encoder);
176                 if (crtc == 0)
177                         continue;
178                 crtc_index = crtc - 1;
179                 lm->available_crtcs &= ~(1 << crtc_index);
180                 break;
181         }
182         return crtc_index;
183 }
184
185 static void drm_find_available_crtcs(struct lm *lm)
186 {
187         // Assume all CRTCS are available by default,
188         lm->available_crtcs = ~0;
189
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);
194                 if (!enc)
195                         continue;
196
197                 int crtc_idx = drm_get_encoder_crtc_index(lm, enc);
198                 if (crtc_idx >= 0)
199                         lm->available_crtcs &= ~(1 << crtc_idx);
200
201                 drmModeFreeEncoder(enc);
202         }
203 }
204
205 static bool drm_find_connector(struct lm *lm, char *name, uint32_t *id)
206 {
207         int connectors = lm->drm_resource->count_connectors;
208
209         for (int i = 0; i < connectors; i++) {
210                 if (strcmp(lm->connector_names[i], name))
211                         continue;
212                 if (id)
213                         *id = lm->drm_resource->connectors[i];
214                 return true;
215         }
216         return false;
217 }
218
219 static void config_get_planes(struct lm *lm,
220                               const struct connector_config *config,
221                               int *nplanes, uint32_t **planes)
222 {
223         if (config && config->planes) {
224                 *nplanes = config->nplanes;
225                 *planes = config->planes;
226         } else {
227                 *nplanes = (int)lm->drm_plane_resource->count_planes;
228                 *planes = lm->drm_plane_resource->planes;
229         }
230 }
231
232 static bool lease_add_planes(struct lm *lm, struct lease *lease,
233                              uint32_t crtc_index,
234                              const struct connector_config *con_config)
235 {
236         int nplanes;
237         uint32_t *planes;
238         uint32_t crtc_mask = (1 << crtc_index);
239
240         /* Only allow shared planes when plane list is explicitly set */
241         bool allow_shared = con_config && con_config->planes;
242
243         config_get_planes(lm, con_config, &nplanes, &planes);
244
245         for (int i = 0; i < nplanes; i++) {
246                 uint32_t plane_id = planes[i];
247                 drmModePlanePtr plane = drmModeGetPlane(lm->drm_fd, plane_id);
248
249                 if (!plane) {
250                         ERROR_LOG(
251                             "Unknown plane id %d configured in lease: %s\n",
252                             plane_id, lease->base.name);
253                         return false;
254                 }
255
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++] =
260                                     plane_id;
261                 }
262                 drmModeFreePlane(plane);
263         }
264         return true;
265 }
266
267 /* Lease transition
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)
273 {
274         uint32_t current_fb = old_fb;
275
276         struct pollfd drm_poll = {
277             .fd = lease->lease_fd,
278             .events = POLLIN,
279         };
280
281         while (current_fb == old_fb) {
282                 drmModeCrtcPtr crtc;
283                 if (poll(&drm_poll, 1, -1) < 0) {
284                         if (errno == EINTR)
285                                 continue;
286                         break;
287                 }
288
289                 crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
290                 current_fb = crtc->buffer_id;
291                 drmModeFreeCrtc(crtc);
292         }
293 }
294
295 struct transition_ctx {
296         struct lease *lease;
297         int close_fd;
298         uint32_t old_fb;
299 };
300
301 static void transition_done(void *arg)
302 {
303         struct transition_ctx *ctx = arg;
304         close(ctx->close_fd);
305         free(ctx);
306 }
307
308 static void *finish_transition_task(void *arg)
309 {
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);
314         return NULL;
315 }
316
317 static void close_after_lease_transition(struct lease *lease, int close_fd)
318 {
319         struct transition_ctx *ctx = calloc(1, sizeof(*ctx));
320
321         assert(ctx);
322
323         drmModeCrtcPtr crtc = drmModeGetCrtc(lease->lease_fd, lease->crtc_id);
324
325         ctx->lease = lease;
326         ctx->close_fd = close_fd;
327         ctx->old_fb = crtc->buffer_id;
328
329         drmModeFreeCrtc(crtc);
330
331         int ret = pthread_create(&lease->transition_tid, NULL,
332                                  finish_transition_task, ctx);
333
334         lease->transition_running = (ret == 0);
335 }
336
337 static void cancel_lease_transition_thread(struct lease *lease)
338 {
339
340         if (lease->transition_running) {
341                 pthread_cancel(lease->transition_tid);
342                 pthread_join(lease->transition_tid, NULL);
343         }
344
345         lease->transition_running = false;
346 }
347
348 static void lease_free(struct lease *lease)
349 {
350         free(lease->base.name);
351         free(lease->object_ids);
352         free(lease);
353 }
354
355 static struct lease *lease_create(struct lm *lm,
356                                   const struct lease_config *config)
357 {
358         struct lease *lease;
359
360         if (!config->lease_name) {
361                 ERROR_LOG("Mising lease name\n");
362                 return NULL;
363         }
364
365         lease = calloc(1, sizeof(struct lease));
366         if (!lease) {
367                 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
368                 return NULL;
369         }
370
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));
374                 goto err;
375         }
376
377         int nconnectors =
378             config->nconnectors > 0 ? config->nconnectors : config->ncids;
379         int nobjects = lm->drm_plane_resource->count_planes +
380                        nconnectors * DRM_OBJECTS_PER_CONNECTOR;
381
382         lease->object_ids = calloc(nobjects, sizeof(uint32_t));
383         if (!lease->object_ids) {
384                 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
385                 goto err;
386         }
387
388         for (int i = 0; i < nconnectors; i++) {
389                 uint32_t cid;
390                 struct connector_config *con_config = NULL;
391
392                 if (config->nconnectors > 0)
393                         con_config = &config->connectors[i];
394
395                 if (con_config) {
396                         char *connector_name = con_config->name;
397                         bool optional = con_config->optional;
398
399                         bool found =
400                             drm_find_connector(lm, connector_name, &cid);
401
402                         bool missing_mandatory = !found && !optional;
403                         bool missing_optional = !found && optional;
404
405                         if (missing_mandatory) {
406                                 ERROR_LOG("Lease: %s, "
407                                           "mandatory connector %s not found\n",
408                                           config->lease_name, connector_name);
409                                 goto err;
410                         } else if (missing_optional) {
411                                 WARN_LOG("Lease: %s, "
412                                          "unknown DRM connector: %s\n",
413                                          config->lease_name, connector_name);
414                                 continue;
415                         }
416                 } else {
417                         cid = config->connector_ids[i];
418                 }
419
420                 drmModeConnectorPtr connector =
421                     drmModeGetConnector(lm->drm_fd, cid);
422
423                 if (connector == NULL) {
424                         ERROR_LOG("Can't find connector id: %d\n", cid);
425                         goto err;
426                 }
427
428                 int crtc_index = drm_get_crtc_index(lm, connector);
429
430                 drmModeFreeConnector(connector);
431
432                 if (crtc_index < 0) {
433                         DEBUG_LOG("No crtc found for connector: %d, lease %s\n",
434                                   cid, lease->base.name);
435                         goto err;
436                 }
437
438                 if (!lease_add_planes(lm, lease, crtc_index, con_config))
439                         goto err;
440
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;
445         }
446         lease->is_granted = false;
447         lease->lease_fd = -1;
448
449         return lease;
450
451 err:
452         lease_free(lease);
453         return NULL;
454 }
455
456 static void destroy_default_lease_configs(int num_configs,
457                                           struct lease_config *configs)
458 {
459         for (int i = 0; i < num_configs; i++) {
460                 free(configs[i].connector_ids);
461                 free(configs[i].lease_name);
462         }
463
464         free(configs);
465 }
466
467 static int create_default_lease_configs(struct lm *lm,
468                                         struct lease_config **configs)
469 {
470         struct lease_config *def_configs;
471         int num_configs = lm->drm_resource->count_connectors;
472
473         if (num_configs < 0)
474                 return -1;
475
476         def_configs = calloc(num_configs, sizeof(*def_configs));
477         if (!def_configs) {
478                 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
479                 return -1;
480         }
481
482         for (int i = 0; i < num_configs; i++) {
483                 uint32_t cid = lm->drm_resource->connectors[i];
484
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",
488                                   strerror(errno));
489                         goto err;
490                 }
491
492                 def_configs[i].lease_name =
493                     drm_create_default_lease_name(lm, i);
494
495                 def_configs[i].connector_ids[0] = cid;
496                 def_configs[i].ncids = 1;
497         }
498
499         *configs = def_configs;
500         return num_configs;
501
502 err:
503         destroy_default_lease_configs(num_configs, def_configs);
504         return -1;
505 }
506
507 static struct lm *drm_device_get_resources(const char *device,
508                                            bool universal_plane)
509 {
510         struct lm *lm = calloc(1, sizeof(struct lm));
511         uint64_t value = 0;
512
513         if (!lm) {
514                 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
515                 return NULL;
516         }
517         lm->drm_fd = open(device, O_RDWR);
518         if (lm->drm_fd < 0) {
519                 ERROR_LOG("Cannot open DRM device (%s): %s\n", device,
520                           strerror(errno));
521                 goto err;
522         }
523
524         /* When -u option set at args. Enable universal planes so that ALL
525            planes, even primary and cursor planes can be assigned from lease
526            configurations. */
527         if (universal_plane == true) {
528                 DEBUG_LOG("Enable universal plean mode.\n");
529                 value = 1;
530         }
531
532         if (drmSetClientCap(lm->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES,
533                             value)) {
534                 DEBUG_LOG("drmSetClientCap failed\n");
535                 goto err;
536         }
537
538         lm->drm_resource = drmModeGetResources(lm->drm_fd);
539         if (!lm->drm_resource) {
540                 ERROR_LOG("Invalid DRM device(%s)\n", device);
541                 DEBUG_LOG("drmModeGetResources failed: %s\n", strerror(errno));
542                 goto err;
543         }
544
545         if (lm->drm_resource->count_connectors <= 0 ||
546             lm->drm_resource->count_crtcs <= 0 ||
547             lm->drm_resource->count_encoders <= 0) {
548                 DEBUG_LOG("Insufficient DRM resources on device(%s)\n", device);
549                 goto err;
550         }
551
552         lm->drm_plane_resource = drmModeGetPlaneResources(lm->drm_fd);
553         if (!lm->drm_plane_resource) {
554                 DEBUG_LOG("drmModeGetPlaneResources failed: %s\n",
555                           strerror(errno));
556                 goto err;
557         }
558
559         struct stat st;
560         if (fstat(lm->drm_fd, &st) < 0 || !S_ISCHR(st.st_mode)) {
561                 DEBUG_LOG("%s is not a valid device file\n", device);
562                 goto err;
563         }
564
565         lm->dev_id = st.st_rdev;
566
567         lm->nconnectors = lm->drm_resource->count_connectors;
568         lm->connector_names = calloc(lm->nconnectors, sizeof(char *));
569
570         for (int i = 0; i < lm->nconnectors; i++) {
571                 drmModeConnectorPtr connector;
572                 uint32_t cid = lm->drm_resource->connectors[i];
573
574                 connector = drmModeGetConnector(lm->drm_fd, cid);
575                 lm->connector_names[i] = drm_create_connector_name(connector);
576                 drmModeFreeConnector(connector);
577
578                 if (!lm->connector_names[i]) {
579                         DEBUG_LOG("Can't create name for connector %d: %s\n",
580                                   cid, strerror(errno));
581                         goto err;
582                 }
583         }
584         return lm;
585 err:
586         lm_destroy(lm);
587         return NULL;
588 }
589
590 static struct lm *drm_find_drm_device(const char *device, bool universal_plane)
591 {
592         drmDevicePtr devices[64];
593         int ndevs;
594         struct lm *lm = NULL;
595
596         if (device)
597                 return drm_device_get_resources(device, universal_plane);
598
599         ndevs = drmGetDevices2(0, devices, 64);
600
601         for (int i = 0; i < ndevs; i++) {
602                 lm = drm_device_get_resources(
603                     devices[i]->nodes[DRM_NODE_PRIMARY], universal_plane);
604                 if (lm)
605                         break;
606         }
607
608         drmFreeDevices(devices, ndevs);
609
610         return lm;
611 }
612
613 static int lm_create_leases(struct lm *lm, int num_leases,
614                             const struct lease_config *configs)
615 {
616         lm->leases = calloc(num_leases, sizeof(struct lease *));
617         if (!lm->leases) {
618                 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
619                 return -1;
620         }
621
622         drm_find_available_crtcs(lm);
623
624         for (int i = 0; i < num_leases; i++) {
625                 struct lease *lease = lease_create(lm, &configs[i]);
626                 if (!lease)
627                         continue;
628
629                 lm->leases[lm->nleases] = lease;
630                 lm->nleases++;
631         }
632         if (lm->nleases == 0)
633                 return -1;
634
635         return 0;
636 }
637
638 struct lm *lm_create_with_config(const char *device, int num_leases,
639                                  struct lease_config *configs,
640                                  bool universal_plane)
641 {
642         struct lease_config *default_configs = NULL;
643         struct lm *lm = drm_find_drm_device(device, universal_plane);
644
645         if (!lm) {
646                 ERROR_LOG("No available DRM device found\n");
647                 return NULL;
648         }
649
650         if (configs == NULL || num_leases == 0) {
651                 num_leases = create_default_lease_configs(lm, &default_configs);
652                 if (num_leases < 0) {
653                         lm_destroy(lm);
654                         ERROR_LOG("DRM connector enumeration failed\n");
655                         return NULL;
656                 }
657                 configs = default_configs;
658         }
659
660         if (lm_create_leases(lm, num_leases, configs) < 0) {
661                 lm_destroy(lm);
662                 lm = NULL;
663         }
664
665         if (default_configs)
666                 destroy_default_lease_configs(num_leases, default_configs);
667         return lm;
668 }
669
670 struct lm *lm_create(const char *device)
671 {
672         return lm_create_with_config(device, 0, NULL, false);
673 }
674
675 void lm_destroy(struct lm *lm)
676 {
677         assert(lm);
678
679         for (int i = 0; i < lm->nleases; i++) {
680                 struct lease_handle *lease_handle = &lm->leases[i]->base;
681                 lm_lease_revoke(lm, lease_handle);
682                 lm_lease_close(lease_handle);
683                 lease_free(lm->leases[i]);
684         }
685
686         free(lm->leases);
687
688         for (int i = 0; i < lm->nconnectors; i++)
689                 free(lm->connector_names[i]);
690         free(lm->connector_names);
691
692         drmModeFreeResources(lm->drm_resource);
693         drmModeFreePlaneResources(lm->drm_plane_resource);
694         close(lm->drm_fd);
695         free(lm);
696 }
697
698 int lm_get_lease_handles(struct lm *lm, struct lease_handle ***handles)
699 {
700         assert(lm);
701         assert(handles);
702
703         *handles = (struct lease_handle **)lm->leases;
704         return lm->nleases;
705 }
706
707 int lm_lease_grant(struct lm *lm, struct lease_handle *handle)
708 {
709         assert(lm);
710         assert(handle);
711
712         struct lease *lease = (struct lease *)handle;
713         if (lease->is_granted) {
714                 /* Lease is already claimed */
715                 return -1;
716         }
717
718         int lease_fd =
719             drmModeCreateLease(lm->drm_fd, lease->object_ids,
720                                lease->nobject_ids, 0, &lease->lessee_id);
721         if (lease_fd < 0) {
722                 ERROR_LOG("drmModeCreateLease failed on lease %s: %s\n",
723                           lease->base.name, strerror(errno));
724                 return -1;
725         }
726
727         lease->is_granted = true;
728
729         int old_lease_fd = lease->lease_fd;
730         lease->lease_fd = lease_fd;
731
732         if (old_lease_fd >= 0)
733                 close_after_lease_transition(lease, old_lease_fd);
734
735         return lease_fd;
736 }
737
738 int lm_lease_transfer(struct lm *lm, struct lease_handle *handle)
739 {
740         assert(lm);
741         assert(handle);
742
743         struct lease *lease = (struct lease *)handle;
744         if (!lease->is_granted)
745                 return -1;
746
747         lm_lease_revoke(lm, handle);
748         if (lm_lease_grant(lm, handle) < 0) {
749                 lm_lease_close(handle);
750                 return -1;
751         }
752
753         return lease->lease_fd;
754 }
755
756 void lm_lease_revoke(struct lm *lm, struct lease_handle *handle)
757 {
758         assert(lm);
759         assert(handle);
760
761         struct lease *lease = (struct lease *)handle;
762
763         if (!lease->is_granted)
764                 return;
765
766         drmModeRevokeLease(lm->drm_fd, lease->lessee_id);
767         cancel_lease_transition_thread(lease);
768         lease->is_granted = false;
769 }
770
771 void lm_lease_close(struct lease_handle *handle)
772 {
773         assert(handle);
774
775         struct lease *lease = (struct lease *)handle;
776         if (lease->lease_fd >= 0)
777                 close(lease->lease_fd);
778         lease->lease_fd = -1;
779 }