[PATCH] Handle hotplugged devices from xorg.conf

Dan Nicholson dbn.lists at gmail.com
Fri Dec 5 11:21:28 PST 2008


Hotplug hooks are added to the HAL core to allow the DDX to handle device
hotplugging. If a registered hook returns False, config/hal will continue
processing the event. This allows the DDX to examine the type of device
and deciding whether to handle it or not.

For xorg, two hooks have been added: xf86HotplugAddDevice and
xf86HotplugRemoveDevice. Given a path to the device file from config/hal,
xorg.conf is searched for an InputDevice with matching Device or Path.
Devices that have been added statically from xorg.conf are skipped.

The config_info field is set for xorg.conf devices to distinguish where
they were added from:
  "hal:<udi>" - Hotplugged devices from config/hal
  "xfree86:<Identifier>" - Static xorg.conf devices
  "xfree86/hal:<udi>" - Hotplugged xorg.conf devices
This is used when finding duplicate devices as well as removing devices
in DeleteInputDeviceRequest so that config/hal devices will have their
data freed.

Signed-off-by: Dan Nicholson <dbn.lists at gmail.com>
---
 config/hal.c                   |   31 +++++++
 hw/xfree86/common/xf86Config.c |  189 ++++++++++++++++++++++++++++++++++++++++
 hw/xfree86/common/xf86Config.h |    3 +
 hw/xfree86/common/xf86Init.c   |   37 ++++++++
 hw/xfree86/common/xf86Xinput.c |   12 ++--
 include/hotplug.h              |   14 +++
 include/inputstr.h             |    2 +-
 7 files changed, 281 insertions(+), 7 deletions(-)

diff --git a/config/hal.c b/config/hal.c
index 8dfbb07..d9b51e4 100644
--- a/config/hal.c
+++ b/config/hal.c
@@ -58,6 +58,8 @@ struct xkb_options {
     char* options;
 };
 
+/* Allow the ddx to handle hotplugged devices. */
+static struct config_hal_hotplug_hook *hotplug_hook;
 
 static void
 remove_device(DeviceIntPtr dev)
@@ -79,6 +81,13 @@ device_removed(LibHalContext *ctx, const char *udi)
     DeviceIntPtr dev, next;
     char *value;
 
+    /* See if the ddx wants to handle this */
+    if (hotplug_hook && hotplug_hook->remove && hotplug_hook->remove(udi)) {
+        LogMessageVerb(X_INFO, 4,
+                       "config/hal: device removed in hook. Ignoring.\n");
+        return;
+    }
+
     value = xalloc(strlen(udi) + 5); /* "hal:" + NULL */
     if (!value)
         return;
@@ -255,6 +264,14 @@ device_added(LibHalContext *hal_ctx, const char *udi)
         goto unwind;
     }
 
+    /* See if the ddx wants to handle this */
+    if (hotplug_hook && hotplug_hook->add && hotplug_hook->add(path, udi)) {
+        LogMessageVerb(X_INFO, 4,
+                       "config/hal: device %s handled in hook. Ignoring.\n",
+                       name);
+        goto unwind;
+    }
+
     /* ok, grab options from hal.. iterate through all properties
     * and lets see if any of them are options that we can add */
     set = libhal_device_get_all_properties(hal_ctx, udi, &error);
@@ -529,6 +546,18 @@ out_err:
     return;
 }
 
