diff --git a/backend/drm/backend.c b/backend/drm/backend.c index 5a545b192..5688f1f18 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -11,6 +11,7 @@ #include #include "backend/drm/drm.h" #include "backend/drm/fb.h" +#include "backend/drm/util.h" #include "render/drm_format_set.h" struct wlr_drm_backend *get_drm_backend_from_backend( @@ -225,6 +226,9 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name); drmFreeVersion(version); + drmDevice *dev_info = NULL; + drmGetDevice2(dev->fd, 0, &dev_info); + struct wlr_drm_backend *drm = calloc(1, sizeof(*drm)); if (!drm) { wlr_log_errno(WLR_ERROR, "Allocation failed"); @@ -243,6 +247,9 @@ struct wlr_backend *wlr_drm_backend_create(struct wlr_session *session, drm->fd = dev->fd; drm->name = name; + drm->bus = get_drm_bus_str(dev_info); + drmFreeDevice(&dev_info); + if (parent != NULL) { drm->parent = get_drm_backend_from_backend(parent); diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 15cb181cf..458f05636 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -1603,6 +1603,92 @@ static drmModeModeInfo *connector_get_current_mode(struct wlr_drm_connector *wlr } } +static struct wlr_drm_connector *find_drm_connector_by_id(struct wlr_drm_backend *drm, uint32_t id) { + struct wlr_drm_connector *conn = NULL; + wl_list_for_each(conn, &drm->connectors, link) { + if (conn->id == id) { + return conn; + } + } + return NULL; +} + +static bool connector_write_root_port(struct wlr_drm_connector *wlr_conn, FILE *f) { + struct wlr_drm_backend *drm = wlr_conn->backend; + + size_t seq_num = 0; + struct wlr_drm_connector *c; + wl_list_for_each(c, &drm->connectors, link) { + seq_num++; + if (c == wlr_conn) { + break; + } + } + + return fprintf(f, "%s/connector-%zu", drm->bus, seq_num) > 0; +} + +static bool connector_write_port(struct wlr_drm_connector *wlr_conn, FILE *f); + +static bool connector_write_nested_port(struct wlr_drm_connector *wlr_conn, FILE *f) { + struct wlr_drm_backend *drm = wlr_conn->backend; + bool ok = false; + + size_t size = 0; + void *path_prop_data = get_drm_prop_blob(drm->fd, wlr_conn->id, wlr_conn->props.path, &size); + if (path_prop_data == NULL) { + goto out; + } + + uint32_t parent_conn_id = 0; + const char *child_path = NULL; + if (!parse_dp_mst_path(path_prop_data, &parent_conn_id, &child_path)) { + goto out; + } + struct wlr_drm_connector *parent = find_drm_connector_by_id(drm, parent_conn_id); + if (parent == NULL) { + goto out; + } + + if (!connector_write_port(parent, f)) { + goto out; + } + + ok = fprintf(f, "/mst-%s", child_path) > 0; + +out: + free(path_prop_data); + return ok; +} + +static bool connector_write_port(struct wlr_drm_connector *wlr_conn, FILE *f) { + if (wlr_conn->props.path != 0) { + return connector_write_nested_port(wlr_conn, f); + } else { + return connector_write_root_port(wlr_conn, f); + } +} + +static char *connector_get_port(struct wlr_drm_connector *wlr_conn) { + if (wlr_conn->backend->bus == NULL) { + return NULL; + } + + char *str = NULL; + size_t str_size = 0; + FILE *f = open_memstream(&str, &str_size); + if (f == NULL) { + return NULL; + } + + bool ok = connector_write_port(wlr_conn, f); + if (fclose(f) != 0 || !ok) { + return NULL; + } + + return str; +} + static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, const drmModeConnector *drm_conn) { struct wlr_drm_backend *drm = wlr_conn->backend; @@ -1741,6 +1827,9 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, wlr_output_set_description(output, description); free(subconnector); + + output->port = connector_get_port(wlr_conn); + wlr_conn->status = DRM_MODE_CONNECTED; return true; } diff --git a/backend/drm/util.c b/backend/drm/util.c index dd2b37351..9d3f9436e 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -281,3 +281,44 @@ void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay, }; snprintf(mode->name, sizeof(mode->name), "%dx%d", hdisplay, vdisplay); } + +char *get_drm_bus_str(const drmDevice *dev) { + char buf[128]; + switch (dev->bustype) { + case DRM_BUS_PCI:; + const drmPciBusInfo *pci = dev->businfo.pci; + snprintf(buf, sizeof(buf), "pci-%04" PRIx16 ":%02" PRIx8 ":%02" PRIx8 ".%" PRIu8, + pci->domain, pci->bus, pci->dev, pci->func); + return strdup(buf); + case DRM_BUS_PLATFORM:; + const drmPlatformBusInfo *platform = dev->businfo.platform; + size_t str_size = strlen("platform-") + strlen(platform->fullname) + 1; + char *str = malloc(str_size); + if (str == NULL) { + return NULL; + } + snprintf(str, str_size, "platform-%s", platform->fullname); + return str; + } + return NULL; +} + +bool parse_dp_mst_path(const char *path, uint32_t *parent_conn_id, const char **child_path) { + const char prefix[] = "mst:"; + if (strncmp(path, prefix, strlen(prefix)) != 0) { + return false; + } + path = &path[strlen(prefix)]; + + char *id_end; + errno = 0; + unsigned long id = strtoul(path, &id_end, 10); + if (errno != 0 || id_end[0] != '-' || id > UINT32_MAX) { + wlr_log(WLR_ERROR, "Malformed PATH DP-MST property: invalid parent connector ID"); + return false; + } + + *parent_conn_id = (uint32_t)id; + *child_path = &id_end[1]; + return true; +} diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index af4231f54..15a03ef6d 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -92,7 +92,7 @@ struct wlr_drm_backend { bool addfb2_modifiers; int fd; - char *name; + char *name, *bus; struct wlr_device *dev; struct liftoff_device *liftoff; diff --git a/include/backend/drm/util.h b/include/backend/drm/util.h index 9ba5f435e..af3eb9bff 100644 --- a/include/backend/drm/util.h +++ b/include/backend/drm/util.h @@ -38,4 +38,7 @@ void match_connectors_with_crtcs(size_t num_conns, size_t num_crtcs, const uint32_t prev_crtcs[static restrict num_crtcs], uint32_t new_crtcs[static restrict num_crtcs]); +char *get_drm_bus_str(const drmDevice *dev); +bool parse_dp_mst_path(const char *path, uint32_t *parent_conn_id, const char **child_path); + #endif diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 6a0dd0455..b1a3e80e9 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -186,6 +186,7 @@ struct wlr_output { char *name; char *description; // may be NULL char *make, *model, *serial; // may be NULL + char *port; // may be NULL int32_t phys_width, phys_height; // mm // Note: some backends may have zero modes diff --git a/types/output/output.c b/types/output/output.c index 1fb0347a0..69a652aa9 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -423,6 +423,7 @@ void wlr_output_finish(struct wlr_output *output) { free(output->make); free(output->model); free(output->serial); + free(output->port); } void wlr_output_destroy(struct wlr_output *output) {