diff mbox

intel: Listen for hotplug uevents (V3)

Message ID 1286214218-10551-1-git-send-email-keithp@keithp.com (mailing list archive)
State New, archived
Headers show

Commit Message

Keith Packard Oct. 4, 2010, 5:43 p.m. UTC
None
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index e066b3d..58fa929 100644
--- a/configure.ac
+++ b/configure.ac
@@ -47,6 +47,11 @@  AC_PROG_LIBTOOL
 PKG_CHECK_MODULES(GEN4ASM, [intel-gen4asm >= 1.0], [gen4asm=yes], [gen4asm=no])
 AM_CONDITIONAL(HAVE_GEN4ASM, test x$gen4asm = xyes)
 
+PKG_CHECK_MODULES(UDEV, [libudev], [udev=yes], [udev=no])
+if test x"$udev" = xyes; then
+	AC_DEFINE(HAVE_UDEV,1,[Enable udev-based monitor hotplug detection])
+fi
+
 AH_TOP([#include "xorg-server.h"])
 
 # Define a configure option for an alternate module directory
diff --git a/man/intel.man b/man/intel.man
index 31860f2..fa7f7dc 100644
--- a/man/intel.man
+++ b/man/intel.man
@@ -192,6 +192,12 @@  Enable XvMC driver. Current support MPEG2 MC on 915/945 and G33 series.
 User should provide absolute path to libIntelXvMC.so in XvMCConfig file.
 .IP
 Default: Disabled.
+.TP
+.BI "Option \*qHotplug\*q \*q" boolean \*q
+This option controls whether the driver automatically notifies
+applications when monitors are connected or disconnected.
+.IP
+Default: enabled.
 
 .SH OUTPUT CONFIGURATION
 On 830M and better chipsets, the driver supports runtime configuration of
diff --git a/src/Makefile.am b/src/Makefile.am
index b0a1cf7..ab70207 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -26,13 +26,13 @@  SUBDIRS = xvmc render_program legacy
 # _ladir passes a dummy rpath to libtool so the thing will actually link
 # TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc.
 
-AM_CFLAGS = @CWARNFLAGS@ @XORG_CFLAGS@ @DRM_CFLAGS@ @DRI_CFLAGS@ \
+AM_CFLAGS = @CWARNFLAGS@ @XORG_CFLAGS@ @UDEV_CFLAGS@ @DRM_CFLAGS@ @DRI_CFLAGS@ \
 	@PCIACCESS_CFLAGS@ -I$(top_srcdir)/uxa -I$(top_srcdir)/src/render_program
 
 intel_drv_la_LTLIBRARIES = intel_drv.la
 intel_drv_la_LDFLAGS = -module -avoid-version
 intel_drv_ladir = @moduledir@/drivers
-intel_drv_la_LIBADD = -lm @DRM_LIBS@ -ldrm_intel ../uxa/libuxa.la legacy/liblegacy.la
+intel_drv_la_LIBADD = @UDEV_LIBS@ -lm @DRM_LIBS@ -ldrm_intel ../uxa/libuxa.la legacy/liblegacy.la
 intel_drv_la_LIBADD += @PCIACCESS_LIBS@
 
 NULL:=#
diff --git a/src/intel.h b/src/intel.h
index b816aeb..1234b94 100644
--- a/src/intel.h
+++ b/src/intel.h
@@ -34,6 +34,10 @@  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  *
  */
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 #if 0
 #define I830DEBUG
 #endif
@@ -70,6 +74,10 @@  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 #include "intel_driver.h"
 
+#if HAVE_UDEV
+#include <libudev.h>
+#endif
+
 #include "uxa.h"
 /* XXX
  * The X server gained an *almost* identical implementation in 1.9.
@@ -435,6 +443,10 @@  typedef struct intel_screen_private {
 	 */
 	Bool fallback_debug;
 	unsigned debug_flush;
+#if HAVE_UDEV
+	struct udev_monitor *uevent_monitor;
+	InputHandlerProc uevent_handler;
+#endif
 } intel_screen_private;
 
 enum {
diff --git a/src/intel_driver.c b/src/intel_driver.c
index 4391672..63b26a4 100644
--- a/src/intel_driver.c
+++ b/src/intel_driver.c
@@ -107,6 +107,7 @@  typedef enum {
    OPTION_DEBUG_FLUSH_BATCHES,
    OPTION_DEBUG_FLUSH_CACHES,
    OPTION_DEBUG_WAIT,
+   OPTION_HOTPLUG,
 } I830Opts;
 
 static OptionInfoRec I830Options[] = {
@@ -125,6 +126,7 @@  static OptionInfoRec I830Options[] = {
    {OPTION_DEBUG_FLUSH_BATCHES, "DebugFlushBatches", OPTV_BOOLEAN, {0}, FALSE},
    {OPTION_DEBUG_FLUSH_CACHES, "DebugFlushCaches", OPTV_BOOLEAN, {0}, FALSE},
    {OPTION_DEBUG_WAIT, "DebugWait", OPTV_BOOLEAN, {0}, FALSE},
+   {OPTION_HOTPLUG,	"HotPlug",	OPTV_BOOLEAN,	{0},	TRUE},
    {-1,			NULL,		OPTV_NONE,	{0},	FALSE}
 };
 /* *INDENT-ON* */
@@ -801,6 +803,110 @@  intel_flush_callback(CallbackListPtr *list,
 	}
 }
 
+#if HAVE_UDEV
+static void
+I830HandleUEvents(int fd, void *closure)
+{
+    ScrnInfoPtr scrn = closure;
+	intel_screen_private *intel = intel_get_screen_private(scrn);
+    struct udev_device *dev;
+    const char *hotplug;
+    struct stat s;
+    dev_t udev_devnum;
+
+    dev = udev_monitor_receive_device(intel->uevent_monitor);
+    if (!dev)
+	return;
+
+    udev_devnum = udev_device_get_devnum(dev);
+    fstat(intel->drmSubFD, &s);
+    /*
+     * Check to make sure this event is directed at our
+     * device (by comparing dev_t values), then make
+     * sure it's a hotplug event (HOTPLUG=1)
+     */
+
+    hotplug = udev_device_get_property_value(dev, "HOTPLUG");
+
+    if (memcmp(&s.st_rdev, &udev_devnum, sizeof (dev_t)) == 0 &&
+	hotplug && atoi(hotplug) == 1)
+	RRGetInfo(screenInfo.screens[scrn->scrnIndex], TRUE);
+
+    udev_device_unref(dev);
+}
+
+static void
+I830UeventInit(ScrnInfoPtr scrn)
+{
+    intel_screen_private *intel = intel_get_screen_private(scrn);
+    struct udev *u;
+    struct udev_monitor *mon;
+    Bool hotplug;
+    MessageType from = X_CONFIG;
+
+    if (!xf86GetOptValBool(intel->Options, OPTION_HOTPLUG, &hotplug)) {
+	from = X_DEFAULT;
+	hotplug = TRUE;
+    }
+
+    xf86DrvMsg(scrn->scrnIndex, from, "hotplug detection: \"%s\"\n",
+	       hotplug ? "enabled" : "disabled");
+    if (!hotplug)
+	return;
+
+    u = udev_new();
+    if (!u)
+	return;
+
+    mon = udev_monitor_new_from_netlink(u, "udev");
+
+    if (!mon) {
+	udev_unref(u);
+	return;
+    }
+
+    if (udev_monitor_filter_add_match_subsystem_devtype(mon,
+							"drm",
+							"drm_minor") < 0 ||
+	udev_monitor_enable_receiving(mon) < 0)
+    {
+	udev_monitor_unref(mon);
+	udev_unref(u);
+	return;
+    }
+
+    intel->uevent_handler =
+	xf86AddGeneralHandler(udev_monitor_get_fd(mon),
+			      I830HandleUEvents,
+			      scrn);
+    if (!intel->uevent_handler) {
+	udev_monitor_unref(mon);
+	udev_unref(u);
+	return;
+    }
+
+    intel->uevent_monitor = mon;
+}
+
+static void
+I830UeventFini(ScrnInfoPtr scrn)
+{
+    intel_screen_private *intel = intel_get_screen_private(scrn);
+
+    if (intel->uevent_handler)
+    {
+	struct udev *u = udev_monitor_get_udev(intel->uevent_monitor);
+
+	xf86RemoveGeneralHandler(intel->uevent_handler);
+
+	udev_monitor_unref(intel->uevent_monitor);
+	udev_unref(u);
+	intel->uevent_handler = NULL;
+	intel->uevent_monitor = NULL;
+    }
+}
+#endif /* HAVE_UDEV */
+
 static Bool
 I830ScreenInit(int scrnIndex, ScreenPtr screen, int argc, char **argv)
 {
@@ -1007,6 +1113,10 @@  I830ScreenInit(int scrnIndex, ScreenPtr screen, int argc, char **argv)
 
 	intel->suspended = FALSE;
 
+#if HAVE_UDEV
+	I830UeventInit(scrn);
+#endif
+
 	return uxa_resources_init(screen);
 }
 
@@ -1088,6 +1198,10 @@  static Bool I830CloseScreen(int scrnIndex, ScreenPtr screen)
 	ScrnInfoPtr scrn = xf86Screens[scrnIndex];
 	intel_screen_private *intel = intel_get_screen_private(scrn);
 
+#if HAVE_UDEV
+	I830UeventFini(scrn);
+#endif
+
 	if (scrn->vtSema == TRUE) {
 		I830LeaveVT(scrnIndex, 0);
 	}