[PATCH 8/8] modesetting: Add output hotplug support

Daniel Martin daniel.martin at secunet.com
Fri Nov 28 02:20:53 PST 2014


From: Daniel Martin <consume.noise at gmail.com>

When receiving a hotplug uevent, check if we have to add or remove
outputs and act accordingly.

Signed-off-by: Daniel Martin <consume.noise at gmail.com>
---
With this patch we create or destroy outputs as a reaction to uevents.

Due to my limited experience, the solution is either total crap, cause
I'm doing it wrong, or it is that complicated, cause the server never
had disappearing outputs in mind, or a combination of both.

 hw/xfree86/drivers/modesetting/drmmode_display.c | 150 ++++++++++++++++++++++-
 1 file changed, 148 insertions(+), 2 deletions(-)

diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index 474e5b6..876af68 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -43,6 +43,7 @@
 #include "xf86Crtc.h"
 #include "drmmode_display.h"
 
+#include <xf86RandR12.h>
 #include <cursorstr.h>
 
 #include <X11/extensions/dpmsconst.h>
@@ -785,8 +786,10 @@ drmmode_output_destroy(xf86OutputPtr output)
         free(drmmode_output->props[i].atoms);
     }
     free(drmmode_output->props);
-    for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) {
-        drmModeFreeEncoder(drmmode_output->mode_encoders[i]);
+    if (drmmode_output->mode_output) {
+        for (i = 0; i < drmmode_output->mode_output->count_encoders; i++) {
+            drmModeFreeEncoder(drmmode_output->mode_encoders[i]);
+        }
     }
     free(drmmode_output->mode_encoders);
     drmModeFreeConnector(drmmode_output->mode_output);
@@ -1488,12 +1491,155 @@ drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn)
 }
 
 #ifdef CONFIG_UDEV_KMS
+static Bool
+drmmode_output_add(drmmode_ptr drmmode, int nth_connector)
+{
+    ScrnInfoPtr scrn = drmmode->scrn;
+    xf86OutputPtr output = drmmode_output_init(scrn, drmmode, nth_connector);
+
+    xf86DrvMsg(scrn->scrnIndex, X_INFO, "Adding output %s\n", output->name);
+
+    if (!output)
+        return FALSE;
+
+    output->randr_output = RROutputCreate(scrn->pScreen, output->name,
+                                          strlen(output->name), output);
+    if (!output->randr_output) {
+        xf86OutputDestroy(output);
+        return FALSE;
+    }
+
+    drmmode_output_create_resources(output);
+    RRPostPendingProperties(output->randr_output);
+
+    return TRUE;
+}
+
+static Bool
+drmmode_output_del(xf86OutputPtr output)
+{
+    ScrnInfoPtr scrn = output->scrn;
+    rrScrPrivPtr pScrPriv = rrGetScrPriv(scrn->pScreen);
+    int i, j;
+
+    output->funcs->dpms(output, DPMSModeOff);
+
+    /* Remove the RROutput from RRCrtc, if any. */
+    if (output->randr_output->crtc) {
+        RRCrtcPtr rr_crtc = output->randr_output->crtc;
+
+        if (rr_crtc->numOutputs < 2) {
+            RRCrtcSet(rr_crtc, NULL, 0, 0, 0, 0, NULL);
+        } else {
+            RROutputPtr *rr_outputs = calloc(rr_crtc->numOutputs,
+                                             sizeof(RROutputPtr));
+            if (!rr_outputs)
+                return FALSE;
+
+            for (i = 0, j = 0; i < rr_crtc->numOutputs; i++) {
+                if (rr_crtc->outputs[i] != output->randr_output) {
+                    rr_outputs[j] = rr_crtc->outputs[i];
+                }
+                j++;
+            }
+
+            RRCrtcSet(rr_crtc, rr_crtc->mode, rr_crtc->x, rr_crtc->y,
+                      rr_crtc->rotation, rr_crtc->numOutputs - 1, rr_outputs);
+            free(rr_outputs);
+        }
+    }
+
+    /* Remove the RROutput from the list of available outputs. */
+    for (i = 0; i < pScrPriv->numOutputs; i++) {
+        if (pScrPriv->outputs[i] == output->randr_output) {
+             memmove(&pScrPriv->outputs[i], &pScrPriv->outputs[i + 1],
+                     ((pScrPriv->numOutputs - (i + 1)) * sizeof(RROutputPtr)));
+             pScrPriv->numOutputs--;
+             break;
+        }
+    }
+
+    xf86DrvMsg(scrn->scrnIndex, X_INFO, "Removing output %s\n", output->name);
+
+    RROutputDestroy(output->randr_output);
+    output->randr_output = NULL;
+
+    xf86OutputDestroy(output);
+
+    return TRUE;
+}
+
 void
 drmmode_output_eval(drmmode_ptr drmmode)
 {
     ScrnInfoPtr scrn = drmmode->scrn;
+    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+
+    drmModeResPtr mode_res = NULL;
+
+    int old_num_output = xf86_config->num_output;
+    xf86OutputPtr *output_del_list = NULL;
+
+    Bool changed = FALSE;
+    int i, j;
+
+    mode_res = drmModeGetResources(drmmode->fd);
+    if (mode_res) {
+        drmModeFreeResources(drmmode->mode_res);
+        drmmode->mode_res = mode_res;
+    } else
+        return;
+
+    output_del_list = calloc(xf86_config->num_output + 1,
+                             sizeof(*output_del_list));
+    if (!output_del_list)
+        goto out;
+
+    /* Track all currently known outputs in output_del_list. We'll NULL each
+     * entry which we can match with one connector in mode_res->connectors.
+     * Later, every entry not being NULL is an output we have to delete. */
+    memcpy(output_del_list, xf86_config->output,
+           xf86_config->num_output * sizeof(*output_del_list));
+
+    /* Check for outputs we already know or have to add. */
+    for (i = 0; i < mode_res->count_connectors; i++) {
+        Bool found = FALSE;
+
+        for (j = 0; j < old_num_output; j++) {
+            drmmode_output_private_ptr drmmode_output;
+
+            if (output_del_list[j] == NULL)
+                continue;
+
+            drmmode_output = output_del_list[j]->driver_private;
+            if (drmmode_output->output_id == mode_res->connectors[i]) {
+                found = TRUE;
+                break;
+            }
+        }
+
+        if (found)
+            /* Found the output, NULL the entry. */
+            output_del_list[j] = NULL;
+        else
+            changed |= drmmode_output_add(drmmode, i);
+    }
+
+    for (i = 0; i < old_num_output; i++)
+        if (output_del_list[i] != NULL)
+            changed |= drmmode_output_del(output_del_list[i]);
+
+    if (changed) {
+        xf86DisableUnusedFunctions(scrn);
+        drmmode_clones_init(scrn, drmmode);
+
+        xf86RandR12TellChanged(xf86ScrnToScreen(scrn));
+    }
 
     RRGetInfo(xf86ScrnToScreen(scrn), TRUE);
+
+out:
+    free(output_del_list);
 }
 
 static void
-- 
2.1.3



More information about the xorg-devel mailing list