[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