diff options
Diffstat (limited to 'backend/session')
-rw-r--r-- | backend/session/direct-ipc.c | 2 | ||||
-rw-r--r-- | backend/session/direct.c | 38 | ||||
-rw-r--r-- | backend/session/logind.c | 44 | ||||
-rw-r--r-- | backend/session/session.c | 271 |
4 files changed, 312 insertions, 43 deletions
diff --git a/backend/session/direct-ipc.c b/backend/session/direct-ipc.c index 71e22148..5f1494a6 100644 --- a/backend/session/direct-ipc.c +++ b/backend/session/direct-ipc.c @@ -212,7 +212,7 @@ void direct_ipc_finish(int sock, pid_t pid) { waitpid(pid, NULL, 0); } -int direct_ipc_start(pid_t *pid_out) { +int direct_ipc_init(pid_t *pid_out) { if (!have_permissions()) { return -1; } diff --git a/backend/session/direct.c b/backend/session/direct.c index 82aa8e00..942dc552 100644 --- a/backend/session/direct.c +++ b/backend/session/direct.c @@ -48,7 +48,7 @@ static int direct_session_open(struct wlr_session *base, const char *path) { } if (major(st.st_rdev) == DRM_MAJOR) { - session->base.drm_fd = fd; + direct_ipc_setmaster(session->sock, fd); } return fd; @@ -65,8 +65,7 @@ static void direct_session_close(struct wlr_session *base, int fd) { } if (major(st.st_rdev) == DRM_MAJOR) { - direct_ipc_dropmaster(session->sock, session->base.drm_fd); - session->base.drm_fd = -1; + direct_ipc_dropmaster(session->sock, fd); } else if (major(st.st_rdev) == INPUT_MAJOR) { ioctl(fd, EVIOCREVOKE, 0); } @@ -79,7 +78,7 @@ static bool direct_change_vt(struct wlr_session *base, unsigned vt) { return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0; } -static void direct_session_finish(struct wlr_session *base) { +static void direct_session_destroy(struct wlr_session *base) { struct direct_session *session = wl_container_of(base, session, base); struct vt_mode mode = { .mode = VT_AUTO, @@ -109,11 +108,27 @@ static int vt_handler(int signo, void *data) { if (session->base.active) { session->base.active = false; wl_signal_emit(&session->base.session_signal, session); - direct_ipc_dropmaster(session->sock, session->base.drm_fd); + + struct wlr_device *dev; + wl_list_for_each(dev, &session->base.devices, link) { + if (major(dev->dev) == DRM_MAJOR) { + direct_ipc_dropmaster(session->sock, + dev->fd); + } + } + ioctl(session->tty_fd, VT_RELDISP, 1); } else { ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ); - direct_ipc_setmaster(session->sock, session->base.drm_fd); + + struct wlr_device *dev; + wl_list_for_each(dev, &session->base.devices, link) { + if (major(dev->dev) == DRM_MAJOR) { + direct_ipc_setmaster(session->sock, + dev->fd); + } + } + session->base.active = true; wl_signal_emit(&session->base.session_signal, session); } @@ -196,14 +211,14 @@ error: return false; } -static struct wlr_session *direct_session_start(struct wl_display *disp) { +static struct wlr_session *direct_session_create(struct wl_display *disp) { struct direct_session *session = calloc(1, sizeof(*session)); if (!session) { wlr_log_errno(L_ERROR, "Allocation failed"); return NULL; } - session->sock = direct_ipc_start(&session->child); + session->sock = direct_ipc_init(&session->child); if (session->sock == -1) { goto error_session; } @@ -221,10 +236,7 @@ static struct wlr_session *direct_session_start(struct wl_display *disp) { wlr_log(L_INFO, "Successfully loaded direct session"); snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); - session->base.drm_fd = -1; session->base.impl = &session_direct; - session->base.active = true; - wl_signal_init(&session->base.session_signal); return &session->base; error_ipc: @@ -236,8 +248,8 @@ error_session: } const struct session_impl session_direct = { - .start = direct_session_start, - .finish = direct_session_finish, + .create = direct_session_create, + .destroy = direct_session_destroy, .open = direct_session_open, .close = direct_session_close, .change_vt = direct_change_vt, diff --git a/backend/session/logind.c b/backend/session/logind.c index 20d9b5ed..3b237360 100644 --- a/backend/session/logind.c +++ b/backend/session/logind.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#include <assert.h> #include <stdio.h> #include <stdbool.h> #include <stdlib.h> @@ -67,10 +68,6 @@ static int logind_take_device(struct wlr_session *base, const char *path) { goto error; } - if (major(st.st_rdev) == DRM_MAJOR) { - session->base.drm_fd = fd; - } - error: sd_bus_error_free(&error); sd_bus_message_unref(msg); @@ -97,10 +94,6 @@ static void logind_release_device(struct wlr_session *base, int fd) { wlr_log(L_ERROR, "Failed to release device '%d'", fd); } - if (major(st.st_rdev) == DRM_MAJOR) { - session->base.drm_fd = -1; - } - sd_bus_error_free(&error); sd_bus_message_unref(msg); } @@ -204,7 +197,7 @@ static void release_control(struct logind_session *session) { sd_bus_message_unref(msg); } -static void logind_session_finish(struct wlr_session *base) { +static void logind_session_destroy(struct wlr_session *base) { struct logind_session *session = wl_container_of(base, session, base); release_control(session); @@ -221,6 +214,20 @@ static int session_removed(sd_bus_message *msg, void *userdata, sd_bus_error *re return 0; } +static struct wlr_device *find_device(struct wlr_session *session, dev_t devnum) { + struct wlr_device *dev; + + wl_list_for_each(dev, &session->devices, link) { + if (dev->dev == devnum) { + return dev; + } + } + + wlr_log(L_ERROR, "Tried to use dev_t %lu not opened by session", + (unsigned long)devnum); + assert(0); +} + static int pause_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_error) { struct logind_session *session = userdata; int ret; @@ -267,9 +274,13 @@ static int resume_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_ } if (major == DRM_MAJOR) { - dup2(fd, session->base.drm_fd); - session->base.active = true; - wl_signal_emit(&session->base.session_signal, session); + struct wlr_device *dev = find_device(&session->base, makedev(major, minor)); + dup2(fd, dev->fd); + + if (!session->base.active) { + session->base.active = true; + wl_signal_emit(&session->base.session_signal, session); + } } error: @@ -316,7 +327,7 @@ static int dbus_event(int fd, uint32_t mask, void *data) { return 1; } -static struct wlr_session *logind_session_start(struct wl_display *disp) { +static struct wlr_session *logind_session_create(struct wl_display *disp) { int ret; struct logind_session *session = calloc(1, sizeof(*session)); if (!session) { @@ -374,10 +385,7 @@ static struct wlr_session *logind_session_start(struct wl_display *disp) { wlr_log(L_INFO, "Successfully loaded logind session"); - session->base.drm_fd = -1; session->base.impl = &session_logind; - session->base.active = true; - wl_signal_init(&session->base.session_signal); return &session->base; error_bus: @@ -390,8 +398,8 @@ error: } const struct session_impl session_logind = { - .start = logind_session_start, - .finish = logind_session_finish, + .create = logind_session_create, + .destroy = logind_session_destroy, .open = logind_take_device, .close = logind_release_device, .change_vt = logind_change_vt, diff --git a/backend/session/session.c b/backend/session/session.c index a07d3cda..c9bc1397 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -1,5 +1,13 @@ +#include <assert.h> #include <stddef.h> #include <stdarg.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <libudev.h> +#include <wayland-server.h> +#include <xf86drm.h> +#include <xf86drmMode.h> #include <wlr/backend/session.h> #include <wlr/backend/session/interface.h> #include <wlr/util/log.h> @@ -15,34 +23,159 @@ static const struct session_impl *impls[] = { NULL, }; -struct wlr_session *wlr_session_start(struct wl_display *disp) { - const struct session_impl **iter; +static int udev_event(int fd, uint32_t mask, void *data) { + struct wlr_session *session = data; + + struct udev_device *udev_dev = udev_monitor_receive_device(session->mon); + if (!udev_dev) { + return 1; + } + + const char *action = udev_device_get_action(udev_dev); + + wlr_log(L_DEBUG, "udev event for %s (%s)", + udev_device_get_sysname(udev_dev), action); + + if (!action || strcmp(action, "change") != 0) { + goto out; + } + + dev_t devnum = udev_device_get_devnum(udev_dev); + struct wlr_device *dev; - for (iter = impls; *iter; ++iter) { - struct wlr_session *session = (*iter)->start(disp); - if (session) { - return session; + wl_list_for_each(dev, &session->devices, link) { + if (dev->dev == devnum) { + wl_signal_emit(&dev->signal, session); + break; } } - wlr_log(L_ERROR, "Failed to load session backend"); +out: + udev_device_unref(udev_dev); + return 1; +} + +struct wlr_session *wlr_session_create(struct wl_display *disp) { + struct wlr_session *session = NULL; + const struct session_impl **iter; + + for (iter = impls; !session && *iter; ++iter) { + session = (*iter)->create(disp); + } + + if (!session) { + wlr_log(L_ERROR, "Failed to load session backend"); + return NULL; + } + + session->active = true; + wl_signal_init(&session->session_signal); + wl_list_init(&session->devices); + + session->udev = udev_new(); + if (!session->udev) { + wlr_log_errno(L_ERROR, "Failed to create udev context"); + goto error_session; + } + + session->mon = udev_monitor_new_from_netlink(session->udev, "udev"); + if (!session->mon) { + wlr_log_errno(L_ERROR, "Failed to create udev monitor"); + goto error_udev; + } + + udev_monitor_filter_add_match_subsystem_devtype(session->mon, "drm", NULL); + udev_monitor_enable_receiving(session->mon); + + struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); + int fd = udev_monitor_get_fd(session->mon); + + session->udev_event = wl_event_loop_add_fd(event_loop, fd, + WL_EVENT_READABLE, udev_event, session); + if (!session->udev_event) { + wlr_log_errno(L_ERROR, "Failed to create udev event source"); + goto error_mon; + } + + return session; + +error_mon: + udev_monitor_unref(session->mon); +error_udev: + udev_unref(session->udev); +error_session: + wlr_session_destroy(session); return NULL; } -void wlr_session_finish(struct wlr_session *session) { +void wlr_session_destroy(struct wlr_session *session) { if (!session) { return; } - session->impl->finish(session); -}; + wl_event_source_remove(session->udev_event); + udev_monitor_unref(session->mon); + udev_unref(session->udev); + + session->impl->destroy(session); +} int wlr_session_open_file(struct wlr_session *session, const char *path) { - return session->impl->open(session, path); + int fd = session->impl->open(session, path); + if (fd < 0) { + return fd; + } + + struct wlr_device *dev = malloc(sizeof(*dev)); + if (!dev) { + wlr_log_errno(L_ERROR, "Allocation failed"); + goto error; + } + + struct stat st; + if (fstat(fd, &st) < 0) { + wlr_log_errno(L_ERROR, "Stat failed"); + goto error; + } + + dev->fd = fd; + dev->dev = st.st_rdev; + wl_signal_init(&dev->signal); + wl_list_insert(&session->devices, &dev->link); + + return fd; + +error: + free(dev); + return fd; +} + +static struct wlr_device *find_device(struct wlr_session *session, int fd) { + struct wlr_device *dev; + + wl_list_for_each(dev, &session->devices, link) { + if (dev->fd == fd) { + return dev; + } + } + + wlr_log(L_ERROR, "Tried to use fd %d not opened by session", fd); + assert(0); } void wlr_session_close_file(struct wlr_session *session, int fd) { + struct wlr_device *dev = find_device(session, fd); + session->impl->close(session, fd); + wl_list_remove(&dev->link); + free(dev); +} + +void wlr_session_signal_add(struct wlr_session *session, int fd, + struct wl_listener *listener) { + struct wlr_device *dev = find_device(session, fd); + + wl_signal_add(&dev->signal, listener); } bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { @@ -52,3 +185,119 @@ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { return session->impl->change_vt(session, vt); } + +/* Tests if 'path' is KMS compatible by trying to open it. + * It leaves the open device in *fd_out it it succeeds. + */ +static bool device_is_kms(struct wlr_session *restrict session, + const char *restrict path, int *restrict fd_out) { + + int fd; + + if (!path) { + return false; + } + + fd = wlr_session_open_file(session, path); + if (fd < 0) { + return false; + } + + drmModeRes *res = drmModeGetResources(fd); + if (!res) { + goto out_fd; + } + + if (res->count_crtcs <= 0 || res->count_connectors <= 0 || + res->count_encoders <= 0) { + + goto out_res; + } + + if (*fd_out >= 0) { + wlr_session_close_file(session, *fd_out); + } + + *fd_out = fd; + + drmModeFreeResources(res); + return true; + +out_res: + drmModeFreeResources(res); +out_fd: + wlr_session_close_file(session, fd); + return false; +} + +/* Tries to find the primary GPU by checking for the "boot_vga" attribute. + * If it's not found, it returns the first valid GPU it finds. + */ +int wlr_session_find_gpu(struct wlr_session *session) { + struct udev_enumerate *en = udev_enumerate_new(session->udev); + if (!en) { + wlr_log(L_ERROR, "Failed to create udev enumeration"); + return -1; + } + + udev_enumerate_add_match_subsystem(en, "drm"); + udev_enumerate_add_match_sysname(en, "card[0-9]*"); + udev_enumerate_scan_devices(en); + + struct udev_list_entry *entry; + int fd = -1; + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(en)) { + bool is_boot_vga = false; + + const char *path = udev_list_entry_get_name(entry); + struct udev_device *dev = udev_device_new_from_syspath(session->udev, path); + if (!dev) { + continue; + } + + /* + const char *seat = udev_device_get_property_value(dev, "ID_SEAT"); + if (!seat) + seat = "seat0"; + if (strcmp(session->seat, seat) != 0) { + udev_device_unref(dev); + continue; + } + */ + + // This is owned by 'dev', so we don't need to free it + struct udev_device *pci = + udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); + + if (pci) { + const char *id = udev_device_get_sysattr_value(pci, "boot_vga"); + if (id && strcmp(id, "1") == 0) { + is_boot_vga = true; + } + } + + // We already have a valid GPU + if (!is_boot_vga && fd >= 0) { + udev_device_unref(dev); + continue; + } + + path = udev_device_get_devnode(dev); + if (!device_is_kms(session, path, &fd)) { + udev_device_unref(dev); + continue; + } + + udev_device_unref(dev); + + // We've found the primary GPU + if (is_boot_vga) { + break; + } + } + + udev_enumerate_unref(en); + + return fd; +} |