1>From d96e2bd2a2b48ede527ad7071d3e0eeda9861b73 Mon Sep 17 00:00:00 2001 2From: Robert Millan <rmh@debian.org> 3Date: Mon, 24 Feb 2014 23:22:57 +0100 4Subject: [PATCH] Add devd config backend for FreeBSD (and GNU/kFreeBSD) 5 6Based on original code by Baptiste Daroussin, with some fixes made 7by Koop Mast and myself. 8 9Signed-off-by: Robert Millan <rmh@freebsd.org> 10 11v2 - Emilio Pozuelo Monfort <pochu@debian.org> 12 13 - Ported to NotifyFd API. 14--- 15 config/Makefile.am | 4 + 16 config/config-backends.h | 5 + 17 config/config.c | 5 + 18 config/devd.c | 387 +++++++++++++++++++++++++++++++++++++++ 19 configure.ac | 16 ++ 20 hw/xfree86/common/xf86Config.c | 7 +- 21 hw/xfree86/common/xf86Globals.c | 3 +- 22 include/dix-config.h.in | 3 + 23 8 files changed, 427 insertions(+), 3 deletions(-) 24 create mode 100644 config/devd.c 25 26--- a/config/Makefile.am 27+++ b/config/Makefile.am 28@@ -34,6 +34,10 @@ if CONFIG_WSCONS 29 libconfig_la_SOURCES += wscons.c 30 endif # CONFIG_WSCONS 31 32+if CONFIG_DEVD 33+libconfig_la_SOURCES += devd.c 34+endif 35+ 36 endif # !CONFIG_HAL 37 38 endif # !CONFIG_UDEV 39--- a/config/config-backends.h 40+++ b/config/config-backends.h 41@@ -44,3 +44,8 @@ void config_hal_fini(void); 42 int config_wscons_init(void); 43 void config_wscons_fini(void); 44 #endif 45+ 46+#ifdef CONFIG_DEVD 47+int config_devd_init(void); 48+void config_devd_fini(void); 49+#endif 50--- a/config/config.c 51+++ b/config/config.c 52@@ -55,6 +55,9 @@ config_init(void) 53 #elif defined(CONFIG_WSCONS) 54 if (!config_wscons_init()) 55 ErrorF("[config] failed to initialise wscons\n"); 56+#elif defined(CONFIG_DEVD) 57+ if (!config_devd_init()) 58+ ErrorF("[config] failed to initialise devd\n"); 59 #endif 60 } 61 62@@ -67,6 +70,8 @@ config_fini(void) 63 config_hal_fini(); 64 #elif defined(CONFIG_WSCONS) 65 config_wscons_fini(); 66+#elif defined(CONFIG_DEVD) 67+ config_devd_fini(); 68 #endif 69 } 70 71--- /dev/null 72+++ b/config/devd.c 73@@ -0,0 +1,375 @@ 74+/* 75+ * Copyright © 2012 Baptiste Daroussin 76+ * Copyright © 2014 Robert Millan 77+ * 78+ * Permission is hereby granted, free of charge, to any person obtaining a 79+ * copy of this software and associated documentation files (the "Software"), 80+ * to deal in the Software without restriction, including without limitation 81+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, 82+ * and/or sell copies of the Software, and to permit persons to whom the 83+ * Software is furnished to do so, subject to the following conditions: 84+ * 85+ * The above copyright notice and this permission notice (including the next 86+ * paragraph) shall be included in all copies or substantial portions of the 87+ * Software. 88+ * 89+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 90+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 91+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 92+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 93+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 94+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 95+ * DEALINGS IN THE SOFTWARE. 96+ * 97+ * Author: Baptiste Daroussin <bapt@FreeBSD.org> 98+ */ 99+ 100+#ifdef HAVE_DIX_CONFIG_H 101+#include <dix-config.h> 102+#endif 103+ 104+#include <sys/types.h> 105+#include <sys/socket.h> 106+#include <sys/sysctl.h> 107+#include <sys/un.h> 108+ 109+#include <ctype.h> 110+#include <errno.h> 111+#include <fcntl.h> 112+#include <stdlib.h> 113+#include <stdio.h> 114+#include <stdarg.h> 115+#include <stdbool.h> 116+#include <unistd.h> 117+ 118+#include "input.h" 119+#include "inputstr.h" 120+#include "hotplug.h" 121+#include "config-backends.h" 122+#include "os.h" 123+ 124+#define DEVD_SOCK_PATH "/var/run/devd.pipe" 125+ 126+#define DEVD_EVENT_ADD '+' 127+#define DEVD_EVENT_REMOVE '-' 128+ 129+static int sock_devd = -1; 130+ 131+struct hw_type { 132+ const char *driver; 133+ int flag; 134+ const char *xdriver; 135+}; 136+ 137+static struct hw_type hw_types[] = { 138+ {"ukbd", ATTR_KEYBOARD, "kbd"}, 139+ {"atkbd", ATTR_KEYBOARD, "kbd"}, 140+ {"ums", ATTR_POINTER, "mouse"}, 141+ {"psm", ATTR_POINTER, "mouse"}, 142+ {"uhid", ATTR_POINTER, "mouse"}, 143+ {"joy", ATTR_JOYSTICK, NULL}, 144+ {"atp", ATTR_TOUCHPAD, NULL}, 145+ {"uep", ATTR_TOUCHSCREEN, NULL}, 146+ {NULL, -1, NULL}, 147+}; 148+ 149+static bool 150+sysctl_exists(const char *format, ...) 151+{ 152+ va_list args; 153+ char *name = NULL; 154+ size_t len; 155+ int ret; 156+ 157+ if (format == NULL) 158+ return false; 159+ 160+ va_start(args, format); 161+ vasprintf(&name, format, args); 162+ va_end(args); 163+ 164+ ret = sysctlbyname(name, NULL, &len, NULL, 0); 165+ 166+ if (ret == -1) 167+ len = 0; 168+ 169+ free(name); 170+ return (len > 0); 171+} 172+ 173+static char * 174+sysctl_get_str(const char *format, ...) 175+{ 176+ va_list args; 177+ char *name = NULL; 178+ char *dest = NULL; 179+ size_t len; 180+ 181+ if (format == NULL) 182+ return NULL; 183+ 184+ va_start(args, format); 185+ vasprintf(&name, format, args); 186+ va_end(args); 187+ 188+ if (sysctlbyname(name, NULL, &len, NULL, 0) == 0) { 189+ dest = malloc(len + 1); 190+ if (!dest) 191+ goto unwind; 192+ if (sysctlbyname(name, dest, &len, NULL, 0) == 0) 193+ dest[len] = '\0'; 194+ else { 195+ free(dest); 196+ dest = NULL; 197+ } 198+ } 199+ 200+ unwind: 201+ free(name); 202+ return dest; 203+} 204+ 205+static void 206+device_added(char *devname) 207+{ 208+ char path[PATH_MAX]; 209+ char *vendor; 210+ char *product = NULL; 211+ char *config_info = NULL; 212+ char *walk; 213+ InputOption *options = NULL; 214+ InputAttributes attrs = { }; 215+ DeviceIntPtr dev = NULL; 216+ int i, rc; 217+ int fd; 218+ 219+ for (i = 0; hw_types[i].driver != NULL; i++) { 220+ if (strncmp(devname, hw_types[i].driver, 221+ strlen(hw_types[i].driver)) == 0 && 222+ isdigit(*(devname + strlen(hw_types[i].driver)))) { 223+ attrs.flags |= hw_types[i].flag; 224+ break; 225+ } 226+ } 227+ if (hw_types[i].driver == NULL) { 228+ LogMessageVerb(X_INFO, 10, "config/devd: ignoring device %s\n", 229+ devname); 230+ return; 231+ } 232+ if (hw_types[i].xdriver == NULL) { 233+ LogMessageVerb(X_INFO, 10, "config/devd: ignoring device %s\n", 234+ devname); 235+ return; 236+ } 237+ snprintf(path, sizeof(path), "/dev/%s", devname); 238+ 239+ options = input_option_new(NULL, "_source", "server/devd"); 240+ if (!options) 241+ return; 242+ 243+ vendor = 244+ sysctl_get_str("dev.%s.%s.%%desc", hw_types[i].driver, 245+ devname + strlen(hw_types[i].driver)); 246+ if (vendor == NULL) { 247+ attrs.vendor = strdup("(unnamed)"); 248+ attrs.product = strdup("(unnamed)"); 249+ } 250+ else { 251+ if ((walk = strchr(vendor, ' ')) != NULL) { 252+ walk[0] = '\0'; 253+ walk++; 254+ product = walk; 255+ if ((walk = strchr(product, ',')) != NULL) 256+ walk[0] = '\0'; 257+ } 258+ 259+ attrs.vendor = strdup(vendor); 260+ if (product) 261+ attrs.product = strdup(product); 262+ else 263+ attrs.product = strdup("(unnamed)"); 264+ 265+ options = input_option_new(options, "name", xstrdup(attrs.product)); 266+ 267+ free(vendor); 268+ } 269+ attrs.usb_id = NULL; 270+ attrs.device = strdup(path); 271+ options = input_option_new(options, "driver", hw_types[i].xdriver); 272+ if (attrs.flags & ATTR_KEYBOARD) { 273+ /* 274+ * Don't pass device option if keyboard is attached to console (open fails), 275+ * thus activating special logic in xf86-input-keyboard. 276+ */ 277+ fd = open(path, O_RDONLY | O_NONBLOCK | O_EXCL); 278+ if (fd > 0) { 279+ close(fd); 280+ options = input_option_new(options, "device", xstrdup(path)); 281+ } 282+ } 283+ else { 284+ options = input_option_new(options, "device", xstrdup(path)); 285+ } 286+ 287+ if (asprintf(&config_info, "devd:%s", devname) == -1) { 288+ config_info = NULL; 289+ goto unwind; 290+ } 291+ 292+ if (device_is_duplicate(config_info)) { 293+ LogMessage(X_WARNING, "config/devd: device %s already added. " 294+ "Ignoring.\n", attrs.product); 295+ goto unwind; 296+ } 297+ 298+ options = input_option_new(options, "config_info", config_info); 299+ LogMessage(X_INFO, "config/devd: adding input device %s (%s)\n", 300+ attrs.product, path); 301+ 302+ rc = NewInputDeviceRequest(options, &attrs, &dev); 303+ 304+ if (rc != Success) 305+ goto unwind; 306+ 307+ unwind: 308+ free(config_info); 309+ input_option_free_list(&options); 310+ 311+ free(attrs.usb_id); 312+ free(attrs.product); 313+ free(attrs.device); 314+ free(attrs.vendor); 315+} 316+ 317+static void 318+device_removed(char *devname) 319+{ 320+ char *value; 321+ 322+ if (asprintf(&value, "devd:%s", devname) == -1) 323+ return; 324+ 325+ remove_devices("devd", value); 326+ 327+ free(value); 328+} 329+ 330+static ssize_t 331+socket_getline(int fd, char **out) 332+{ 333+ char *buf, *newbuf; 334+ ssize_t ret, cap, sz = 0; 335+ char c; 336+ 337+ cap = 1024; 338+ buf = malloc(cap * sizeof(char)); 339+ if (!buf) 340+ return -1; 341+ 342+ for (;;) { 343+ ret = read(sock_devd, &c, 1); 344+ if (ret < 1) { 345+ if (errno == EINTR) 346+ continue; 347+ free(buf); 348+ return -1; 349+ } 350+ 351+ if (c == '\n') 352+ break; 353+ 354+ if (sz + 1 >= cap) { 355+ cap *= 2; 356+ newbuf = realloc(buf, cap * sizeof(char)); 357+ if (!newbuf) { 358+ free(buf); 359+ return -1; 360+ } 361+ buf = newbuf; 362+ } 363+ buf[sz] = c; 364+ sz++; 365+ } 366+ 367+ buf[sz] = '\0'; 368+ if (sz >= 0) 369+ *out = buf; 370+ else 371+ free(buf); 372+ 373+ return sz; /* number of bytes in the line, not counting the line break */ 374+} 375+ 376+static void 377+socket_handler(int fd, int ready, void *data) 378+{ 379+ char *line = NULL; 380+ char *walk; 381+ 382+ if (socket_getline(sock_devd, &line) < 0) 383+ return; 384+ 385+ walk = strchr(line + 1, ' '); 386+ if (walk != NULL) 387+ walk[0] = '\0'; 388+ 389+ switch (*line) { 390+ case DEVD_EVENT_ADD: 391+ device_added(line + 1); 392+ break; 393+ case DEVD_EVENT_REMOVE: 394+ device_removed(line + 1); 395+ break; 396+ default: 397+ break; 398+ } 399+ free(line); 400+} 401+ 402+int 403+config_devd_init(void) 404+{ 405+ struct sockaddr_un devd; 406+ char devicename[1024]; 407+ int i, j; 408+ 409+ /* first scan the sysctl to determine the hardware if needed */ 410+ 411+ for (i = 0; hw_types[i].driver != NULL; i++) { 412+ for (j = 0; sysctl_exists("dev.%s.%i.%%desc", hw_types[i].driver, j); 413+ j++) { 414+ snprintf(devicename, sizeof(devicename), "%s%i", hw_types[i].driver, 415+ j); 416+ device_added(devicename); 417+ } 418+ 419+ } 420+ sock_devd = socket(AF_UNIX, SOCK_STREAM, 0); 421+ if (sock_devd < 0) { 422+ ErrorF("config/devd: Fail opening stream socket"); 423+ return 0; 424+ } 425+ 426+ devd.sun_family = AF_UNIX; 427+ strlcpy(devd.sun_path, DEVD_SOCK_PATH, sizeof(devd.sun_path)); 428+ 429+ if (connect(sock_devd, (struct sockaddr *) &devd, sizeof(devd)) < 0) { 430+ close(sock_devd); 431+ ErrorF("config/devd: Fail to connect to devd"); 432+ return 0; 433+ } 434+ 435+ SetNotifyFd(sock_devd, socket_handler, X_NOTIFY_READ, NULL); 436+ 437+ return 1; 438+} 439+ 440+void 441+config_devd_fini(void) 442+{ 443+ if (sock_devd < 0) 444+ return; 445+ 446+ RemoveNotifyFd(sock_devd); 447+ close(sock_devd); 448+} 449--- a/configure.ac 450+++ b/configure.ac 451@@ -566,6 +566,7 @@ AC_ARG_ENABLE(dpms, AS_HELP_ST 452 AC_ARG_ENABLE(config-udev, AS_HELP_STRING([--enable-config-udev], [Build udev support (default: auto)]), [CONFIG_UDEV=$enableval], [CONFIG_UDEV=auto]) 453 AC_ARG_ENABLE(config-udev-kms, AS_HELP_STRING([--enable-config-udev-kms], [Build udev kms support (default: auto)]), [CONFIG_UDEV_KMS=$enableval], [CONFIG_UDEV_KMS=auto]) 454 AC_ARG_ENABLE(config-hal, AS_HELP_STRING([--disable-config-hal], [Build HAL support (default: auto)]), [CONFIG_HAL=$enableval], [CONFIG_HAL=auto]) 455+AC_ARG_ENABLE(config-devd, AS_HELP_STRING([--disable-config-devd], [Build devd support (default: auto)]), [CONFIG_DEVD=$enableval], [CONFIG_DEVD=auto]) 456 AC_ARG_ENABLE(config-wscons, AS_HELP_STRING([--enable-config-wscons], [Build wscons config support (default: auto)]), [CONFIG_WSCONS=$enableval], [CONFIG_WSCONS=auto]) 457 AC_ARG_ENABLE(xfree86-utils, AS_HELP_STRING([--enable-xfree86-utils], [Build xfree86 DDX utilities (default: enabled)]), [XF86UTILS=$enableval], [XF86UTILS=yes]) 458 AC_ARG_ENABLE(vgahw, AS_HELP_STRING([--enable-vgahw], [Build Xorg with vga access (default: enabled)]), [VGAHW=$enableval], [VGAHW=yes]) 459@@ -950,6 +951,21 @@ if test "x$CONFIG_WSCONS" = xyes; then 460 AC_DEFINE(CONFIG_WSCONS, 1, [Use wscons for input auto configuration]) 461 fi 462 463+if test "x$CONFIG_DEVD" = xauto; then 464+ case $host_os in 465+ freebsd* | kfreebsd*-gnu) 466+ CONFIG_DEVD=yes; 467+ ;; 468+ *) 469+ CONFIG_DEVD=no; 470+ ;; 471+ esac 472+fi 473+AM_CONDITIONAL(CONFIG_DEVD, [test "x$CONFIG_DEVD" = xyes]) 474+if test "x$CONFIG_DEVD" = xyes; then 475+ AC_DEFINE(CONFIG_DEVD, 1, [Use devd for input auto configuration]) 476+fi 477+ 478 479 AC_MSG_CHECKING([for glibc...]) 480 AC_PREPROC_IFELSE([AC_LANG_SOURCE([ 481@@ -2429,7 +2445,7 @@ AC_SUBST([prefix]) 482 483 AC_CONFIG_COMMANDS([sdksyms], [touch hw/xfree86/sdksyms.dep]) 484 485-if test "x$CONFIG_HAL" = xno && test "x$CONFIG_UDEV" = xno; then 486+if test "x$CONFIG_HAL" = xno && test "x$CONFIG_UDEV" = xno && test "x$CONFIG_DEVD" = xno; then 487 AC_MSG_WARN([ 488 *********************************************** 489 Neither HAL nor udev backend will be enabled. 490--- a/hw/xfree86/common/xf86Config.c 491+++ b/hw/xfree86/common/xf86Config.c 492@@ -1257,15 +1257,18 @@ checkCoreInputDevices(serverLayoutPtr se 493 } 494 495 if (!xf86Info.forceInputDevices && !(foundPointer && foundKeyboard)) { 496-#if defined(CONFIG_HAL) || defined(CONFIG_UDEV) || defined(CONFIG_WSCONS) 497+#if defined(CONFIG_HAL) || defined(CONFIG_UDEV) || defined(CONFIG_WSCONS) || \ 498+ defined(CONFIG_DEVD) 499 const char *config_backend; 500 501 #if defined(CONFIG_HAL) 502 config_backend = "HAL"; 503 #elif defined(CONFIG_UDEV) 504 config_backend = "udev"; 505-#else 506+#elif defined(CONFIG_WSCONS) 507 config_backend = "wscons"; 508+#elif defined(CONFIG_DEVD) 509+ config_backend = "devd"; 510 #endif 511 xf86Msg(X_INFO, "The server relies on %s to provide the list of " 512 "input devices.\n\tIf no devices become available, " 513--- a/hw/xfree86/common/xf86Globals.c 514+++ b/hw/xfree86/common/xf86Globals.c 515@@ -117,7 +117,8 @@ xf86InfoRec xf86Info = { 516 .miscModInDevEnabled = TRUE, 517 .miscModInDevAllowNonLocal = FALSE, 518 .pmFlag = TRUE, 519-#if defined(CONFIG_HAL) || defined(CONFIG_UDEV) || defined(CONFIG_WSCONS) 520+#if defined(CONFIG_HAL) || defined(CONFIG_UDEV) || defined(CONFIG_WSCONS) || \ 521+ defined(CONFIG_DEVD) 522 .forceInputDevices = FALSE, 523 .autoAddDevices = TRUE, 524 .autoEnableDevices = TRUE, 525--- a/include/dix-config.h.in 526+++ b/include/dix-config.h.in 527@@ -433,6 +433,9 @@ 528 /* Enable systemd-logind integration */ 529 #undef SYSTEMD_LOGIND 1 530 531+/* Support devd for hotplug */ 532+#undef CONFIG_DEVD 533+ 534 /* Have a monotonic clock from clock_gettime() */ 535 #undef MONOTONIC_CLOCK 536 537