From 968e1277bc8cb8051254c37209dc6f8ddedb98c4 Mon Sep 17 00:00:00 2001 From: illiliti Date: Sun, 13 Mar 2022 21:35:38 +0300 Subject: [PATCH] backend/session/session.c: decouple udev to backend/session/dev_udev.c --- backend/session/dev_udev.c | 308 ++++++++++++++++++++++++++++++++++++ backend/session/meson.build | 2 +- backend/session/session.c | 279 +------------------------------- 3 files changed, 314 insertions(+), 275 deletions(-) create mode 100644 backend/session/dev_udev.c diff --git a/backend/session/dev_udev.c b/backend/session/dev_udev.c new file mode 100644 index 000000000..88dab7b33 --- /dev/null +++ b/backend/session/dev_udev.c @@ -0,0 +1,308 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "backend/session/session.h" +#include "backend/session/dev.h" +#include "util/signal.h" + +#define WAIT_GPU_TIMEOUT 10000 // ms + +static bool is_drm_card(const char *sysname) { + const char prefix[] = DRM_PRIMARY_MINOR_NAME; + if (strncmp(sysname, prefix, strlen(prefix)) != 0) { + return false; + } + for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) { + if (sysname[i] < '0' || sysname[i] > '9') { + return false; + } + } + return true; +} + +static void read_udev_change_event(struct wlr_device_change_event *event, + struct udev_device *udev_dev) { + const char *hotplug = udev_device_get_property_value(udev_dev, "HOTPLUG"); + if (hotplug != NULL && strcmp(hotplug, "1") == 0) { + event->type = WLR_DEVICE_HOTPLUG; + struct wlr_device_hotplug_event *hotplug = &event->hotplug; + + const char *connector = + udev_device_get_property_value(udev_dev, "CONNECTOR"); + if (connector != NULL) { + hotplug->connector_id = strtoul(connector, NULL, 10); + } + + const char *prop = + udev_device_get_property_value(udev_dev, "PROPERTY"); + if (prop != NULL) { + hotplug->prop_id = strtoul(prop, NULL, 10); + } + + return; + } + + const char *lease = udev_device_get_property_value(udev_dev, "LEASE"); + if (lease != NULL && strcmp(lease, "1") == 0) { + event->type = WLR_DEVICE_LEASE; + return; + } +} + +static int handle_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 *sysname = udev_device_get_sysname(udev_dev); + const char *devnode = udev_device_get_devnode(udev_dev); + const char *action = udev_device_get_action(udev_dev); + wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action); + + if (!is_drm_card(sysname) || !action || !devnode) { + goto out; + } + + const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT"); + if (!seat) { + seat = "seat0"; + } + if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) { + goto out; + } + + if (strcmp(action, "add") == 0) { + wlr_log(WLR_DEBUG, "DRM device %s added", sysname); + struct wlr_session_add_event event = { + .path = devnode, + }; + wlr_signal_emit_safe(&session->events.add_drm_card, &event); + } else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) { + dev_t devnum = udev_device_get_devnum(udev_dev); + struct wlr_device *dev; + wl_list_for_each(dev, &session->devices, link) { + if (dev->dev != devnum) { + continue; + } + + if (strcmp(action, "change") == 0) { + wlr_log(WLR_DEBUG, "DRM device %s changed", sysname); + struct wlr_device_change_event event = {0}; + read_udev_change_event(&event, udev_dev); + wlr_signal_emit_safe(&dev->events.change, &event); + } else if (strcmp(action, "remove") == 0) { + wlr_log(WLR_DEBUG, "DRM device %s removed", sysname); + wlr_signal_emit_safe(&dev->events.remove, NULL); + } else { + assert(0); + } + break; + } + } + +out: + udev_device_unref(udev_dev); + return 1; +} + +int dev_init(struct wlr_session *session, struct wl_display *disp) { + session->udev = udev_new(); + if (!session->udev) { + wlr_log_errno(WLR_ERROR, "Failed to create udev context"); + return -1; + } + + session->mon = udev_monitor_new_from_netlink(session->udev, "udev"); + if (!session->mon) { + wlr_log_errno(WLR_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, handle_udev_event, session); + if (!session->udev_event) { + wlr_log_errno(WLR_ERROR, "Failed to create udev event source"); + goto error_mon; + } + + return 0; + +error_mon: + udev_monitor_unref(session->mon); +error_udev: + udev_unref(session->udev); + return -1; +} + +void dev_finish(struct wlr_session *session) { + if (!session) { + return; + } + + wl_event_source_remove(session->udev_event); + udev_monitor_unref(session->mon); + udev_unref(session->udev); +} + +static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) { + struct udev_enumerate *en = udev_enumerate_new(udev); + if (!en) { + wlr_log(WLR_ERROR, "udev_enumerate_new failed"); + return NULL; + } + + udev_enumerate_add_match_subsystem(en, "drm"); + udev_enumerate_add_match_sysname(en, DRM_PRIMARY_MINOR_NAME "[0-9]*"); + + if (udev_enumerate_scan_devices(en) != 0) { + wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed"); + udev_enumerate_unref(en); + return NULL; + } + + return en; +} + +static uint64_t get_current_time_ms(void) { + struct timespec ts = {0}; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; +} + +struct find_gpus_add_handler { + bool added; + struct wl_listener listener; +}; + +static void find_gpus_handle_add(struct wl_listener *listener, void *data) { + struct find_gpus_add_handler *handler = + wl_container_of(listener, handler, listener); + handler->added = true; +} + +/* 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. + */ +ssize_t dev_find_gpus(struct wlr_session *session, + size_t ret_len, struct wlr_device **ret) { + struct udev_enumerate *en = enumerate_drm_cards(session->udev); + if (!en) { + return -1; + } + + if (udev_enumerate_get_list_entry(en) == NULL) { + udev_enumerate_unref(en); + wlr_log(WLR_INFO, "Waiting for a DRM card device"); + + struct find_gpus_add_handler handler = {0}; + handler.listener.notify = find_gpus_handle_add; + wl_signal_add(&session->events.add_drm_card, &handler.listener); + + uint64_t started_at = get_current_time_ms(); + uint64_t timeout = WAIT_GPU_TIMEOUT; + struct wl_event_loop *event_loop = + wl_display_get_event_loop(session->display); + while (!handler.added) { + int ret = wl_event_loop_dispatch(event_loop, (int)timeout); + if (ret < 0) { + wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: " + "wl_event_loop_dispatch failed"); + udev_enumerate_unref(en); + return -1; + } + + uint64_t now = get_current_time_ms(); + if (now >= started_at + WAIT_GPU_TIMEOUT) { + break; + } + timeout = started_at + WAIT_GPU_TIMEOUT - now; + } + + wl_list_remove(&handler.listener.link); + + en = enumerate_drm_cards(session->udev); + if (!en) { + return -1; + } + } + + struct udev_list_entry *entry; + size_t i = 0; + + udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(en)) { + if (i == ret_len) { + break; + } + + 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 (session->seat[0] && 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; + } + } + + struct wlr_device *wlr_dev = + session_open_if_kms(session, udev_device_get_devnode(dev)); + if (!wlr_dev) { + udev_device_unref(dev); + continue; + } + + udev_device_unref(dev); + + ret[i] = wlr_dev; + if (is_boot_vga) { + struct wlr_device *tmp = ret[0]; + ret[0] = ret[i]; + ret[i] = tmp; + } + + ++i; + } + + udev_enumerate_unref(en); + + return i; +} diff --git a/backend/session/meson.build b/backend/session/meson.build index 27915506b..f0ff132f1 100644 --- a/backend/session/meson.build +++ b/backend/session/meson.build @@ -3,5 +3,5 @@ libseat = dependency('libseat', fallback: 'seatd', default_options: ['server=disabled', 'man-pages=disabled'], ) -wlr_files += files('session.c') +wlr_files += files('session.c', 'dev_udev.c') wlr_deps += libseat diff --git a/backend/session/session.c b/backend/session/session.c index 7d6d080da..cd46a04fb 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -1,8 +1,5 @@ #define _POSIX_C_SOURCE 200809L -#include -#include #include -#include #include #include #include @@ -14,15 +11,13 @@ #include #include #include -#include #include #include "backend/session/session.h" +#include "backend/session/dev.h" #include "util/signal.h" #include -#define WAIT_GPU_TIMEOUT 10000 // ms - static void handle_enable_seat(struct libseat *seat, void *data) { struct wlr_session *session = data; session->active = true; @@ -126,107 +121,6 @@ static void libseat_session_finish(struct wlr_session *session) { session->libseat_event = NULL; } -static bool is_drm_card(const char *sysname) { - const char prefix[] = DRM_PRIMARY_MINOR_NAME; - if (strncmp(sysname, prefix, strlen(prefix)) != 0) { - return false; - } - for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) { - if (sysname[i] < '0' || sysname[i] > '9') { - return false; - } - } - return true; -} - -static void read_udev_change_event(struct wlr_device_change_event *event, - struct udev_device *udev_dev) { - const char *hotplug = udev_device_get_property_value(udev_dev, "HOTPLUG"); - if (hotplug != NULL && strcmp(hotplug, "1") == 0) { - event->type = WLR_DEVICE_HOTPLUG; - struct wlr_device_hotplug_event *hotplug = &event->hotplug; - - const char *connector = - udev_device_get_property_value(udev_dev, "CONNECTOR"); - if (connector != NULL) { - hotplug->connector_id = strtoul(connector, NULL, 10); - } - - const char *prop = - udev_device_get_property_value(udev_dev, "PROPERTY"); - if (prop != NULL) { - hotplug->prop_id = strtoul(prop, NULL, 10); - } - - return; - } - - const char *lease = udev_device_get_property_value(udev_dev, "LEASE"); - if (lease != NULL && strcmp(lease, "1") == 0) { - event->type = WLR_DEVICE_LEASE; - return; - } -} - -static int handle_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 *sysname = udev_device_get_sysname(udev_dev); - const char *devnode = udev_device_get_devnode(udev_dev); - const char *action = udev_device_get_action(udev_dev); - wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action); - - if (!is_drm_card(sysname) || !action || !devnode) { - goto out; - } - - const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT"); - if (!seat) { - seat = "seat0"; - } - if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) { - goto out; - } - - if (strcmp(action, "add") == 0) { - wlr_log(WLR_DEBUG, "DRM device %s added", sysname); - struct wlr_session_add_event event = { - .path = devnode, - }; - wlr_signal_emit_safe(&session->events.add_drm_card, &event); - } else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) { - dev_t devnum = udev_device_get_devnum(udev_dev); - struct wlr_device *dev; - wl_list_for_each(dev, &session->devices, link) { - if (dev->dev != devnum) { - continue; - } - - if (strcmp(action, "change") == 0) { - wlr_log(WLR_DEBUG, "DRM device %s changed", sysname); - struct wlr_device_change_event event = {0}; - read_udev_change_event(&event, udev_dev); - wlr_signal_emit_safe(&dev->events.change, &event); - } else if (strcmp(action, "remove") == 0) { - wlr_log(WLR_DEBUG, "DRM device %s removed", sysname); - wlr_signal_emit_safe(&dev->events.remove, NULL); - } else { - assert(0); - } - break; - } - } - -out: - udev_device_unref(udev_dev); - return 1; -} - static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_session *session = wl_container_of(listener, session, display_destroy); @@ -250,31 +144,11 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { goto error_open; } - session->udev = udev_new(); - if (!session->udev) { - wlr_log_errno(WLR_ERROR, "Failed to create udev context"); + if (dev_init(session, disp) == -1) { + wlr_log(WLR_ERROR, "Failed to initialize dev backend"); goto error_session; } - session->mon = udev_monitor_new_from_netlink(session->udev, "udev"); - if (!session->mon) { - wlr_log_errno(WLR_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, handle_udev_event, session); - if (!session->udev_event) { - wlr_log_errno(WLR_ERROR, "Failed to create udev event source"); - goto error_mon; - } - session->display = disp; session->display_destroy.notify = handle_display_destroy; @@ -282,10 +156,6 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { return session; -error_mon: - udev_monitor_unref(session->mon); -error_udev: - udev_unref(session->udev); error_session: libseat_session_finish(session); error_open: @@ -301,9 +171,7 @@ void wlr_session_destroy(struct wlr_session *session) { wlr_signal_emit_safe(&session->events.destroy, session); wl_list_remove(&session->display_destroy.link); - wl_event_source_remove(session->udev_event); - udev_monitor_unref(session->mon); - udev_unref(session->udev); + dev_finish(session); struct wlr_device *dev, *tmp_dev; wl_list_for_each_safe(dev, tmp_dev, &session->devices, link) { @@ -418,45 +286,6 @@ static ssize_t explicit_find_gpus(struct wlr_session *session, return i; } -static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) { - struct udev_enumerate *en = udev_enumerate_new(udev); - if (!en) { - wlr_log(WLR_ERROR, "udev_enumerate_new failed"); - return NULL; - } - - udev_enumerate_add_match_subsystem(en, "drm"); - udev_enumerate_add_match_sysname(en, DRM_PRIMARY_MINOR_NAME "[0-9]*"); - - if (udev_enumerate_scan_devices(en) != 0) { - wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed"); - udev_enumerate_unref(en); - return NULL; - } - - return en; -} - -static uint64_t get_current_time_ms(void) { - struct timespec ts = {0}; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; -} - -struct find_gpus_add_handler { - bool added; - struct wl_listener listener; -}; - -static void find_gpus_handle_add(struct wl_listener *listener, void *data) { - struct find_gpus_add_handler *handler = - wl_container_of(listener, handler, listener); - handler->added = true; -} - -/* 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. - */ ssize_t wlr_session_find_gpus(struct wlr_session *session, size_t ret_len, struct wlr_device **ret) { const char *explicit = getenv("WLR_DRM_DEVICES"); @@ -464,103 +293,5 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, return explicit_find_gpus(session, ret_len, ret, explicit); } - struct udev_enumerate *en = enumerate_drm_cards(session->udev); - if (!en) { - return -1; - } - - if (udev_enumerate_get_list_entry(en) == NULL) { - udev_enumerate_unref(en); - wlr_log(WLR_INFO, "Waiting for a DRM card device"); - - struct find_gpus_add_handler handler = {0}; - handler.listener.notify = find_gpus_handle_add; - wl_signal_add(&session->events.add_drm_card, &handler.listener); - - uint64_t started_at = get_current_time_ms(); - uint64_t timeout = WAIT_GPU_TIMEOUT; - struct wl_event_loop *event_loop = - wl_display_get_event_loop(session->display); - while (!handler.added) { - int ret = wl_event_loop_dispatch(event_loop, (int)timeout); - if (ret < 0) { - wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: " - "wl_event_loop_dispatch failed"); - udev_enumerate_unref(en); - return -1; - } - - uint64_t now = get_current_time_ms(); - if (now >= started_at + WAIT_GPU_TIMEOUT) { - break; - } - timeout = started_at + WAIT_GPU_TIMEOUT - now; - } - - wl_list_remove(&handler.listener.link); - - en = enumerate_drm_cards(session->udev); - if (!en) { - return -1; - } - } - - struct udev_list_entry *entry; - size_t i = 0; - - udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(en)) { - if (i == ret_len) { - break; - } - - 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 (session->seat[0] && 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; - } - } - - struct wlr_device *wlr_dev = - session_open_if_kms(session, udev_device_get_devnode(dev)); - if (!wlr_dev) { - udev_device_unref(dev); - continue; - } - - udev_device_unref(dev); - - ret[i] = wlr_dev; - if (is_boot_vga) { - struct wlr_device *tmp = ret[0]; - ret[0] = ret[i]; - ret[i] = tmp; - } - - ++i; - } - - udev_enumerate_unref(en); - - return i; + return dev_find_gpus(session, ret_len, ret); }