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

Daniel Martin daniel.martin at secunet.com
Mon Dec 8 03:03:07 PST 2014


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

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

v2: Move log message in drmmode_output_add() below 'if (!output)' to
    prevent possible NULL dereference.

Signed-off-by: Daniel Martin <consume.noise at gmail.com>
Reviewed-by: Keith Packard <keithp at keithp.com>
---
 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 ea701f1..23be9ef 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);
+
+    if (!output)
+        return FALSE;
+
+    xf86DrvMsg(scrn->scrnIndex, X_INFO, "Adding output %s\n", output->name);
+
+    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