Changeset View
Changeset View
Standalone View
Standalone View
x11-servers/xorg-server/files/patch-config_devd.c
--- config/devd.c.orig 2015-05-19 19:41:49 UTC | --- /dev/null 2016-08-20 16:05:18.000000000 +0300 | ||||
+++ config/devd.c | +++ config/devd.c 2016-08-20 16:09:28.076064000 +0300 | ||||
@@ -0,0 +1,531 @@ | @@ -0,0 +1,892 @@ | ||||
+/* | +/* | ||||
+ * Copyright (c) 2012 Baptiste Daroussin | + * Copyright (c) 2012 Baptiste Daroussin | ||||
+ * Copyright (c) 2013, 2014 Alex Kozlov | + * Copyright (c) 2013, 2014 Alex Kozlov | ||||
+ * Copyright (c) 2014 Robert Millan | + * Copyright (c) 2014 Robert Millan | ||||
+ * Copyright (c) 2014 Jean-Sebastien Pedron | + * Copyright (c) 2014 Jean-Sebastien Pedron | ||||
+ * | + * | ||||
+ * Permission is hereby granted, free of charge, to any person obtaining a | + * Permission is hereby granted, free of charge, to any person obtaining a | ||||
+ * copy of this software and associated documentation files (the "Software"), | + * copy of this software and associated documentation files (the "Software"), | ||||
Show All 16 Lines | |||||
+ * | + * | ||||
+ * Author: Baptiste Daroussin <bapt@FreeBSD.org> | + * Author: Baptiste Daroussin <bapt@FreeBSD.org> | ||||
+ */ | + */ | ||||
+ | + | ||||
+#ifdef HAVE_DIX_CONFIG_H | +#ifdef HAVE_DIX_CONFIG_H | ||||
+#include <dix-config.h> | +#include <dix-config.h> | ||||
+#endif | +#endif | ||||
+ | + | ||||
+#include <sys/types.h> | +#include <sys/param.h> | ||||
+#include <sys/ioccom.h> | |||||
+#include <sys/kbio.h> | +#include <sys/kbio.h> | ||||
+#include <sys/queue.h> | |||||
+#include <sys/socket.h> | +#include <sys/socket.h> | ||||
+#include <sys/stat.h> | +#include <sys/stat.h> | ||||
+#include <sys/sysctl.h> | +#include <sys/sysctl.h> | ||||
+#include <sys/un.h> | +#include <sys/un.h> | ||||
+ | + | ||||
+#include <ctype.h> | +#include <ctype.h> | ||||
+#include <dirent.h> | |||||
+#include <errno.h> | +#include <errno.h> | ||||
+#include <fcntl.h> | +#include <fcntl.h> | ||||
+#include <limits.h> | |||||
+#include <stddef.h> | |||||
+#include <stdlib.h> | +#include <stdlib.h> | ||||
+#include <stdio.h> | +#include <stdio.h> | ||||
+#include <stdbool.h> | +#include <stdbool.h> | ||||
+#include <string.h> | |||||
+#include <unistd.h> | +#include <unistd.h> | ||||
+#include <paths.h> | |||||
+ | + | ||||
+#include "input.h" | +#include "input.h" | ||||
+#include "inputstr.h" | +#include "inputstr.h" | ||||
+#include "hotplug.h" | +#include "hotplug.h" | ||||
+#include "config-backends.h" | +#include "config-backends.h" | ||||
+#include "os.h" | +#include "os.h" | ||||
+ | + | ||||
+#define DEVD_SOCK_PATH "/var/run/devd.pipe" | +#define DEVD_SOCK_PATH "/var/run/devd.pipe" | ||||
+ | + | ||||
+#define DEVD_EVENT_ADD '+' | +#define RECONNECT_DELAY (5 * 1000) | ||||
+#define DEVD_EVENT_REMOVE '-' | |||||
+ | + | ||||
+#define RECONNECT_DELAY 5 * 1000 | |||||
+ | |||||
+static int sock_devd; | +static int sock_devd; | ||||
+static bool is_console_kbd = false; | |||||
+static bool is_kbdmux = false; | +static bool is_kbdmux = false; | ||||
+static bool is_kernel_evdev = false; | |||||
+OsTimerPtr rtimer; | +OsTimerPtr rtimer; | ||||
+ | + | ||||
+struct hw_type { | +struct hw_type { | ||||
+ const char *driver; | + const char *driver; | ||||
+ int flag; | + int flag; | ||||
+ const char *xdriver; | + const char *xdriver; | ||||
+ const char *sysctldesc; | |||||
+}; | +}; | ||||
+ | + | ||||
+static struct hw_type hw_types[] = { | +static const struct hw_type hw_types0[] = { | ||||
+ { "ukbd", ATTR_KEYBOARD, "kbd" }, | + { _PATH_DEV "sysmouse", ATTR_POINTER, "mouse", NULL }, | ||||
+ { "atkbd", ATTR_KEYBOARD, "kbd" }, | + { _PATH_DEV "vboxguest", ATTR_POINTER, "vboxmouse", NULL }, | ||||
+ { "kbdmux", ATTR_KEYBOARD, "kbd" }, | + { NULL, 0, NULL, NULL }, | ||||
+ { "sysmouse", ATTR_POINTER, "mouse" }, | |||||
+ { "ums", ATTR_POINTER, "mouse" }, | |||||
+ { "psm", ATTR_POINTER, "mouse" }, | |||||
+ { "vboxguest", ATTR_POINTER, "vboxmouse" }, | |||||
+ { "joy", ATTR_JOYSTICK, NULL }, | |||||
+ { "atp", ATTR_TOUCHPAD, NULL }, | |||||
+ { "uep", ATTR_TOUCHSCREEN, NULL }, | |||||
+ { NULL, -1, NULL }, | |||||
+}; | +}; | ||||
+ | + | ||||
+static bool | +static const struct hw_type hw_types1[] = { | ||||
+sysctl_exists(const struct hw_type *device, int unit, | + { _PATH_DEV "ukbd%d", ATTR_KEYBOARD, "kbd", "ukbd" }, | ||||
+ char *devname, size_t devname_len) | + { _PATH_DEV "atkbd%d", ATTR_KEYBOARD, "kbd", "atkbd" }, | ||||
+ { _PATH_DEV "kbdmux%d", ATTR_KEYBOARD, "kbd", NULL }, | |||||
+ { _PATH_DEV "ums%d", ATTR_POINTER, "mouse", "ums" }, | |||||
+ { _PATH_DEV "psm%d", ATTR_POINTER, "mouse", "psm" }, | |||||
+ { _PATH_DEV "joy%d", ATTR_JOYSTICK, "mouse", "joy" }, | |||||
+ { _PATH_DEV "atp%d", ATTR_TOUCHPAD, "mouse", "atp" }, | |||||
+ { _PATH_DEV "wsp%d", ATTR_TOUCHPAD, "mouse", "wsp" }, | |||||
+ { _PATH_DEV "uep%d", ATTR_TOUCHSCREEN, "mouse", "uep" }, | |||||
+ { _PATH_DEV "input/event%d", 0, "evdev", NULL }, | |||||
+ { NULL, 0, NULL, NULL }, | |||||
+}; | |||||
+ | |||||
+/* like basename() but returns a pointer to incoming string */ | |||||
+static char * | |||||
+bname(const char *path) | |||||
+{ | +{ | ||||
+ char sysctlname[PATH_MAX]; | + char *slash = NULL; | ||||
+ size_t len; | |||||
+ int ret; | |||||
+ | + | ||||
+ if (device == NULL || device->driver == NULL) | + if (path != NULL) | ||||
+ return false; | + slash = strrchr(path, '/'); | ||||
+ return (slash == NULL) ? (char *)path : slash + 1; | |||||
+} | |||||
+ | + | ||||
+ /* Check if a sysctl exists. */ | +struct dev_entry { | ||||
+ snprintf(sysctlname, sizeof(sysctlname), "dev.%s.%i.%%desc", | + SLIST_ENTRY(dev_entry) next; | ||||
+ device->driver, unit); | + char name[]; | ||||
+ ret = sysctlbyname(sysctlname, NULL, &len, NULL, 0); | +}; | ||||
+SLIST_HEAD(dev_list, dev_entry); | |||||
+ | + | ||||
+ if (ret == 0 && len > 0) { | +#define dev_list_init(dev_list) SLIST_INIT(dev_list) | ||||
+ snprintf(devname, devname_len, "%s%i", device->driver, unit); | +#define dev_list_empty(dev_list) SLIST_EMPTY(dev_list) | ||||
+ return true; | + | ||||
+static void | |||||
+dev_list_insert(struct dev_list *devs, const char *dev_name) | |||||
+{ | |||||
+ struct dev_entry *dev; | |||||
+ | |||||
+ dev = malloc(offsetof(struct dev_entry, name) + strlen(dev_name) + 1); | |||||
+ if (dev != NULL) { | |||||
+ strcpy(dev->name, dev_name); | |||||
+ SLIST_INSERT_HEAD(devs, dev, next); | |||||
+ } | + } | ||||
+} | |||||
+ | + | ||||
+ return false; | +static void | ||||
+dev_list_destroy(struct dev_list *devs) | |||||
+{ | |||||
+ struct dev_entry *dev; | |||||
+ | |||||
+ while (!SLIST_EMPTY(devs)) { | |||||
+ dev = SLIST_FIRST(devs); | |||||
+ SLIST_REMOVE_HEAD(devs, next); | |||||
+ free(dev); | |||||
+} | + } | ||||
+} | |||||
+ | + | ||||
+static bool | +static bool | ||||
+devpath_exists(const struct hw_type *device, | +dev_list_search(struct dev_list *devs, const char *dev_name) | ||||
+ char *devname, size_t devname_len) | |||||
+{ | +{ | ||||
+ char *devpath; | + struct dev_entry *dev; | ||||
+ struct stat st; | |||||
+ int ret; | |||||
+ | + | ||||
+ if (device == NULL || device->driver == NULL) | + if (dev_name != NULL) | ||||
+ SLIST_FOREACH(dev, devs, next) | |||||
+ if (strcmp(dev_name, dev->name) == 0) | |||||
+ return true; | |||||
+ return false; | + return false; | ||||
+} | |||||
+ | + | ||||
+ /* Check if /dev/$driver exists. */ | +/* Some definitions from linux/input.h */ | ||||
+ asprintf(&devpath, "/dev/%s", device->driver); | +struct input_id { | ||||
+ if (devpath == NULL) | + uint16_t bustype; | ||||
+ return false; | + uint16_t vendor; | ||||
+ uint16_t product; | |||||
+ uint16_t version; | |||||
+}; | |||||
+ | + | ||||
+ ret = stat(devpath, &st); | +#define EVIOCGBIT(ev,len) _IOC(IOC_OUT, 'E', 0x20 + (ev), len) | ||||
+ free(devpath); | +#define EVIOCGID _IOR('E', 0x02, struct input_id) | ||||
+#define EVIOCGNAME(len) _IOC(IOC_OUT, 'E', 0x06, len) | |||||
+#define EVIOCGPHYS(len) _IOC(IOC_OUT, 'E', 0x07, len) | |||||
+ | + | ||||
+ if (ret == 0) { | +#define EV_KEY 0x01 | ||||
+ strncpy(devname, device->driver, devname_len); | +#define EV_REL 0x02 | ||||
+ return true; | +#define EV_ABS 0x03 | ||||
+#define BTN_MISC 0x100 | |||||
+#define BTN_LEFT 0x110 | |||||
+#define BTN_RIGHT 0x111 | |||||
+#define BTN_MIDDLE 0x112 | |||||
+#define BTN_JOYSTICK 0x120 | |||||
+#define BTN_TOOL_PEN 0x140 | |||||
+#define BTN_TOOL_FINGER 0x145 | |||||
+#define BTN_TOUCH 0x14a | |||||
+#define BTN_STYLUS 0x14b | |||||
+#define BTN_STYLUS2 0x14c | |||||
+#define KEY_MAX 0x2ff | |||||
+#define KEY_CNT (KEY_MAX+1) | |||||
+#define REL_X 0x00 | |||||
+#define REL_Y 0x01 | |||||
+#define REL_MAX 0x0f | |||||
+#define REL_CNT (REL_MAX+1) | |||||
+#define ABS_X 0x00 | |||||
+#define ABS_Y 0x01 | |||||
+#define ABS_PRESSURE 0x18 | |||||
+#define ABS_MT_SLOT 0x2f | |||||
+#define ABS_MAX 0x3f | |||||
+#define ABS_CNT (ABS_MAX+1) | |||||
+ | |||||
+#define LONG_BITS (sizeof(long) * 8) | |||||
+#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) | |||||
+ | |||||
+static inline bool | |||||
+bit_is_set(const unsigned long *array, int bit) | |||||
+{ | |||||
+ return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); | |||||
+ } | +} | ||||
+ | + | ||||
+static inline bool | |||||
+bit_find(const unsigned long *array, int start, int stop) | |||||
+{ | |||||
+ int i; | |||||
+ | |||||
+ for (i = start; i < stop; i++) | |||||
+ if (bit_is_set(array, i)) | |||||
+ return true; | |||||
+ | |||||
+ return false; | + return false; | ||||
+} | +} | ||||
+ | + | ||||
+static char * | +/* | ||||
+sysctl_get_str(const char *sysctlname) | + * Event device type detection routine. | ||||
+ * Derived from EvdevProbe() function of xf86-input-evdev driver | |||||
+ */ | |||||
+static void | |||||
+get_evdev_attrs(InputAttributes *attrs, const char *devicename) | |||||
+{ | +{ | ||||
+ char *dest = NULL; | + int fd, flags; | ||||
+ size_t len; | + bool has_keys, has_buttons, has_lmr; | ||||
+ bool has_rel_axes, has_abs_axes, has_mt; | |||||
+ unsigned long key_bits[NLONGS(KEY_CNT)]; | |||||
+ unsigned long rel_bits[NLONGS(REL_CNT)]; | |||||
+ unsigned long abs_bits[NLONGS(ABS_CNT)]; | |||||
+ struct input_id id; | |||||
+ char name[80]; | |||||
+ char *walk; | |||||
+ | + | ||||
+ if (sysctlname == NULL) | + flags = 0; | ||||
+ return NULL; | |||||
+ | + | ||||
+ if (sysctlbyname(sysctlname, NULL, &len, NULL, 0) == 0) { | + fd = open(devicename, O_RDONLY | O_CLOEXEC); | ||||
+ dest = malloc(len + 1); | + if (fd < 0) | ||||
+ if (dest) { | + goto out; | ||||
+ if (sysctlbyname(sysctlname, dest, &len, NULL, 0) == 0) | + if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), name) < 0 || | ||||
+ dest[len] = '\0'; | + ioctl(fd, EVIOCGID, &id) < 0 || | ||||
+ else { | + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), rel_bits) < 0 || | ||||
+ free(dest); | + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits) < 0 || | ||||
+ dest = NULL; | + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits) < 0) { | ||||
+ close(fd); | |||||
+ goto out; | |||||
+ } | + } | ||||
+ close(fd); | |||||
+ | |||||
+ if ((walk = strchr(name, ',')) != NULL) | |||||
+ *walk = '\0'; /* strip name */ | |||||
+ | |||||
+ attrs->product = strdup(name); | |||||
+ asprintf(&attrs->usb_id, "%04x:%04x", id.vendor, id.product); | |||||
+ asprintf(&attrs->vendor, "0x%04x", id.vendor); | |||||
+ | |||||
+ has_keys = bit_find(key_bits, 0, BTN_MISC); | |||||
+ has_buttons = bit_find(key_bits, BTN_MISC, BTN_JOYSTICK); | |||||
+ has_lmr = bit_find(key_bits, BTN_LEFT, BTN_MIDDLE + 1); | |||||
+ has_rel_axes = bit_find(rel_bits, 0, REL_CNT); | |||||
+ has_abs_axes = bit_find(abs_bits, 0, ABS_CNT); | |||||
+ has_mt = bit_find(abs_bits, ABS_MT_SLOT, ABS_CNT); | |||||
+ | |||||
+ if (has_abs_axes) { | |||||
+ if (has_mt) { | |||||
+ if (!has_buttons) { | |||||
+ /* | |||||
+ * XXX: I'm not sure that joystick detection is | |||||
+ * done right. xf86-evdev does not support them. | |||||
+ */ | |||||
+ if (bit_is_set(key_bits, BTN_JOYSTICK)) { | |||||
+ flags = ATTR_JOYSTICK; | |||||
+ goto out; | |||||
+ } else { | |||||
+ has_buttons = true; | |||||
+ } | + } | ||||
+ } | + } | ||||
+ } | |||||
+ | + | ||||
+ return dest; | + if (bit_is_set(abs_bits, ABS_X) && | ||||
+ bit_is_set(abs_bits, ABS_Y)) { | |||||
+ if (bit_is_set(key_bits, BTN_TOOL_PEN) || | |||||
+ bit_is_set(key_bits, BTN_STYLUS) || | |||||
+ bit_is_set(key_bits, BTN_STYLUS2)) { | |||||
+ flags = ATTR_TABLET; | |||||
+ goto out; | |||||
+ } else if (bit_is_set(abs_bits, ABS_PRESSURE) || | |||||
+ bit_is_set(key_bits, BTN_TOUCH)) { | |||||
+ if (has_lmr || | |||||
+ bit_is_set(key_bits, BTN_TOOL_FINGER)) { | |||||
+ flags = ATTR_TOUCHPAD; | |||||
+ } else { | |||||
+ flags = ATTR_TOUCHSCREEN; | |||||
+} | + } | ||||
+ goto out; | |||||
+ } else if (!(bit_is_set(rel_bits, REL_X) && | |||||
+ bit_is_set(rel_bits, REL_Y)) && | |||||
+ has_lmr) { | |||||
+ /* some touchscreens use BTN_LEFT rather than BTN_TOUCH */ | |||||
+ flags = ATTR_TOUCHSCREEN; | |||||
+ goto out; | |||||
+ } | |||||
+ } | |||||
+ } | |||||
+ | + | ||||
+ if (has_keys) | |||||
+ flags = ATTR_KEYBOARD; | |||||
+ else if (has_rel_axes || has_abs_axes || has_buttons) | |||||
+ flags = ATTR_POINTER; | |||||
+ | |||||
+out: | |||||
+ attrs->flags |= flags; | |||||
+} | |||||
+ | |||||
+/* Returns list of devices supporting evdev protocol */ | |||||
+static void | +static void | ||||
+device_added(const char *devname) | +get_evdev_blacklist(struct dev_list *devs) | ||||
+{ | +{ | ||||
+ char path[PATH_MAX]; | + struct dirent *entry; | ||||
+ char sysctlname[PATH_MAX]; | + DIR *dp; | ||||
+ char *vendor; | + static const char ev_dir[] = _PATH_DEV "input"; | ||||
+ char *product = NULL; | +#define EV_LEN nitems(ev_dir) | ||||
+ char *config_info = NULL; | + char path[PATH_MAX + 1]; | ||||
+ char *walk; | + char phys[80]; | ||||
+ InputOption *options = NULL; | |||||
+ InputAttributes attrs = { }; | |||||
+ DeviceIntPtr dev = NULL; | |||||
+ int i; | |||||
+ int fd; | + int fd; | ||||
+ | + | ||||
+ for (i = 0; hw_types[i].driver != NULL; i++) { | + snprintf(path, sizeof(path), "%s/", ev_dir); | ||||
+ size_t len; | + if ((dp = opendir(ev_dir)) == NULL) | ||||
+ return; | |||||
+ | + | ||||
+ len = strlen(hw_types[i].driver); | + while ((entry = readdir(dp)) != NULL) { | ||||
+ if (strcmp(devname, hw_types[i].driver) == 0 || | + if (entry->d_type != DT_CHR) | ||||
+ (strncmp(devname, hw_types[i].driver, len) == 0 && | + continue; | ||||
+ isnumber(*(devname + len)))) { | + if (strncmp(entry->d_name, "event", 5) != 0) | ||||
+ attrs.flags |= hw_types[i].flag; | + continue; | ||||
+ break; | + strlcpy(path + EV_LEN, entry->d_name, sizeof(path) - EV_LEN); | ||||
+ fd = open(path, O_RDONLY | O_CLOEXEC); | |||||
+ if (fd < 0) | |||||
+ continue; | |||||
+ /* XXX: Should uinput- and cuse-backed devices be skipped? */ | |||||
+ if (ioctl(fd, EVIOCGPHYS(sizeof(phys) - 1), phys) == 0 && | |||||
+ phys[0] != 0) | |||||
+ dev_list_insert(devs, bname(phys)); | |||||
+ close(fd); | |||||
+ } | + } | ||||
+ closedir(dp); | |||||
+ return; | |||||
+ } | +} | ||||
+ | + | ||||
+ if (hw_types[i].driver == NULL || hw_types[i].xdriver == NULL) { | +static void | ||||
+ LogMessage(X_INFO, "config/devd: ignoring device %s\n", | +get_usb_id(char **pptr, int fd) | ||||
+ devname); | +{ | ||||
+ return; | + unsigned short vendor; | ||||
+ unsigned short product; | |||||
+ unsigned int speed; | |||||
+#define WEBCAMD_IOCTL_GET_USB_VENDOR_ID _IOR('q', 250, unsigned short) | |||||
+#define WEBCAMD_IOCTL_GET_USB_PRODUCT_ID _IOR('q', 251, unsigned short) | |||||
+#define WEBCAMD_IOCTL_GET_USB_SPEED _IOR('q', 252, unsigned int) | |||||
+ if (ioctl(fd, WEBCAMD_IOCTL_GET_USB_VENDOR_ID, &vendor) == 0 && | |||||
+ ioctl(fd, WEBCAMD_IOCTL_GET_USB_PRODUCT_ID, &product) == 0 && | |||||
+ ioctl(fd, WEBCAMD_IOCTL_GET_USB_SPEED, &speed) == 0) { | |||||
+ if (asprintf(pptr, "%04x:%04x", vendor, product) == -1) | |||||
+ *pptr = NULL; | |||||
+ } | + } | ||||
+} | |||||
+ | + | ||||
+ /* Skip keyboard devices if kbdmux is enabled */ | +static char * | ||||
+ if (is_kbdmux && is_console_kbd && hw_types[i].flag & ATTR_KEYBOARD) { | +get_prop_value(const char *buf, const char *prop, size_t *len) | ||||
+ LogMessage(X_INFO, "config/devd: kbdmux is enabled, ignoring device %s\n", | +{ | ||||
+ devname); | + char *prop_pos, *val_pos, *walk; | ||||
+ return; | + size_t prop_len; | ||||
+ | |||||
+ prop_len = strlen(prop); | |||||
+ prop_pos = strstr(buf, prop); | |||||
+ if (prop_pos == NULL || | |||||
+ (prop_pos != buf && prop_pos[-1] != ' ') || | |||||
+ prop_pos[prop_len] != '=') | |||||
+ return (NULL); | |||||
+ | |||||
+ val_pos = prop_pos + prop_len + 1; | |||||
+ if ((walk = strchr(val_pos, ' ')) == NULL) | |||||
+ *len = strlen(val_pos); | |||||
+ else | |||||
+ *len = walk - val_pos; | |||||
+ return (val_pos); | |||||
+ } | +} | ||||
+ | + | ||||
+ snprintf(path, sizeof(path), "/dev/%s", devname); | +static void | ||||
+get_sysctl_attrs(InputAttributes *attrs, const char* driver, int unit) | |||||
+{ | |||||
+ char mib[32], name[80], pnpinfo[1024], *pnp_id, *walk; | |||||
+ const char *vendorstr, *prodstr, *devicestr; | |||||
+ size_t len, vendorlen, prodlen, devicelen, pnplen; | |||||
+ uint32_t product, vendor; | |||||
+ | + | ||||
+ options = input_option_new(NULL, "_source", "server/devd"); | + snprintf(mib, sizeof(mib), "dev.%s.%d.%%desc", driver, unit); | ||||
+ if (!options) | + len = sizeof(name); | ||||
+ if (sysctlbyname(mib, name, &len, NULL, 0) < 0) | |||||
+ return; | + return; | ||||
+ if ((walk = strchr(name, ',')) != NULL) | |||||
+ *walk = '\0'; /* strip name */ | |||||
+ attrs->product = strdup(name); | |||||
+ | + | ||||
+ snprintf(sysctlname, sizeof(sysctlname), "dev.%s.%s.%%desc", | + snprintf(mib, sizeof(mib), "dev.%s.%d.%%pnpinfo", driver, unit); | ||||
+ hw_types[i].driver, devname + strlen(hw_types[i].driver)); | + len = sizeof(pnpinfo); | ||||
+ vendor = sysctl_get_str(sysctlname); | + if (sysctlbyname(mib, pnpinfo, &len, NULL, 0) < 0) | ||||
+ if (vendor == NULL) { | + return; | ||||
+ options = input_option_new(options, "name", devname); | + | ||||
+ vendorstr = get_prop_value(pnpinfo, "vendor", &vendorlen); | |||||
+ prodstr = get_prop_value(pnpinfo, "product", &prodlen); | |||||
+ devicestr = get_prop_value(pnpinfo, "device", &devicelen); | |||||
+ pnp_id = get_prop_value(pnpinfo, "_HID", &pnplen); | |||||
+ if (pnp_id != NULL && pnplen == 4 && strncmp(pnp_id, "none", 4) == 0) | |||||
+ pnp_id = NULL; | |||||
+ if (pnp_id != NULL) { | |||||
+ pnp_id[pnplen] = '\0'; | |||||
+ attrs->pnp_id = strdup(pnp_id); | |||||
+ } | + } | ||||
+ else { | + if (prodstr != NULL && vendorstr != NULL) { | ||||
+ if ((walk = strchr(vendor, ' ')) != NULL) { | + /* bus = BUS_USB; */ | ||||
+ walk[0] = '\0'; | + vendor = strtol(vendorstr, NULL, 0); | ||||
+ walk++; | + product = strtol(prodstr, NULL, 0); | ||||
+ product = walk; | + } else if (devicestr != NULL && vendorstr != NULL) { | ||||
+ if ((walk = strchr(product, ',')) != NULL) | + /* bus = BUS_PCI; */ | ||||
+ walk[0] = '\0'; | + vendor = strtol(vendorstr, NULL, 0); | ||||
+ } | + product = strtol(devicestr, NULL, 0); | ||||
+ } else | |||||
+ return; | |||||
+ | + | ||||
+ attrs.vendor = strdup(vendor); | + asprintf(&attrs->usb_id, "%04x:%04x", vendor, product); | ||||
+ if (product) { | + asprintf(&attrs->vendor, "0x%04x", vendor); | ||||
+ attrs.product = strdup(product); | + | ||||
+ options = input_option_new(options, "name", product); | + return; | ||||
+ } | +} | ||||
+ else | |||||
+ options = input_option_new(options, "name", "(unnamed)"); | |||||
+ | + | ||||
+ free(vendor); | +static const char * | ||||
+skip_path_dev(const char *ptr) | |||||
+{ | |||||
+ if (strstr(ptr, _PATH_DEV) == ptr) | |||||
+ ptr += strlen(_PATH_DEV); | |||||
+ return (ptr); | |||||
+ } | +} | ||||
+ | + | ||||
+ /* XXX implement usb_id */ | +static void | ||||
+ attrs.usb_id = NULL; | +device_added(const char *devicename, struct dev_list *blacklist) | ||||
+ attrs.device = strdup(path); | +{ | ||||
+ options = input_option_new(options, "driver", hw_types[i].xdriver); | + InputAttributes attrs = { }; | ||||
+ InputOption *options = NULL; | |||||
+ char *config_info = NULL; | |||||
+ DeviceIntPtr dev = NULL; | |||||
+ struct hw_type hw_type; | |||||
+ int unit = 0; | |||||
+ int fd = -1; | |||||
+ int i; | |||||
+ bool allow_no_device = false; | |||||
+ | + | ||||
+ fd = open(path, O_RDONLY); | + for (i = 0; hw_types0[i].driver != NULL; i++) { | ||||
+ if (fd > 0) { | + if (strcmp(devicename, hw_types0[i].driver) == 0) { | ||||
+ close(fd); | + hw_type = hw_types0[i]; | ||||
+ options = input_option_new(options, "device", path); | + goto found; | ||||
+ } | + } | ||||
+ else { | + } | ||||
+ if (attrs.flags & ~ATTR_KEYBOARD) { | + for (i = 0; hw_types1[i].driver != NULL; i++) { | ||||
+ LogMessage(X_INFO, "config/devd: device %s already opened\n", | + if (sscanf(devicename, hw_types1[i].driver, &unit) == 1) { | ||||
+ path); | + hw_type = hw_types1[i]; | ||||
+ goto found; | |||||
+ } | |||||
+ } | |||||
+ goto ignore; | |||||
+ | + | ||||
+found: | |||||
+ if (hw_type.xdriver == NULL) | |||||
+ goto ignore; | |||||
+ | |||||
+ /* set flags, if any */ | |||||
+ attrs.flags |= hw_type.flag; | |||||
+ | |||||
+ if (strcmp(hw_type.xdriver, "evdev") == 0) { | |||||
+ get_evdev_attrs(&attrs, devicename); | |||||
+ /* Set keyboard rules explicitly for libinput */ | |||||
+ if (attrs.flags & ATTR_KEYBOARD) { | |||||
+ options = input_option_new(options, "xkb_rules", | |||||
+ "evdev"); | |||||
+ if (options == NULL) | |||||
+ goto error; | |||||
+ } | |||||
+ } else { | |||||
+ if (is_kernel_evdev) { | |||||
+ if (dev_list_empty(blacklist)) | |||||
+ get_evdev_blacklist(blacklist); | |||||
+ /* | + /* | ||||
+ * Fail if cannot open device, it breaks AllowMouseOpenFail, | + * Prefer evdev input kernel interface to native one. | ||||
+ * but it should not matter when config/devd enabled | + * Assume that both evdev 'physical path' and non-evdev | ||||
+ * character device path endings are device names so | |||||
+ * we can compare them and skip latter. | |||||
+ */ | + */ | ||||
+ goto unwind; | + if (dev_list_search(blacklist, bname(devicename))) | ||||
+ goto ignore; | |||||
+ } | + } | ||||
+ } | |||||
+ | + | ||||
+ if (is_console_kbd) { | + if (strcmp(hw_type.xdriver, "kbd") == 0) { | ||||
+ /* | + bool match = (strstr(hw_type.driver, | ||||
+ * There can be only one keyboard attached to console and | + _PATH_DEV "kbdmux") == hw_type.driver); | ||||
+ * it is already added. | + | ||||
+ */ | + if (is_kbdmux) { | ||||
+ LogMessage(X_WARNING, "config/devd: console keyboard is " | + allow_no_device = true; | ||||
+ "already added, ignoring %s (%s)\n", | + if (!match) | ||||
+ attrs.product, path); | + goto ignore; | ||||
+ goto unwind; | + } else { | ||||
+ if (match) | |||||
+ goto ignore; | |||||
+ } | + } | ||||
+ else | + } | ||||
+ | |||||
+ options = input_option_new(options, "_source", "server/devd"); | |||||
+ if (options == NULL) | |||||
+ goto error; | |||||
+ | |||||
+ if (hw_type.sysctldesc != NULL) | |||||
+ get_sysctl_attrs(&attrs, hw_type.sysctldesc, unit); | |||||
+ | |||||
+ if (attrs.product == NULL) | |||||
+ attrs.product = strdup(skip_path_dev(devicename)); | |||||
+ | |||||
+ options = input_option_new(options, "name", attrs.product); | |||||
+ if (options == NULL) | |||||
+ goto error; | |||||
+ | |||||
+ attrs.device = strdup(devicename); | |||||
+ | |||||
+ fd = open(devicename, O_RDONLY); | |||||
+ if (fd > -1) { | |||||
+ if (attrs.usb_id == NULL) | |||||
+ get_usb_id(&attrs.usb_id, fd); | |||||
+ close(fd); | |||||
+ options = input_option_new(options, "device", devicename); | |||||
+ if (options == NULL) | |||||
+ goto error; | |||||
+ } else if (allow_no_device) { | |||||
+ /* | + /* | ||||
+ * Don't pass "device" option if the keyboard is already | + * Don't pass "device" option if the keyboard is | ||||
+ * attached to the console (ie. open() fails). | + * already attached to the console (ie. open() fails). | ||||
+ * This would activate a special logic in xf86-input-keyboard. | + * This would activate a special logic in | ||||
+ * Prevent any other attached to console keyboards being | + * xf86-input-keyboard. Prevent any other attached to | ||||
+ * processed. There can be only one such device. | + * console keyboards being processed. There can be | ||||
+ * only one such device. | |||||
+ */ | + */ | ||||
+ is_console_kbd = true; | + } else { | ||||
+ goto ignore; | |||||
+ } | + } | ||||
+ | + | ||||
+ if (asprintf(&config_info, "devd:%s", devname) == -1) { | + options = input_option_new(options, "driver", hw_type.xdriver); | ||||
+ if (options == NULL) | |||||
+ goto error; | |||||
+ | |||||
+ if (asprintf(&config_info, "devd:%s", | |||||
+ skip_path_dev(devicename)) == -1) { | |||||
+ config_info = NULL; | + config_info = NULL; | ||||
+ goto unwind; | + goto error; | ||||
+ } | + } | ||||
+ | + | ||||
+ if (device_is_duplicate(config_info)) { | + if (device_is_duplicate(config_info)) | ||||
+ LogMessage(X_WARNING, "config/devd: device %s (%s) already added. " | + goto duplicate; | ||||
+ "ignoring\n", attrs.product, path); | |||||
+ goto unwind; | |||||
+ } | |||||
+ | + | ||||
+ options = input_option_new(options, "config_info", config_info); | + options = input_option_new(options, "config_info", config_info); | ||||
+ LogMessage(X_INFO, "config/devd: adding input device %s (%s)\n", | + if (options == NULL) | ||||
+ attrs.product, path); | + goto error; | ||||
+ | + | ||||
+ LogMessage(X_INFO, "config/devd: adding input device '%s'\n", | |||||
+ devicename); | |||||
+ | |||||
+ NewInputDeviceRequest(options, &attrs, &dev); | + NewInputDeviceRequest(options, &attrs, &dev); | ||||
+ goto done; | |||||
+ | + | ||||
+unwind: | +duplicate: | ||||
+ LogMessage(X_WARNING, "config/devd: device '%s' already " | |||||
+ "added. Ignoring\n", devicename); | |||||
+ goto done; | |||||
+ | |||||
+error: | |||||
+ LogMessage(X_INFO, "config/devd: error adding device '%s'\n", | |||||
+ devicename); | |||||
+ goto done; | |||||
+ | |||||
+ignore: | |||||
+ LogMessage(X_INFO, "config/devd: ignoring device '%s'\n", | |||||
+ devicename); | |||||
+ goto done; | |||||
+ | |||||
+done: | |||||
+ free(config_info); | + free(config_info); | ||||
+ input_option_free_list(&options); | + input_option_free_list(&options); | ||||
+ free(attrs.usb_id); | + free(attrs.usb_id); | ||||
+ free(attrs.pnp_id); | |||||
+ free(attrs.product); | + free(attrs.product); | ||||
+ free(attrs.device); | + free(attrs.device); | ||||
+ free(attrs.vendor); | + free(attrs.vendor); | ||||
+} | +} | ||||
+ | + | ||||
+static void | +static void | ||||
+device_removed(char *devname) | +devpath_scan_sub(char *path, int off, int rem, void *udata) | ||||
+{ | +{ | ||||
+ struct dirent *entry; | |||||
+ DIR *dp; | |||||
+ | |||||
+ if ((dp = opendir(path)) == NULL) { | |||||
+ LogMessage(X_INFO, "Cannot open directory '%s'\n", path); | |||||
+ return; | |||||
+ } | |||||
+ while ((entry = readdir(dp)) != NULL) { | |||||
+ int len = strlen(entry->d_name); | |||||
+ if (len > rem) | |||||
+ continue; | |||||
+ strcpy(path + off, entry->d_name); | |||||
+ off += len; | |||||
+ rem -= len; | |||||
+ switch (entry->d_type) { | |||||
+ case DT_DIR: | |||||
+ if (strcmp(entry->d_name, ".") == 0 || | |||||
+ strcmp(entry->d_name, "..") == 0) | |||||
+ break; | |||||
+ if (rem < 1) | |||||
+ break; | |||||
+ path[off] = '/'; | |||||
+ path[off+1] = '\0'; | |||||
+ off++; | |||||
+ rem--; | |||||
+ /* recurse */ | |||||
+ devpath_scan_sub(path, off, rem, udata); | |||||
+ off--; | |||||
+ rem++; | |||||
+ break; | |||||
+ case DT_SOCK: | |||||
+ case DT_FIFO: | |||||
+ case DT_LNK: | |||||
+ case DT_CHR: | |||||
+ /* add device, if any */ | |||||
+ device_added(path, udata); | |||||
+ break; | |||||
+ default: | |||||
+ break; | |||||
+ } | |||||
+ off -= len; | |||||
+ rem += len; | |||||
+ } | |||||
+ closedir(dp); | |||||
+} | |||||
+ | |||||
+static void | |||||
+devpath_scan(void *udata) | |||||
+{ | |||||
+ char path[PATH_MAX + 1]; | |||||
+ | |||||
+ strlcpy(path, _PATH_DEV, sizeof(path)); | |||||
+ | |||||
+ devpath_scan_sub(path, strlen(path), PATH_MAX - strlen(path), udata); | |||||
+} | |||||
+ | |||||
+static void | |||||
+device_removed(char *devicename) | |||||
+{ | |||||
+ char *config_info; | + char *config_info; | ||||
+ | + | ||||
+ if (asprintf(&config_info, "devd:%s", devname) == -1) | + if (asprintf(&config_info, "devd:%s", | ||||
+ skip_path_dev(devicename)) == -1) | |||||
+ return; | + return; | ||||
+ | + | ||||
+ if (device_is_duplicate(config_info)) { | |||||
+ LogMessage(X_INFO, "config/devd: removing input device '%s'\n", | |||||
+ devicename); | |||||
+ } | |||||
+ remove_devices("devd", config_info); | + remove_devices("devd", config_info); | ||||
+ | + | ||||
+ free(config_info); | + free(config_info); | ||||
+} | +} | ||||
+ | + | ||||
+static bool is_kbdmux_enabled(void) | +static bool is_kbdmux_enabled(void) | ||||
+{ | +{ | ||||
+ /* Xorg uses /dev/ttyv0 as a console device */ | + /* Xorg uses /dev/ttyv0 as a console device */ | ||||
+ /* const char device[]="/dev/console"; */ | + static const char device[]= { _PATH_DEV "ttyv0" }; | ||||
+ const char device[]="/dev/ttyv0"; | |||||
+ keyboard_info_t info; | + keyboard_info_t info; | ||||
+ int fd; | + int fd; | ||||
+ | + | ||||
+ fd = open(device, O_RDONLY); | + fd = open(device, O_RDONLY); | ||||
+ | + | ||||
+ if (fd < 0) | + if (fd < 0) | ||||
+ return false; | + return false; | ||||
+ | + | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
+ } | + } | ||||
+ | + | ||||
+ AddGeneralSocket(sock); | + AddGeneralSocket(sock); | ||||
+ | + | ||||
+ return sock; | + return sock; | ||||
+} | +} | ||||
+ | + | ||||
+static CARD32 | +static CARD32 | ||||
+reconnect_handler(OsTimerPtr timer, CARD32 time, void *arg) | +reconnect_handler(OsTimerPtr timer, CARD32 unused_time, void *arg) | ||||
+{ | +{ | ||||
+ int newsock; | + int newsock; | ||||
+ | + | ||||
+ if ((newsock = connect_devd()) > 0) { | + if ((newsock = connect_devd()) > 0) { | ||||
+ sock_devd = newsock; | + sock_devd = newsock; | ||||
+ TimerFree(rtimer); | + TimerFree(rtimer); | ||||
+ rtimer = NULL; | + rtimer = NULL; | ||||
+ LogMessage(X_INFO, "config/devd: reopening devd socket\n"); | + LogMessage(X_INFO, "config/devd: reopening devd socket\n"); | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
+ | + | ||||
+ /* Number of bytes in the line, not counting the line break */ | + /* Number of bytes in the line, not counting the line break */ | ||||
+ return sz; | + return sz; | ||||
+} | +} | ||||
+ | + | ||||
+static void | +static void | ||||
+wakeup_handler(void *data, int err, void *read_mask) | +wakeup_handler(void *data, int err, void *read_mask) | ||||
+{ | +{ | ||||
+ static const char cdev_create[] = { "!system=DEVFS subsystem=CDEV type=CREATE cdev=" }; | |||||
+ static const char cdev_destroy[] = { "!system=DEVFS subsystem=CDEV type=DESTROY cdev=" }; | |||||
+ static const char cdev_path[] = { _PATH_DEV }; | |||||
+ char *line = NULL; | + char *line = NULL; | ||||
+ char *devicename; | |||||
+ char *walk; | + char *walk; | ||||
+ struct dev_list blacklist; | |||||
+ | + | ||||
+ if (err < 0) | + if (err < 0) | ||||
+ return; | + return; | ||||
+ | + | ||||
+ if (FD_ISSET(sock_devd, (fd_set *) read_mask)) { | + if (FD_ISSET(sock_devd, (fd_set *) read_mask)) { | ||||
+ if (socket_getline(sock_devd, &line) < 0) | + if (socket_getline(sock_devd, &line) < 0) | ||||
+ return; | + return; | ||||
+ | + if (strstr(line, cdev_create) == line) { | ||||
+ walk = strchr(line + 1, ' '); | + devicename = line + strlen(cdev_create) - strlen(cdev_path); | ||||
+ memcpy(devicename, cdev_path, strlen(cdev_path)); | |||||
+ walk = strchr(devicename, ' '); | |||||
+ if (walk != NULL) | + if (walk != NULL) | ||||
+ walk[0] = '\0'; | + walk[0] = '\0'; | ||||
+ | + /* Blacklist is lazy-populated in device_added() */ | ||||
+ switch (*line) { | + dev_list_init(&blacklist); | ||||
+ case DEVD_EVENT_ADD: | + device_added(devicename, &blacklist); | ||||
+ device_added(line + 1); | + dev_list_destroy(&blacklist); | ||||
+ break; | + } else if (strstr(line, cdev_destroy) == line) { | ||||
+ case DEVD_EVENT_REMOVE: | + devicename = line + strlen(cdev_destroy) - strlen(cdev_path); | ||||
+ device_removed(line + 1); | + memcpy(devicename, cdev_path, strlen(cdev_path)); | ||||
+ break; | + walk = strchr(devicename, ' '); | ||||
+ default: | + if (walk != NULL) | ||||
+ break; | + walk[0] = '\0'; | ||||
+ device_removed(devicename); | |||||
+ } | + } | ||||
+ free(line); | + free(line); | ||||
+ } | + } | ||||
+} | +} | ||||
+ | + | ||||
+static void | +static void | ||||
+block_handler(void *data, struct timeval **tv, void *read_mask) | +block_handler(void *data, struct timeval **tv, void *read_mask) | ||||
+{ | +{ | ||||
+} | +} | ||||
+ | + | ||||
+int | +int | ||||
+config_devd_init(void) | +config_devd_init(void) | ||||
+{ | +{ | ||||
+ char devicename[1024]; | + struct dev_list blacklist; | ||||
+ int i, j; | |||||
+ | + | ||||
+ LogMessage(X_INFO, "config/devd: probing input devices...\n"); | + LogMessage(X_INFO, "config/devd: probing input devices...\n"); | ||||
+ | + | ||||
+ /* | + /* Blacklist is lazy-populated in device_added() */ | ||||
+ * Add fake keyboard and give up on keyboards management | + dev_list_init(&blacklist); | ||||
+ * if kbdmux is enabled | |||||
+ */ | |||||
+ if ((is_kbdmux = is_kbdmux_enabled()) == true) | |||||
+ device_added("kbdmux"); | |||||
+ | + | ||||
+ for (i = 0; hw_types[i].driver != NULL; i++) { | + /* Check if kbdmux is enabled */ | ||||
+ /* First scan the sysctl to determine the hardware */ | + is_kbdmux = is_kbdmux_enabled(); | ||||
+ for (j = 0; j < 16; j++) { | |||||
+ if (sysctl_exists(&hw_types[i], j, | |||||
+ devicename, sizeof(devicename)) != 0) | |||||
+ device_added(devicename); | |||||
+ } | |||||
+ | + | ||||
+ if (devpath_exists(&hw_types[i], devicename, sizeof(devicename)) != 0) | + /* Check if evdev support is compiled into kernel */ | ||||
+ device_added(devicename); | + is_kernel_evdev = feature_present("evdev") != 0; | ||||
+ } | |||||
+ | + | ||||
+ /* Connect to devd, so that we don't loose any events */ | |||||
+ if ((sock_devd = connect_devd()) < 0) | + if ((sock_devd = connect_devd()) < 0) | ||||
+ return 0; | + return 0; | ||||
+ | + | ||||
+ RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); | + /* Scan what is currently connected */ | ||||
+ devpath_scan(&blacklist); | |||||
+ | + | ||||
+ /* Register wakeup handler */ | |||||
+ RegisterBlockAndWakeupHandlers(block_handler, | |||||
+ wakeup_handler, NULL); | |||||
+ | |||||
+ dev_list_destroy(&blacklist); | |||||
+ | |||||
+ return 1; | + return 1; | ||||
+} | +} | ||||
+ | + | ||||
+void | +void | ||||
+config_devd_fini(void) | +config_devd_fini(void) | ||||
+{ | +{ | ||||
+ LogMessage(X_INFO, "config/devd: terminating backend...\n"); | + LogMessage(X_INFO, "config/devd: terminating backend...\n"); | ||||
+ | + | ||||
+ if (rtimer) { | + if (rtimer) { | ||||
+ TimerFree(rtimer); | + TimerFree(rtimer); | ||||
+ rtimer = NULL; | + rtimer = NULL; | ||||
+ } | + } | ||||
+ | + | ||||
+ disconnect_devd(sock_devd); | + disconnect_devd(sock_devd); | ||||
+ | + | ||||
+ RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); | + RemoveBlockAndWakeupHandlers(block_handler, | ||||
+ | + wakeup_handler, NULL); | ||||
+ is_console_kbd = false; | |||||
+} | +} |