From: Daniel Martin <consume.no...@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.no...@gmail.com> Reviewed-by: Keith Packard <kei...@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 _______________________________________________ xorg-devel@lists.x.org: X.Org development Archives: http://lists.x.org/archives/xorg-devel Info: http://lists.x.org/mailman/listinfo/xorg-devel