From 40b85d992a0a8dcdbff5305e232fbcd26d873148 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 1 Jun 2017 20:29:10 -0400 Subject: Refactor udev and drm initialization --- backend/CMakeLists.txt | 2 +- backend/backend.c | 23 +++++- backend/drm/backend.c | 31 +++---- backend/drm/udev.c | 217 ------------------------------------------------ backend/udev.c | 220 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 256 insertions(+), 237 deletions(-) delete mode 100644 backend/drm/udev.c create mode 100644 backend/udev.c (limited to 'backend') diff --git a/backend/CMakeLists.txt b/backend/CMakeLists.txt index ef612639..91f0f06e 100644 --- a/backend/CMakeLists.txt +++ b/backend/CMakeLists.txt @@ -11,7 +11,7 @@ add_library(wlr-backend #wayland/wl_output.c drm/backend.c drm/drm.c - drm/udev.c + udev.c backend.c egl.c ) diff --git a/backend/backend.c b/backend/backend.c index e13f67ac..9a3d81d8 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -39,7 +40,27 @@ void wlr_backend_destroy(struct wlr_backend *backend) { struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, struct wlr_session *session) { // TODO: Choose the most appropriate backend for the situation + // Attempt DRM+libinput + struct wlr_udev *udev; + if (!(udev = wlr_udev_create(display))) { + wlr_log(L_ERROR, "Failed to start udev"); + goto error; + } + int gpu = wlr_udev_find_gpu(udev, session); + if (gpu == -1) { + wlr_log(L_ERROR, "Failed to open DRM device"); + goto error_udev; + } struct wlr_backend *wlr; - wlr = wlr_drm_backend_create(display, session); + wlr = wlr_drm_backend_create(display, session, udev, gpu); + if (!wlr) { + goto error_gpu; + } return wlr; +error_gpu: + close(gpu); +error_udev: + wlr_udev_destroy(udev); +error: + return NULL; } diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 255384fb..bceb2184 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -3,17 +3,16 @@ #include #include #include +#include #include #include - #include #include #include - #include "backend.h" +#include "backend/udev.h" #include "backend/drm/backend.h" #include "backend/drm/drm.h" -#include "backend/drm/udev.h" #include "common/log.h" static bool wlr_drm_backend_init(struct wlr_backend_state *state) { @@ -30,7 +29,6 @@ static void wlr_drm_backend_destroy(struct wlr_backend_state *state) { wlr_output_destroy(output->wlr_output); } wlr_drm_renderer_free(&state->renderer); - wlr_udev_free(&state->udev); wlr_session_close_file(state->session, state->fd); wlr_session_finish(state->session); wl_event_source_remove(state->drm_event); @@ -75,8 +73,14 @@ static void device_resumed(struct wl_listener *listener, void *data) { } } +static void drm_invalidated(struct wl_listener *listener, void *state) { + struct wlr_backend_state *drm = wl_container_of(listener, drm, drm_invalidated); + wlr_drm_scan_connectors(drm); +} + struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, - struct wlr_session *session) { + struct wlr_session *session, struct wlr_udev *udev, int gpu_fd) { + assert(display && session && gpu_fd > 0); struct wlr_backend_state *state = calloc(1, sizeof(struct wlr_backend_state)); if (!state) { wlr_log(L_ERROR, "Allocation failed: %s", strerror(errno)); @@ -97,16 +101,10 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, goto error_backend; } - if (!wlr_udev_init(display, &state->udev)) { - wlr_log(L_ERROR, "Failed to start udev"); - goto error_list; - } - - state->fd = wlr_udev_find_gpu(&state->udev, state->session); - if (state->fd == -1) { - wlr_log(L_ERROR, "Failed to open DRM device"); - goto error_udev; - } + state->fd = gpu_fd; + wl_list_init(&state->drm_invalidated.link); + state->drm_invalidated.notify = drm_invalidated; + wl_signal_add(&udev->invalidate_drm, &state->drm_invalidated); struct stat st; if (fstat(state->fd, &st) < 0) { @@ -145,9 +143,6 @@ error_event: wl_event_source_remove(state->drm_event); error_fd: wlr_session_close_file(state->session, state->fd); -error_udev: - wlr_udev_free(&state->udev); -error_list: list_free(state->outputs); error_backend: free(state); diff --git a/backend/drm/udev.c b/backend/drm/udev.c deleted file mode 100644 index 79e303d6..00000000 --- a/backend/drm/udev.c +++ /dev/null @@ -1,217 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "backend.h" -#include "backend/drm/backend.h" -#include "backend/drm/udev.h" -#include "backend/drm/drm.h" -#include "common/log.h" - -/* 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_udev_find_gpu(struct wlr_udev *udev, struct wlr_session *session) { - struct udev_enumerate *en = udev_enumerate_new(udev->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; - char *drm_path = NULL; - - 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(udev->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; - } - - free(drm_path); - drm_path = strdup(path); - - udev_device_unref(dev); - - // We've found the primary GPU - if (is_boot_vga) { - break; - } - } - - udev_enumerate_unref(en); - - udev->drm_path = drm_path; - return fd; -} - -static int udev_event(int fd, uint32_t mask, void *data) { - struct wlr_udev *udev = data; - struct wlr_backend_state *state = wl_container_of(udev, state, udev); - - struct udev_device *dev = udev_monitor_receive_device(udev->mon); - if (!dev) { - return 1; - } - - const char *action = udev_device_get_action(dev); - const char *path = udev_device_get_devnode(dev); - - wlr_log(L_DEBUG, "udev event for %s (%s)", - udev_device_get_sysname(dev), action); - - if (!path || strcmp(path, udev->drm_path) != 0) { - goto out; - } - - if (!action || strcmp(action, "change") != 0) { - goto out; - } - - wlr_drm_scan_connectors(state); - -out: - udev_device_unref(dev); - return 1; -} - -bool wlr_udev_init(struct wl_display *display, struct wlr_udev *udev) { - udev->udev = udev_new(); - if (!udev->udev) { - wlr_log(L_ERROR, "Failed to create udev context"); - return false; - } - - udev->mon = udev_monitor_new_from_netlink(udev->udev, "udev"); - if (!udev->mon) { - wlr_log(L_ERROR, "Failed to create udev monitor"); - goto error_udev; - } - - udev_monitor_filter_add_match_subsystem_devtype(udev->mon, "drm", NULL); - udev_monitor_enable_receiving(udev->mon); - - struct wl_event_loop *event_loop = wl_display_get_event_loop(display); - int fd = udev_monitor_get_fd(udev->mon); - - udev->event = wl_event_loop_add_fd(event_loop, fd, WL_EVENT_READABLE, - udev_event, udev); - if (!udev->event) { - wlr_log(L_ERROR, "Failed to create udev event source"); - goto error_mon; - } - - wlr_log(L_DEBUG, "Successfully initialized udev"); - return true; - -error_mon: - udev_monitor_unref(udev->mon); -error_udev: - udev_unref(udev->udev); - return false; -} - -void wlr_udev_free(struct wlr_udev *udev) { - if (!udev) { - return; - } - - wl_event_source_remove(udev->event); - - udev_monitor_unref(udev->mon); - udev_unref(udev->udev); - free(udev->drm_path); -} diff --git a/backend/udev.c b/backend/udev.c new file mode 100644 index 00000000..30c24e49 --- /dev/null +++ b/backend/udev.c @@ -0,0 +1,220 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "backend.h" +#include "backend/drm/backend.h" +#include "backend/udev.h" +#include "common/log.h" + +/* 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_udev_find_gpu(struct wlr_udev *udev, struct wlr_session *session) { + struct udev_enumerate *en = udev_enumerate_new(udev->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; + char *drm_path = NULL; + + 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(udev->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; + } + + free(drm_path); + drm_path = strdup(path); + + udev_device_unref(dev); + + // We've found the primary GPU + if (is_boot_vga) { + break; + } + } + + udev_enumerate_unref(en); + + udev->drm_path = drm_path; + return fd; +} + +static int udev_event(int fd, uint32_t mask, void *data) { + struct wlr_udev *udev = data; + + struct udev_device *dev = udev_monitor_receive_device(udev->mon); + if (!dev) { + return 1; + } + + const char *action = udev_device_get_action(dev); + const char *path = udev_device_get_devnode(dev); + + wlr_log(L_DEBUG, "udev event for %s (%s)", + udev_device_get_sysname(dev), action); + + if (!path || strcmp(path, udev->drm_path) != 0) { + goto out; + } + + if (!action || strcmp(action, "change") != 0) { + goto out; + } + + // TODO: Specify the GPU that's being invalidated + wl_signal_emit(&udev->invalidate_drm, udev); + +out: + udev_device_unref(dev); + return 1; +} + +struct wlr_udev *wlr_udev_create(struct wl_display *display) { + struct wlr_udev *udev = calloc(sizeof(struct wlr_udev), 1); + if (!udev) { + return NULL; + } + udev->udev = udev_new(); + if (!udev->udev) { + wlr_log(L_ERROR, "Failed to create udev context"); + goto error; + } + wl_signal_init(&udev->invalidate_drm); + + udev->mon = udev_monitor_new_from_netlink(udev->udev, "udev"); + if (!udev->mon) { + wlr_log(L_ERROR, "Failed to create udev monitor"); + goto error_udev; + } + + udev_monitor_filter_add_match_subsystem_devtype(udev->mon, "drm", NULL); + udev_monitor_enable_receiving(udev->mon); + + struct wl_event_loop *event_loop = wl_display_get_event_loop(display); + int fd = udev_monitor_get_fd(udev->mon); + + udev->event = wl_event_loop_add_fd(event_loop, fd, WL_EVENT_READABLE, + udev_event, udev); + if (!udev->event) { + wlr_log(L_ERROR, "Failed to create udev event source"); + goto error_mon; + } + + wlr_log(L_DEBUG, "Successfully initialized udev"); + return udev; + +error_mon: + udev_monitor_unref(udev->mon); +error_udev: + udev_unref(udev->udev); +error: + free(udev); + return NULL; +} + +void wlr_udev_destroy(struct wlr_udev *udev) { + if (!udev) { + return; + } + + wl_event_source_remove(udev->event); + + udev_monitor_unref(udev->mon); + udev_unref(udev->udev); + free(udev->drm_path); +} -- cgit v1.2.3