1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <systemd/sd-bus.h>
27 #include <systemd/sd-event.h>
28 #include <systemd/sd-login.h>
30 #include "grdev-internal.h"
32 #include "login-shared.h"
34 #include "udev-util.h"
37 static void pipe_enable(grdev_pipe *pipe);
38 static void pipe_disable(grdev_pipe *pipe);
39 static void card_modified(grdev_card *card);
40 static void session_frame(grdev_session *session, grdev_display *display);
46 static inline grdev_tile *tile_leftmost(grdev_tile *tile) {
50 while (tile->type == GRDEV_TILE_NODE && tile->node.child_list)
51 tile = tile->node.child_list;
56 #define TILE_FOREACH(_root, _i) \
57 for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->childs_by_node_next) ? : _i->parent)
59 #define TILE_FOREACH_SAFE(_root, _i, _next) \
60 for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->childs_by_node_next) ? : _i->parent), true); _i = _next)
62 static void tile_link(grdev_tile *tile, grdev_tile *parent) {
63 grdev_display *display;
67 assert(!tile->parent);
68 assert(!tile->display);
70 assert(parent->type == GRDEV_TILE_NODE);
72 display = parent->display;
74 assert(!display || !display->enabled);
76 ++parent->node.n_childs;
77 LIST_PREPEND(childs_by_node, parent->node.child_list, tile);
78 tile->parent = parent;
81 display->modified = true;
82 TILE_FOREACH(tile, t) {
84 if (t->type == GRDEV_TILE_LEAF) {
87 pipe_enable(t->leaf.pipe);
93 static void tile_unlink(grdev_tile *tile) {
94 grdev_tile *parent, *t;
95 grdev_display *display;
99 display = tile->display;
100 parent = tile->parent;
106 assert(parent->type == GRDEV_TILE_NODE);
107 assert(parent->display == display);
108 assert(parent->node.n_childs > 0);
110 --parent->node.n_childs;
111 LIST_REMOVE(childs_by_node, parent->node.child_list, tile);
115 display->modified = true;
116 TILE_FOREACH(tile, t) {
118 if (t->type == GRDEV_TILE_LEAF) {
120 t->leaf.pipe->cache = NULL;
121 pipe_disable(t->leaf.pipe);
126 /* Tile trees are driven by leafs. Internal nodes have no owner, thus,
127 * we must take care to not leave them around. Therefore, whenever we
128 * unlink any part of a tree, we also destroy the parent, in case it's
130 * Parents are stale if they have no childs and either have no display
131 * or if they are intermediate nodes (i.e, they have a parent).
132 * This means, you can easily create trees, but you can never partially
133 * move or destruct them so far. They're always reduced to minimal form
134 * if you cut them. This might change later, but so far we didn't need
135 * partial destruction or the ability to move whole trees. */
137 if (parent->node.n_childs < 1 && (parent->parent || !parent->display))
138 grdev_tile_free(parent);
141 static int tile_new(grdev_tile **out) {
142 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
146 tile = new0(grdev_tile, 1);
150 tile->type = (unsigned)-1;
157 int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe) {
158 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
161 assert_return(pipe, -EINVAL);
162 assert_return(!pipe->tile, -EINVAL);
168 tile->type = GRDEV_TILE_LEAF;
169 tile->leaf.pipe = pipe;
177 int grdev_tile_new_node(grdev_tile **out) {
178 _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
181 assert_return(out, -EINVAL);
187 tile->type = GRDEV_TILE_NODE;
194 grdev_tile *grdev_tile_free(grdev_tile *tile) {
200 switch (tile->type) {
201 case GRDEV_TILE_LEAF:
202 assert(!tile->parent);
203 assert(!tile->display);
204 assert(tile->leaf.pipe);
207 case GRDEV_TILE_NODE:
208 assert(!tile->parent);
209 assert(!tile->display);
210 assert(tile->node.n_childs == 0);
220 grdev_display *grdev_find_display(grdev_session *session, const char *name) {
221 assert_return(session, NULL);
222 assert_return(name, NULL);
224 return hashmap_get(session->display_map, name);
227 int grdev_display_new(grdev_display **out, grdev_session *session, const char *name) {
228 _cleanup_(grdev_display_freep) grdev_display *display = NULL;
234 display = new0(grdev_display, 1);
238 display->session = session;
240 display->name = strdup(name);
244 r = grdev_tile_new_node(&display->tile);
248 display->tile->display = display;
250 r = hashmap_put(session->display_map, display->name, display);
260 grdev_display *grdev_display_free(grdev_display *display) {
264 assert(!display->public);
265 assert(!display->enabled);
266 assert(!display->modified);
267 assert(display->n_leafs == 0);
268 assert(display->n_pipes == 0);
271 hashmap_remove_value(display->session->display_map, display->name, display);
274 display->tile->display = NULL;
275 grdev_tile_free(display->tile);
278 free(display->pipes);
285 bool grdev_display_is_enabled(grdev_display *display) {
286 return display && display->enabled;
289 void grdev_display_enable(grdev_display *display) {
294 if (!display->enabled) {
295 display->enabled = true;
296 TILE_FOREACH(display->tile, t)
297 if (t->type == GRDEV_TILE_LEAF)
298 pipe_enable(t->leaf.pipe);
302 void grdev_display_disable(grdev_display *display) {
307 if (display->enabled) {
308 display->enabled = false;
309 TILE_FOREACH(display->tile, t)
310 if (t->type == GRDEV_TILE_LEAF)
311 pipe_disable(t->leaf.pipe);
315 const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev, uint64_t minage) {
316 grdev_display_cache *cache;
319 assert_return(display, NULL);
320 assert_return(!display->modified, NULL);
321 assert_return(display->enabled, NULL);
324 cache = container_of(prev, grdev_display_cache, target);
327 assert(cache->pipe->tile->display == display);
328 assert(display->pipes >= cache);
330 idx = (cache - display->pipes) / sizeof(*cache) + 1;
335 for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) {
336 grdev_display_target *target;
341 target = &cache->target;
343 if (!pipe->running || !pipe->enabled)
346 /* if front-buffer is up-to-date, there's nothing to do */
347 if (minage > 0 && pipe->front && pipe->front->age >= minage)
350 /* find suitable back-buffer */
351 if (!(fb = pipe->back)) {
352 if (!pipe->vtable->target || !(fb = pipe->vtable->target(pipe)))
356 /* if back-buffer is up-to-date, schedule flip */
357 if (minage > 0 && fb->age >= minage) {
358 grdev_display_flip_target(display, target, fb->age);
362 /* we have an out-of-date back-buffer; return for redraw */
370 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
371 grdev_display_cache *cache;
375 assert(!display->modified);
376 assert(display->enabled);
380 cache = container_of(target, grdev_display_cache, target);
383 assert(cache->pipe->tile->display == display);
385 /* reset age of all FB on overflow */
386 if (age < target->fb->age)
387 for (i = 0; i < cache->pipe->max_fbs; ++i)
388 if (cache->pipe->fbs[i])
389 cache->pipe->fbs[i]->age = 0;
391 ((grdev_fb*)target->fb)->age = age;
392 cache->pipe->flip = true;
395 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
396 uint32_t x, y, width, height;
397 grdev_display_target *t;
401 assert(l->cache_w >= c->target.width + c->target.x);
402 assert(l->cache_h >= c->target.height + c->target.y);
408 t->rotate = (t->rotate + l->rotate) & 0x3;
418 case GRDEV_ROTATE_90:
419 t->x = l->cache_h - (height + y);
424 case GRDEV_ROTATE_180:
425 t->x = l->cache_w - (width + x);
426 t->y = l->cache_h - (height + y);
428 case GRDEV_ROTATE_270:
430 t->y = l->cache_w - (width + x);
440 if (l->flip & GRDEV_FLIP_HORIZONTAL)
441 t->x = l->cache_w - (t->width + t->x);
442 if (l->flip & GRDEV_FLIP_VERTICAL)
443 t->y = l->cache_h - (t->height + t->y);
451 static void display_cache_targets(grdev_display *display) {
452 grdev_display_cache *c;
457 /* depth-first with childs before parent */
458 for (tile = tile_leftmost(display->tile);
460 tile = tile_leftmost(tile->childs_by_node_next) ? : tile->parent) {
461 if (tile->type == GRDEV_TILE_LEAF) {
464 /* We're at a leaf and no parent has been cached, yet.
465 * Copy the pipe information into the target cache and
466 * update our global pipe-caches if required. */
468 assert(tile->leaf.pipe);
469 assert(display->n_pipes + 1 <= display->max_pipes);
472 c = &display->pipes[display->n_pipes++];
477 c->target.width = p->width;
478 c->target.height = p->height;
479 tile->cache_w = p->width;
480 tile->cache_h = p->height;
482 /* all new tiles are incomplete due to geometry changes */
483 c->incomplete = true;
485 display_cache_apply(c, tile);
487 grdev_tile *child, *l;
489 /* We're now at a node with all it's childs already
490 * computed (depth-first, child before parent). We
491 * first need to know the size of our tile, then we
492 * recurse into all leafs and update their cache. */
497 LIST_FOREACH(childs_by_node, child, tile->node.child_list) {
498 if (child->x + child->cache_w > tile->cache_w)
499 tile->cache_w = child->x + child->cache_w;
500 if (child->y + child->cache_h > tile->cache_h)
501 tile->cache_h = child->y + child->cache_h;
504 assert(tile->cache_w > 0);
505 assert(tile->cache_h > 0);
507 TILE_FOREACH(tile, l)
508 if (l->type == GRDEV_TILE_LEAF)
509 display_cache_apply(l->leaf.pipe->cache, tile);
514 static bool display_cache(grdev_display *display) {
522 if (!display->modified)
525 display->modified = false;
526 display->framed = false;
527 display->n_pipes = 0;
531 if (display->n_leafs < 1)
534 TILE_FOREACH(display->tile, tile)
535 if (tile->type == GRDEV_TILE_LEAF)
536 tile->leaf.pipe->cache = NULL;
538 if (display->n_leafs > display->max_pipes) {
539 n = ALIGN_POWER2(display->n_leafs);
545 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
552 display->max_pipes = n;
555 display_cache_targets(display);
561 log_debug("grdev: %s/%s: cannot cache pipes: %s",
562 display->session->name, display->name, strerror(-r));
570 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
571 assert_return(card, NULL);
572 assert_return(name, NULL);
574 return hashmap_get(card->pipe_map, name);
577 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
580 assert_return(pipe, -EINVAL);
581 assert_return(pipe->vtable, -EINVAL);
582 assert_return(pipe->vtable->free, -EINVAL);
583 assert_return(pipe->card, -EINVAL);
584 assert_return(pipe->card->session, -EINVAL);
585 assert_return(!pipe->cache, -EINVAL);
586 assert_return(pipe->width > 0, -EINVAL);
587 assert_return(pipe->height > 0, -EINVAL);
588 assert_return(!pipe->enabled, -EINVAL);
589 assert_return(!pipe->running, -EINVAL);
590 assert_return(name, -EINVAL);
592 pipe->name = strdup(name);
597 pipe->fbs = new0(grdev_fb*, n_fbs);
601 pipe->max_fbs = n_fbs;
604 r = grdev_tile_new_leaf(&pipe->tile, pipe);
608 r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
612 card_modified(pipe->card);
616 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
623 assert(pipe->vtable);
624 assert(pipe->vtable->free);
627 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
629 tile_unlink(pipe->tile);
631 assert(!pipe->cache);
634 pipe->vtable->free(pipe);
636 grdev_tile_free(tmp.tile);
637 card_modified(tmp.card);
644 static void pipe_enable(grdev_pipe *pipe) {
647 if (!pipe->enabled) {
648 pipe->enabled = true;
649 if (pipe->vtable->enable)
650 pipe->vtable->enable(pipe);
654 static void pipe_disable(grdev_pipe *pipe) {
658 pipe->enabled = false;
659 if (pipe->vtable->disable)
660 pipe->vtable->disable(pipe);
664 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
667 /* grdev_pipe_ready() is used by backends to notify about pipe state
668 * changed. If a pipe is ready, it can be fully used by us (available,
669 * enabled and accessable). Backends can disable pipes at any time
670 * (like for async revocation), but can only enable them from parent
671 * context. Otherwise, we might call user-callbacks recursively. */
673 if (pipe->running == running)
676 pipe->running = running;
678 /* runtime events for unused pipes are not interesting */
680 grdev_display *display = pipe->tile->display;
686 session_frame(display->session, display);
688 pipe->cache->incomplete = true;
693 void grdev_pipe_frame(grdev_pipe *pipe) {
694 grdev_display *display;
698 /* if pipe is unused, ignore any frame events */
702 display = pipe->tile->display;
706 session_frame(display->session, display);
713 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
714 assert_return(session, NULL);
715 assert_return(name, NULL);
717 return hashmap_get(session->card_map, name);
720 int grdev_card_add(grdev_card *card, const char *name) {
723 assert_return(card, -EINVAL);
724 assert_return(card->vtable, -EINVAL);
725 assert_return(card->vtable->free, -EINVAL);
726 assert_return(card->session, -EINVAL);
727 assert_return(name, -EINVAL);
729 card->name = strdup(name);
733 card->pipe_map = hashmap_new(&string_hash_ops);
737 r = hashmap_put(card->session->card_map, card->name, card);
744 grdev_card *grdev_card_free(grdev_card *card) {
750 assert(!card->enabled);
751 assert(card->vtable);
752 assert(card->vtable->free);
755 hashmap_remove_value(card->session->card_map, card->name, card);
758 card->vtable->free(card);
760 assert(hashmap_size(tmp.pipe_map) == 0);
762 hashmap_free(tmp.pipe_map);
768 static void card_modified(grdev_card *card) {
770 assert(card->session->n_pins > 0);
772 card->modified = true;
775 static void grdev_card_enable(grdev_card *card) {
778 if (!card->enabled) {
779 card->enabled = true;
780 if (card->vtable->enable)
781 card->vtable->enable(card);
785 static void grdev_card_disable(grdev_card *card) {
789 card->enabled = false;
790 if (card->vtable->disable)
791 card->vtable->disable(card);
799 static void session_raise(grdev_session *session, grdev_event *event) {
800 session->event_fn(session, session->userdata, event);
803 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
804 grdev_event event = {
805 .type = GRDEV_EVENT_DISPLAY_ADD,
811 session_raise(session, &event);
814 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
815 grdev_event event = {
816 .type = GRDEV_EVENT_DISPLAY_REMOVE,
822 session_raise(session, &event);
825 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
826 grdev_event event = {
827 .type = GRDEV_EVENT_DISPLAY_CHANGE,
833 session_raise(session, &event);
836 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
837 grdev_event event = {
838 .type = GRDEV_EVENT_DISPLAY_FRAME,
844 session_raise(session, &event);
847 static void session_add_card(grdev_session *session, grdev_card *card) {
851 log_debug("grdev: %s: add card '%s'", session->name, card->name);
853 /* Cards are not exposed to users, but managed internally. Cards are
854 * enabled if the session is enabled, and will track that state. The
855 * backend can probe the card at any time, but only if enabled. It
856 * will then add pipes according to hardware state.
857 * That is, the card may create pipes as soon as we enable it here. */
859 if (session->enabled)
860 grdev_card_enable(card);
863 static void session_remove_card(grdev_session *session, grdev_card *card) {
867 log_debug("grdev: %s: remove card '%s'", session->name, card->name);
869 /* As cards are not exposed, it can never be accessed by outside
870 * users and we can simply remove it. Disabling the card does not
871 * necessarily drop all pipes of the card. This is usually deferred
872 * to card destruction (as pipes are cached as long as FDs remain
873 * open). Therefore, the card destruction might cause pipes, and thus
874 * visible displays, to be removed. */
876 grdev_card_disable(card);
877 grdev_card_free(card);
880 static void session_add_display(grdev_session *session, grdev_display *display) {
883 assert(!display->enabled);
885 log_debug("grdev: %s: add display '%s'", session->name, display->name);
887 /* Displays are the main entity for public API users. We create them
888 * independent of card backends and they wrap any underlying display
889 * architecture. Displays are public at all times, thus, may be entered
890 * by outside users at any time. */
892 display->public = true;
893 session_raise_display_add(session, display);
896 static void session_remove_display(grdev_session *session, grdev_display *display) {
900 log_debug("grdev: %s: remove display '%s'", session->name, display->name);
902 /* Displays are public, so we have to be careful when removing them.
903 * We first tell users about their removal, disable them and then drop
904 * them. We now, after the notification, no external access will
905 * happen. Therefore, we can release the tiles afterwards safely. */
907 if (display->public) {
908 display->public = false;
909 session_raise_display_remove(session, display);
912 grdev_display_disable(display);
913 grdev_display_free(display);
916 static void session_change_display(grdev_session *session, grdev_display *display) {
922 changed = display_cache(display);
924 if (display->n_leafs == 0)
925 session_remove_display(session, display);
926 else if (!display->public)
927 session_add_display(session, display);
929 session_raise_display_change(session, display);
930 else if (display->framed)
931 session_frame(session, display);
934 static void session_frame(grdev_session *session, grdev_display *display) {
938 display->framed = false;
940 if (!display->enabled || !session->enabled)
943 if (session->n_pins > 0)
944 display->framed = true;
946 session_raise_display_frame(session, display);
949 int grdev_session_new(grdev_session **out,
950 grdev_context *context,
953 grdev_event_fn event_fn,
955 _cleanup_(grdev_session_freep) grdev_session *session = NULL;
962 assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
963 assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
964 assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
966 session = new0(grdev_session, 1);
970 session->context = grdev_context_ref(context);
971 session->custom = flags & GRDEV_SESSION_CUSTOM;
972 session->managed = flags & GRDEV_SESSION_MANAGED;
973 session->event_fn = event_fn;
974 session->userdata = userdata;
976 session->name = strdup(name);
980 if (session->managed) {
981 r = sd_bus_path_encode("/org/freedesktop/login1/session",
982 session->name, &session->path);
987 session->card_map = hashmap_new(&string_hash_ops);
988 if (!session->card_map)
991 session->display_map = hashmap_new(&string_hash_ops);
992 if (!session->display_map)
995 r = hashmap_put(context->session_map, session->name, session);
1004 grdev_session *grdev_session_free(grdev_session *session) {
1010 grdev_session_disable(session);
1012 while ((card = hashmap_first(session->card_map)))
1013 session_remove_card(session, card);
1015 assert(hashmap_size(session->display_map) == 0);
1018 hashmap_remove_value(session->context->session_map, session->name, session);
1020 hashmap_free(session->display_map);
1021 hashmap_free(session->card_map);
1022 session->context = grdev_context_unref(session->context);
1023 free(session->path);
1024 free(session->name);
1030 bool grdev_session_is_enabled(grdev_session *session) {
1031 return session && session->enabled;
1034 void grdev_session_enable(grdev_session *session) {
1040 if (!session->enabled) {
1041 session->enabled = true;
1042 HASHMAP_FOREACH(card, session->card_map, iter)
1043 grdev_card_enable(card);
1047 void grdev_session_disable(grdev_session *session) {
1053 if (session->enabled) {
1054 session->enabled = false;
1055 HASHMAP_FOREACH(card, session->card_map, iter)
1056 grdev_card_disable(card);
1060 void grdev_session_commit(grdev_session *session) {
1066 if (!session->enabled)
1069 HASHMAP_FOREACH(card, session->card_map, iter)
1070 if (card->vtable->commit)
1071 card->vtable->commit(card);
1074 void grdev_session_restore(grdev_session *session) {
1080 if (!session->enabled)
1083 HASHMAP_FOREACH(card, session->card_map, iter)
1084 if (card->vtable->restore)
1085 card->vtable->restore(card);
1088 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1096 devnum = udev_device_get_devnum(ud);
1100 card = grdev_find_drm_card(session, devnum);
1104 r = grdev_drm_card_new(&card, session, ud);
1106 log_debug("grdev: %s: cannot add DRM device for %s: %s",
1107 session->name, udev_device_get_syspath(ud), strerror(-r));
1111 session_add_card(session, card);
1114 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1121 devnum = udev_device_get_devnum(ud);
1125 card = grdev_find_drm_card(session, devnum);
1129 session_remove_card(session, card);
1132 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1139 devnum = udev_device_get_devnum(ud);
1143 card = grdev_find_drm_card(session, devnum);
1147 /* TODO: hotplug card */
1150 static void session_configure(grdev_session *session) {
1151 grdev_display *display;
1161 * Whenever backends add or remove pipes, we set session->modified and
1162 * require them to pin the session while modifying it. On release, we
1163 * reconfigure the device and re-assign displays to all modified pipes.
1165 * So far, we configure each pipe as a separate display. We do not
1166 * support user-configuration, nor have we gotten any reports from
1167 * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1168 * we get reports, we keep the logic to a minimum.
1171 /* create new displays for all unconfigured pipes */
1172 HASHMAP_FOREACH(card, session->card_map, i) {
1173 if (!card->modified)
1176 card->modified = false;
1178 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1183 assert(!tile->parent);
1185 display = grdev_find_display(session, pipe->name);
1186 if (display && display->tile) {
1187 log_debug("grdev: %s/%s: occupied display for pipe %s",
1188 session->name, card->name, pipe->name);
1190 } else if (!display) {
1191 r = grdev_display_new(&display, session, pipe->name);
1193 log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
1194 session->name, card->name, pipe->name, strerror(-r));
1199 tile_link(pipe->tile, display->tile);
1203 /* update displays */
1204 HASHMAP_FOREACH(display, session->display_map, i)
1205 session_change_display(session, display);
1208 grdev_session *grdev_session_pin(grdev_session *session) {
1215 grdev_session *grdev_session_unpin(grdev_session *session) {
1219 assert(session->n_pins > 0);
1221 if (--session->n_pins == 0)
1222 session_configure(session);
1231 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1232 _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1234 assert_return(out, -EINVAL);
1235 assert_return(event, -EINVAL);
1237 context = new0(grdev_context, 1);
1242 context->event = sd_event_ref(event);
1245 context->sysbus = sd_bus_ref(sysbus);
1247 context->session_map = hashmap_new(&string_hash_ops);
1248 if (!context->session_map)
1256 static void context_cleanup(grdev_context *context) {
1257 assert(hashmap_size(context->session_map) == 0);
1259 hashmap_free(context->session_map);
1260 context->sysbus = sd_bus_unref(context->sysbus);
1261 context->event = sd_event_unref(context->event);
1265 grdev_context *grdev_context_ref(grdev_context *context) {
1266 assert_return(context, NULL);
1267 assert_return(context->ref > 0, NULL);
1273 grdev_context *grdev_context_unref(grdev_context *context) {
1277 assert_return(context->ref > 0, NULL);
1279 if (--context->ref == 0)
1280 context_cleanup(context);