+void
+config_hal_hotplug_set_hook(struct config_hal_hotplug_hook *hook)
+{
+    hotplug_hook = hook;
+}
+
+void
+config_hal_hotplug_clear_hook(void)
+{
+    hotplug_hook = NULL;
+}
+
 static struct config_hal_info hal_info;
 static struct config_dbus_core_hook hook = {
     .connect = connect_hook,
@@ -543,6 +572,8 @@ config_hal_init(void)
     hal_info.system_bus = NULL;
     hal_info.hal_ctx = NULL;
 
+    hotplug_hook = NULL;
+
     if (!config_dbus_core_add_hook(&hook)) {
         LogMessage(X_ERROR, "config/hal: failed to add D-Bus hook\n");
         return 0;
diff --git a/hw/xfree86/common/xf86Config.c b/hw/xfree86/common/xf86Config.c
index a03d977..e953061 100644
--- a/hw/xfree86/common/xf86Config.c
+++ b/hw/xfree86/common/xf86Config.c
@@ -51,6 +51,10 @@
 #include <grp.h>
 #endif
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
 #include "xf86.h"
 #include "xf86Parser.h"
 #include "xf86tokens.h"
@@ -2614,3 +2618,188 @@ xf86PathIsSafe(const char *path)
 {
     return (xf86pathIsSafe(path) != 0);
 }
+
+/* See whether a device has already been added either through xorg.conf
+ * hotplugging or a static layout. */
+static Bool
+DeviceIsDuplicate(IDevPtr idev, const char *config_info)
+{
+    DeviceIntPtr dev;
+
+    for (dev = inputInfo.devices; dev; dev = dev->next) {
+        if (!dev->config_info)
+            continue;
+        if (strcmp(dev->config_info, config_info) == 0)
+            return True;
+        if ((strncmp(dev->config_info, "xfree86:", 8) == 0) &&
+            (strcmp(&dev->config_info[8], idev->identifier) == 0))
+            return True;
+    }
+
+    for (dev = inputInfo.off_devices; dev; dev = dev->next) {
+        if (!dev->config_info)
+            continue;
+        if (strcmp(dev->config_info, config_info) == 0)
+            return True;
+        if ((strncmp(dev->config_info, "xfree86:", 8) == 0) &&
+            (strcmp(&dev->config_info[8], idev->identifier) == 0))
+            return True;
+    }
+
+    return False;
+}
+
+/* Add a hotplugged device from the configuration. It it's already been
+ * added or is a core device, skip it. */
+static Bool
+AddHotplugDevice(XF86ConfInputPtr conf, const char *udi)
+{
+    IDevPtr idev = NULL;
+    DeviceIntPtr pdev;
+    char *config_info = NULL;
+    Bool ret = False;
+    int rc;
+
+    idev = xcalloc(1, sizeof(*idev));
+    if (!idev)
+        goto unwind;
+
+    if (!configInput(idev, conf, X_CONFIG))
+        goto unwind;
+
+    config_info = xalloc(strlen(udi) + 13); /* "xfree86/hal:" and NULL */
+    if (!config_info) {
+        xf86Msg(X_ERROR, "xfree86/hal: Could not allocate name\n");
+        goto unwind;
+    }
+    sprintf(config_info, "xfree86/hal:%s", udi);
+
+    /* Skip duplicate devices, but say they were handled. */
+    if (DeviceIsDuplicate(idev, config_info)) {
+        xf86Msg(X_WARNING, "xfree86/hal: Device %s already added. Ignoring.\n",
+                idev->identifier);
+        ret = True;
+        goto unwind;
+    }
+
+    /* Add the device and enable it if AutoEnableDevices is true. */
+    xf86Msg(X_INFO, "xfree86/hal: Adding input device %s\n", idev->identifier);
+    rc = xf86NewInputDevice(idev, &pdev, xf86Info.autoEnableDevices);
+    if (rc != Success) {
+        xf86Msg(X_ERROR, "xfree86/hal: xf86NewInputDevice failed (%d)\n", rc);
+        goto unwind;
+    }
+
+    /* Store the config_info so that we can skip duplicates later. */
+    for(; pdev; pdev = pdev->next) {
+        if (pdev->config_info)
+            xfree(pdev->config_info);
+        pdev->config_info = xstrdup(config_info);
+    }
+    xfree(config_info);
+
+    return True;
+
+unwind:
+    if (idev)
+        xfree(idev);
+    if (config_info)
+        xfree(config_info);
+    return ret;
+}
+
+/* See if we can find an InputDevice in the configuration whose Device
+ * matches the one being hotplugged. If so, add it. */
+Bool
+xf86HotplugAddDevice(const char *path, const char *udi)
+{
+    struct stat device_stat;
+    XF86ConfInputPtr conf;
+
+    if (!xf86configptr) {
+        xf86Msg(X_ERROR,
+                "xfree86/hal: Cannot access global config data structure\n");
+        return False;
+    }
+
+    if (stat(path, &device_stat) < 0)
+        return False;
+
+    for (conf = xf86configptr->conf_input_lst; conf; conf = conf->list.next)
+    {
+        char *conf_path;
+        struct stat conf_stat;
+
+        if (!conf->inp_option_lst)
+            continue;
+
+        conf_path = xf86FindOptionValue(conf->inp_option_lst, "Device");
+        if (!conf_path)
+            conf_path = xf86FindOptionValue(conf->inp_option_lst, "Path");
+        if (!conf_path)
+            continue;
+
+        if (stat(conf_path, &conf_stat) < 0)
+            continue;
+
+        /* If both paths have the same mode with equal device numbers,
+         * then we have a match. */
+        if ((device_stat.st_mode == conf_stat.st_mode) &&
+            (device_stat.st_rdev == conf_stat.st_rdev))
+            break;
+    }
+
+    if (!conf)
+        return False;
+
+    return AddHotplugDevice(conf, udi);
+}
+
+static void
+RemoveHotplugDevice(DeviceIntPtr dev)
+{
+    xf86Msg(X_INFO, "xfree86/hal: removing device %s\n", dev->name);
+
+    /* Call PIE here so we don't try to dereference a device that's
+     * already been removed. */
+    OsBlockSignals();
+    ProcessInputEvents();
+    DeleteInputDeviceRequest(dev);
+    OsReleaseSignals();
+}
+
+Bool
+xf86HotplugRemoveDevice(const char *udi)
+{
+    Bool ret = False;
+    char *config_info;
+    DeviceIntPtr dev, next;
+
+    config_info = xalloc(strlen(udi) + 13); /* "xfree86/hal:" and NULL */
+    if (!config_info) {
+        xf86Msg(X_ERROR, "xfree86/hal: Could not allocate name\n");
+        goto unwind;
+    }
+    sprintf(config_info, "xfree86/hal:%s", udi);
+
+    for (dev = inputInfo.devices; dev; dev = next) {
+        next = dev->next;
+        if (dev->config_info && strcmp(config_info, dev->config_info) == 0) {
+            RemoveHotplugDevice(dev);
+            ret = True;
+        }
+    }
+
+    for (dev = inputInfo.off_devices; dev; dev = next) {
+        next = dev->next;
+        if (dev->config_info && strcmp(config_info, dev->config_info) == 0) {
+            RemoveHotplugDevice(dev);
+            ret = True;
+        }
+    }
+
+unwind:
+    if (config_info)
+        xfree(config_info);
+    return ret;
+}
diff --git a/hw/xfree86/common/xf86Config.h b/hw/xfree86/common/xf86Config.h
index a174e46..3d10825 100644
--- a/hw/xfree86/common/xf86Config.h
+++ b/hw/xfree86/common/xf86Config.h
@@ -71,4 +71,7 @@ GDevPtr autoConfigDevice(GDevPtr preconf_device);
 char* chooseVideoDriver(void);
 int xchomp(char *line);
 
+Bool xf86HotplugAddDevice(const char *path, const char *udi);
+Bool xf86HotplugRemoveDevice(const char *udi);
+
 #endif /* _xf86_config_h */
diff --git a/hw/xfree86/common/xf86Init.c b/hw/xfree86/common/xf86Init.c
index eacf344..509d814 100644
--- a/hw/xfree86/common/xf86Init.c
+++ b/hw/xfree86/common/xf86Init.c
@@ -88,6 +88,10 @@
 #include "dpmsproc.h"
 #endif
 
+#ifdef CONFIG_HAL
+#include <hotplug.h>
+#endif
+
 #include <pciaccess.h>
 #include "Pci.h"
 #include "xf86Bus.h"
@@ -121,6 +125,13 @@ static int numFormats = 6;
 #endif
 static Bool formatsDone = FALSE;
 
+#ifdef CONFIG_HAL
+static struct config_hal_hotplug_hook hotplug_hook = {
+    .add    = xf86HotplugAddDevice,
+    .remove = xf86HotplugRemoveDevice,
+};
+#endif
+
 #ifndef OSNAME
 #define OSNAME " unknown"
 #endif
@@ -837,6 +848,11 @@ InitOutput(ScreenInfo *pScreenInfo, int argc, char **argv)
       xfree(modulelist);
     }
 
+#ifdef CONFIG_HAL
+    /* Register the hal hotplug handler. */
+    config_hal_hotplug_set_hook(&hotplug_hook);
+#endif
+
     /* Load all input driver modules specified in the config file. */
     if ((modulelist = xf86InputDriverlistFromConfig())) {
       xf86LoadModules(modulelist, NULL);
@@ -1317,6 +1333,8 @@ InitInput(argc, argv)
 
     /* Call the PreInit function for each input device instance. */
     for (pDev = xf86ConfigLayout.inputs; pDev && *pDev; pDev++) {
+        char *config_info;
+
         /* Replace obsolete keyboard driver with kbd */
         if (!xf86NameCmp((*pDev)->driver, "keyboard")) {
             strcpy((*pDev)->driver, "kbd");
@@ -1325,6 +1343,21 @@ InitInput(argc, argv)
         /* If one fails, the others will too */
         if (xf86NewInputDevice(*pDev, &dev, TRUE) == BadAlloc)
             break;
+
+        /* "xfree86:" and NULL */
+        config_info = xalloc(strlen((*pDev)->identifier) + 9);
+        if (!config_info) {
+            xf86Msg(X_ERROR, "Could not allocate name\n");
+            continue;
+        }
+        sprintf(config_info, "xfree86:%s", (*pDev)->identifier);
+
+        for (; dev; dev = dev->next) {
+            if (dev->config_info)
+                xfree(dev->config_info);
+            dev->config_info = xstrdup(config_info);
+        }
+        xfree(config_info);
     }
 
     mieqInit();
@@ -1392,6 +1425,10 @@ ddxGiveUp()
     xf86OSPMClose = NULL;
 #endif
 
+#ifdef CONFIG_HAL
+    config_hal_hotplug_clear_hook();
+#endif
+
     xf86AccessLeaveState();
 
     for (i = 0; i < xf86NumScreens; i++) {
diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c
index 38fd1ec..2c5a0c2 100644
--- a/hw/xfree86/common/xf86Xinput.c
+++ b/hw/xfree86/common/xf86Xinput.c
@@ -654,6 +654,7 @@ DeleteInputDeviceRequest(DeviceIntPtr pDev)
     IDevRec *idev;
     IDevPtr *it;
     Bool isMaster = pDev->isMaster;
+    Bool isHalDev;
 
     if (pInfo) /* need to get these before RemoveDevice */
     {
@@ -661,6 +662,9 @@ DeleteInputDeviceRequest(DeviceIntPtr pDev)
         idev = pInfo->conf_idev;
     }
 
+    isHalDev = (pDev->config_info &&
+                strncmp(pDev->config_info, "hal:", 4) == 0);
+
     OsBlockSignals();
     RemoveDevice(pDev);
 
@@ -671,12 +675,8 @@ DeleteInputDeviceRequest(DeviceIntPtr pDev)
         else
             xf86DeleteInput(pInfo, 0);
 
-        /* devices added through HAL aren't in the config layout */
-        it = xf86ConfigLayout.inputs;
-        while(*it && *it != idev)
-            it++;
-
-        if (!(*it)) /* end of list, not in the layout */
+        /* devices added through HAL aren't in the configuration */
+        if (isHalDev)
         {
             xfree(idev->driver);
             xfree(idev->identifier);
diff --git a/include/hotplug.h b/include/hotplug.h
index d074df3..68ff7ae 100644
--- a/include/hotplug.h
+++ b/include/hotplug.h
@@ -29,4 +29,18 @@
 extern _X_EXPORT void config_init(void);
 extern _X_EXPORT void config_fini(void);
 
+#ifdef CONFIG_HAL
+typedef Bool (*config_hal_hotplug_add_hook)(const char *path,
+                                            const char *udi);
+typedef Bool (*config_hal_hotplug_remove_hook)(const char *udi);
+
+struct config_hal_hotplug_hook {
+    config_hal_hotplug_add_hook add;
+    config_hal_hotplug_remove_hook remove;
+};
+
+void config_hal_hotplug_set_hook(struct config_hal_hotplug_hook *hook);
+void config_hal_hotplug_clear_hook(void);
+#endif
+
 #endif /* HOTPLUG_H */
diff --git a/include/inputstr.h b/include/inputstr.h
index 039d5bc..fe2a515 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -449,7 +449,7 @@ typedef struct _DeviceIntRec {
 #else
     void                *pad0;
 #endif
-    char                *config_info; /* used by the hotplug layer */
+    char                *config_info; /* unique configuration identifier */
     PrivateRec		*devPrivates;
     int			nPrivates;
     DeviceUnwrapProc    unwrapProc;
-- 
1.5.6.5




More information about the xorg mailing list