hal.c revision 05b261ec
105b261ecSmrg/* 205b261ecSmrg * Copyright © 2007 Daniel Stone 305b261ecSmrg * 405b261ecSmrg * Permission is hereby granted, free of charge, to any person obtaining a 505b261ecSmrg * copy of this software and associated documentation files (the "Software"), 605b261ecSmrg * to deal in the Software without restriction, including without limitation 705b261ecSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 805b261ecSmrg * and/or sell copies of the Software, and to permit persons to whom the 905b261ecSmrg * Software is furnished to do so, subject to the following conditions: 1005b261ecSmrg * 1105b261ecSmrg * The above copyright notice and this permission notice (including the next 1205b261ecSmrg * paragraph) shall be included in all copies or substantial portions of the 1305b261ecSmrg * Software. 1405b261ecSmrg * 1505b261ecSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1605b261ecSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1705b261ecSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1805b261ecSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1905b261ecSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2005b261ecSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2105b261ecSmrg * DEALINGS IN THE SOFTWARE. 2205b261ecSmrg * 2305b261ecSmrg * Author: Daniel Stone <daniel@fooishbar.org> 2405b261ecSmrg */ 2505b261ecSmrg 2605b261ecSmrg#ifdef HAVE_DIX_CONFIG_H 2705b261ecSmrg#include <dix-config.h> 2805b261ecSmrg#endif 2905b261ecSmrg 3005b261ecSmrg#include <dbus/dbus.h> 3105b261ecSmrg#include <hal/libhal.h> 3205b261ecSmrg#include <string.h> 3305b261ecSmrg#include <sys/select.h> 3405b261ecSmrg 3505b261ecSmrg#include "input.h" 3605b261ecSmrg#include "inputstr.h" 3705b261ecSmrg#include "hotplug.h" 3805b261ecSmrg#include "config-backends.h" 3905b261ecSmrg#include "os.h" 4005b261ecSmrg 4105b261ecSmrg#define TYPE_NONE 0 4205b261ecSmrg#define TYPE_KEYS 1 4305b261ecSmrg#define TYPE_POINTER 2 4405b261ecSmrg 4505b261ecSmrgstruct config_hal_info { 4605b261ecSmrg DBusConnection *system_bus; 4705b261ecSmrg LibHalContext *hal_ctx; 4805b261ecSmrg}; 4905b261ecSmrg 5005b261ecSmrgstatic void 5105b261ecSmrgremove_device(DeviceIntPtr dev) 5205b261ecSmrg{ 5305b261ecSmrg DebugF("[config/hal] removing device %s\n", dev->name); 5405b261ecSmrg 5505b261ecSmrg /* Call PIE here so we don't try to dereference a device that's 5605b261ecSmrg * already been removed. */ 5705b261ecSmrg OsBlockSignals(); 5805b261ecSmrg ProcessInputEvents(); 5905b261ecSmrg DeleteInputDeviceRequest(dev); 6005b261ecSmrg OsReleaseSignals(); 6105b261ecSmrg} 6205b261ecSmrg 6305b261ecSmrgstatic void 6405b261ecSmrgdevice_removed(LibHalContext *ctx, const char *udi) 6505b261ecSmrg{ 6605b261ecSmrg DeviceIntPtr dev, next; 6705b261ecSmrg char *value; 6805b261ecSmrg 6905b261ecSmrg value = xalloc(strlen(udi) + 5); /* "hal:" + NULL */ 7005b261ecSmrg if (!value) 7105b261ecSmrg return; 7205b261ecSmrg sprintf(value, "hal:%s", udi); 7305b261ecSmrg 7405b261ecSmrg for (dev = inputInfo.devices; dev; dev = next) { 7505b261ecSmrg next = dev->next; 7605b261ecSmrg if (dev->config_info && strcmp(dev->config_info, value) == 0) 7705b261ecSmrg remove_device(dev); 7805b261ecSmrg } 7905b261ecSmrg for (dev = inputInfo.off_devices; dev; dev = next) { 8005b261ecSmrg next = dev->next; 8105b261ecSmrg if (dev->config_info && strcmp(dev->config_info, value) == 0) 8205b261ecSmrg remove_device(dev); 8305b261ecSmrg } 8405b261ecSmrg 8505b261ecSmrg xfree(value); 8605b261ecSmrg} 8705b261ecSmrg 8805b261ecSmrgstatic void 8905b261ecSmrgadd_option(InputOption **options, const char *key, const char *value) 9005b261ecSmrg{ 9105b261ecSmrg if (!value || *value == '\0') 9205b261ecSmrg return; 9305b261ecSmrg 9405b261ecSmrg for (; *options; options = &(*options)->next) 9505b261ecSmrg ; 9605b261ecSmrg *options = xcalloc(sizeof(**options), 1); 9705b261ecSmrg if (!*options) /* Yeesh. */ 9805b261ecSmrg return; 9905b261ecSmrg (*options)->key = xstrdup(key); 10005b261ecSmrg (*options)->value = xstrdup(value); 10105b261ecSmrg (*options)->next = NULL; 10205b261ecSmrg} 10305b261ecSmrg 10405b261ecSmrgstatic char * 10505b261ecSmrgget_prop_string(LibHalContext *hal_ctx, const char *udi, const char *name) 10605b261ecSmrg{ 10705b261ecSmrg char *prop, *ret; 10805b261ecSmrg 10905b261ecSmrg prop = libhal_device_get_property_string(hal_ctx, udi, name, NULL); 11005b261ecSmrg DebugF(" [config/hal] getting %s on %s returned %s\n", name, udi, prop); 11105b261ecSmrg if (prop) { 11205b261ecSmrg ret = xstrdup(prop); 11305b261ecSmrg libhal_free_string(prop); 11405b261ecSmrg } 11505b261ecSmrg else { 11605b261ecSmrg return NULL; 11705b261ecSmrg } 11805b261ecSmrg 11905b261ecSmrg return ret; 12005b261ecSmrg} 12105b261ecSmrg 12205b261ecSmrgstatic char * 12305b261ecSmrgget_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop) 12405b261ecSmrg{ 12505b261ecSmrg char **props, *ret, *str; 12605b261ecSmrg int i, len = 0; 12705b261ecSmrg 12805b261ecSmrg props = libhal_device_get_property_strlist(hal_ctx, udi, prop, NULL); 12905b261ecSmrg if (props) { 13005b261ecSmrg for (i = 0; props[i]; i++) 13105b261ecSmrg len += strlen(props[i]); 13205b261ecSmrg 13305b261ecSmrg ret = xcalloc(sizeof(char), len + i); /* i - 1 commas, 1 NULL */ 13405b261ecSmrg if (!ret) { 13505b261ecSmrg libhal_free_string_array(props); 13605b261ecSmrg return NULL; 13705b261ecSmrg } 13805b261ecSmrg 13905b261ecSmrg str = ret; 14005b261ecSmrg for (i = 0; props[i]; i++) { 14105b261ecSmrg strcpy(str, props[i]); 14205b261ecSmrg str += strlen(props[i]); 14305b261ecSmrg *str++ = ','; 14405b261ecSmrg } 14505b261ecSmrg *(str-1) = '\0'; 14605b261ecSmrg 14705b261ecSmrg libhal_free_string_array(props); 14805b261ecSmrg } 14905b261ecSmrg else { 15005b261ecSmrg return NULL; 15105b261ecSmrg } 15205b261ecSmrg 15305b261ecSmrg return ret; 15405b261ecSmrg} 15505b261ecSmrg 15605b261ecSmrgstatic void 15705b261ecSmrgdevice_added(LibHalContext *hal_ctx, const char *udi) 15805b261ecSmrg{ 15905b261ecSmrg char **props; 16005b261ecSmrg char *path = NULL, *driver = NULL, *name = NULL, *xkb_rules = NULL; 16105b261ecSmrg char *xkb_model = NULL, *xkb_layout = NULL, *xkb_variant = NULL; 16205b261ecSmrg char *xkb_options = NULL, *config_info = NULL; 16305b261ecSmrg InputOption *options = NULL, *tmpo = NULL; 16405b261ecSmrg DeviceIntPtr dev; 16505b261ecSmrg DBusError error; 16605b261ecSmrg int type = TYPE_NONE; 16705b261ecSmrg int i; 16805b261ecSmrg 16905b261ecSmrg dbus_error_init(&error); 17005b261ecSmrg 17105b261ecSmrg props = libhal_device_get_property_strlist(hal_ctx, udi, 17205b261ecSmrg "info.capabilities", &error); 17305b261ecSmrg if (!props) { 17405b261ecSmrg DebugF("[config/hal] couldn't get capabilities for %s: %s (%s)\n", 17505b261ecSmrg udi, error.name, error.message); 17605b261ecSmrg goto out_error; 17705b261ecSmrg } 17805b261ecSmrg for (i = 0; props[i]; i++) { 17905b261ecSmrg /* input.keys is the new, of which input.keyboard is a subset, but 18005b261ecSmrg * input.keyboard is the old 'we have keys', so we have to keep it 18105b261ecSmrg * around. */ 18205b261ecSmrg if (strcmp(props[i], "input.keys") == 0 || 18305b261ecSmrg strcmp(props[i], "input.keyboard") == 0) 18405b261ecSmrg type |= TYPE_KEYS; 18505b261ecSmrg if (strcmp(props[i], "input.mouse") == 0 || 18605b261ecSmrg strcmp(props[i], "input.touchpad") == 0) 18705b261ecSmrg type |= TYPE_POINTER; 18805b261ecSmrg } 18905b261ecSmrg libhal_free_string_array(props); 19005b261ecSmrg 19105b261ecSmrg if (type == TYPE_NONE) 19205b261ecSmrg goto out_error; 19305b261ecSmrg 19405b261ecSmrg driver = get_prop_string(hal_ctx, udi, "input.x11_driver"); 19505b261ecSmrg path = get_prop_string(hal_ctx, udi, "input.device"); 19605b261ecSmrg if (!driver || !path) { 19705b261ecSmrg DebugF("[config/hal] no driver or path specified for %s\n", udi); 19805b261ecSmrg goto unwind; 19905b261ecSmrg } 20005b261ecSmrg name = get_prop_string(hal_ctx, udi, "info.product"); 20105b261ecSmrg if (!name) 20205b261ecSmrg name = xstrdup("(unnamed)"); 20305b261ecSmrg 20405b261ecSmrg if (type & TYPE_KEYS) { 20505b261ecSmrg xkb_rules = get_prop_string(hal_ctx, udi, "input.xkb.rules"); 20605b261ecSmrg xkb_model = get_prop_string(hal_ctx, udi, "input.xkb.model"); 20705b261ecSmrg xkb_layout = get_prop_string(hal_ctx, udi, "input.xkb.layout"); 20805b261ecSmrg xkb_variant = get_prop_string(hal_ctx, udi, "input.xkb.variant"); 20905b261ecSmrg xkb_options = get_prop_string_array(hal_ctx, udi, "input.xkb.options"); 21005b261ecSmrg } 21105b261ecSmrg 21205b261ecSmrg options = xcalloc(sizeof(*options), 1); 21305b261ecSmrg options->key = xstrdup("_source"); 21405b261ecSmrg options->value = xstrdup("server/hal"); 21505b261ecSmrg if (!options->key || !options->value) { 21605b261ecSmrg ErrorF("[config] couldn't allocate first key/value pair\n"); 21705b261ecSmrg goto unwind; 21805b261ecSmrg } 21905b261ecSmrg 22005b261ecSmrg add_option(&options, "path", path); 22105b261ecSmrg add_option(&options, "driver", driver); 22205b261ecSmrg add_option(&options, "name", name); 22305b261ecSmrg config_info = xalloc(strlen(udi) + 5); /* "hal:" and NULL */ 22405b261ecSmrg if (!config_info) 22505b261ecSmrg goto unwind; 22605b261ecSmrg sprintf(config_info, "hal:%s", udi); 22705b261ecSmrg 22805b261ecSmrg if (xkb_model) 22905b261ecSmrg add_option(&options, "xkb_model", xkb_model); 23005b261ecSmrg if (xkb_layout) 23105b261ecSmrg add_option(&options, "xkb_layout", xkb_layout); 23205b261ecSmrg if (xkb_variant) 23305b261ecSmrg add_option(&options, "xkb_variant", xkb_variant); 23405b261ecSmrg if (xkb_options) 23505b261ecSmrg add_option(&options, "xkb_options", xkb_options); 23605b261ecSmrg 23705b261ecSmrg if (NewInputDeviceRequest(options, &dev) != Success) { 23805b261ecSmrg DebugF("[config/hal] NewInputDeviceRequest failed\n"); 23905b261ecSmrg dev = NULL; 24005b261ecSmrg goto unwind; 24105b261ecSmrg } 24205b261ecSmrg 24305b261ecSmrg for (; dev; dev = dev->next) 24405b261ecSmrg dev->config_info = xstrdup(config_info); 24505b261ecSmrg 24605b261ecSmrgunwind: 24705b261ecSmrg if (path) 24805b261ecSmrg xfree(path); 24905b261ecSmrg if (driver) 25005b261ecSmrg xfree(driver); 25105b261ecSmrg if (name) 25205b261ecSmrg xfree(name); 25305b261ecSmrg if (xkb_rules) 25405b261ecSmrg xfree(xkb_rules); 25505b261ecSmrg if (xkb_model) 25605b261ecSmrg xfree(xkb_model); 25705b261ecSmrg if (xkb_layout) 25805b261ecSmrg xfree(xkb_layout); 25905b261ecSmrg if (xkb_options) 26005b261ecSmrg xfree(xkb_options); 26105b261ecSmrg if (config_info) 26205b261ecSmrg xfree(config_info); 26305b261ecSmrg while (!dev && (tmpo = options)) { 26405b261ecSmrg options = tmpo->next; 26505b261ecSmrg xfree(tmpo->key); 26605b261ecSmrg xfree(tmpo->value); 26705b261ecSmrg xfree(tmpo); 26805b261ecSmrg } 26905b261ecSmrg 27005b261ecSmrgout_error: 27105b261ecSmrg dbus_error_free(&error); 27205b261ecSmrg 27305b261ecSmrg return; 27405b261ecSmrg} 27505b261ecSmrg 27605b261ecSmrgstatic void 27705b261ecSmrgdisconnect_hook(void *data) 27805b261ecSmrg{ 27905b261ecSmrg DBusError error; 28005b261ecSmrg struct config_hal_info *info = data; 28105b261ecSmrg 28205b261ecSmrg if (info->hal_ctx) { 28305b261ecSmrg dbus_error_init(&error); 28405b261ecSmrg if (!libhal_ctx_shutdown(info->hal_ctx, &error)) 28505b261ecSmrg DebugF("[config/hal] couldn't shut down context: %s (%s)\n", 28605b261ecSmrg error.name, error.message); 28705b261ecSmrg libhal_ctx_free(info->hal_ctx); 28805b261ecSmrg dbus_error_free(&error); 28905b261ecSmrg } 29005b261ecSmrg 29105b261ecSmrg info->hal_ctx = NULL; 29205b261ecSmrg info->system_bus = NULL; 29305b261ecSmrg} 29405b261ecSmrg 29505b261ecSmrgstatic void 29605b261ecSmrgconnect_hook(DBusConnection *connection, void *data) 29705b261ecSmrg{ 29805b261ecSmrg DBusError error; 29905b261ecSmrg struct config_hal_info *info = data; 30005b261ecSmrg char **devices; 30105b261ecSmrg int num_devices, i; 30205b261ecSmrg 30305b261ecSmrg info->system_bus = connection; 30405b261ecSmrg 30505b261ecSmrg dbus_error_init(&error); 30605b261ecSmrg 30705b261ecSmrg if (!info->hal_ctx) 30805b261ecSmrg info->hal_ctx = libhal_ctx_new(); 30905b261ecSmrg if (!info->hal_ctx) { 31005b261ecSmrg ErrorF("[config/hal] couldn't create HAL context\n"); 31105b261ecSmrg goto out_err; 31205b261ecSmrg } 31305b261ecSmrg 31405b261ecSmrg if (!libhal_ctx_set_dbus_connection(info->hal_ctx, info->system_bus)) { 31505b261ecSmrg ErrorF("[config/hal] couldn't associate HAL context with bus\n"); 31605b261ecSmrg goto out_ctx; 31705b261ecSmrg } 31805b261ecSmrg if (!libhal_ctx_init(info->hal_ctx, &error)) { 31905b261ecSmrg ErrorF("[config/hal] couldn't initialise context: %s (%s)\n", 32005b261ecSmrg error.name, error.message); 32105b261ecSmrg goto out_ctx; 32205b261ecSmrg } 32305b261ecSmrg if (!libhal_device_property_watch_all(info->hal_ctx, &error)) { 32405b261ecSmrg ErrorF("[config/hal] couldn't watch all properties: %s (%s)\n", 32505b261ecSmrg error.name, error.message); 32605b261ecSmrg goto out_ctx2; 32705b261ecSmrg } 32805b261ecSmrg libhal_ctx_set_device_added(info->hal_ctx, device_added); 32905b261ecSmrg libhal_ctx_set_device_removed(info->hal_ctx, device_removed); 33005b261ecSmrg 33105b261ecSmrg devices = libhal_find_device_by_capability(info->hal_ctx, "input", 33205b261ecSmrg &num_devices, &error); 33305b261ecSmrg /* FIXME: Get default devices if error is set. */ 33405b261ecSmrg for (i = 0; i < num_devices; i++) 33505b261ecSmrg device_added(info->hal_ctx, devices[i]); 33605b261ecSmrg libhal_free_string_array(devices); 33705b261ecSmrg 33805b261ecSmrg dbus_error_free(&error); 33905b261ecSmrg 34005b261ecSmrg return; 34105b261ecSmrg 34205b261ecSmrgout_ctx2: 34305b261ecSmrg if (!libhal_ctx_shutdown(info->hal_ctx, &error)) 34405b261ecSmrg DebugF("[config/hal] couldn't shut down context: %s (%s)\n", 34505b261ecSmrg error.name, error.message); 34605b261ecSmrgout_ctx: 34705b261ecSmrg libhal_ctx_free(info->hal_ctx); 34805b261ecSmrgout_err: 34905b261ecSmrg dbus_error_free(&error); 35005b261ecSmrg 35105b261ecSmrg info->hal_ctx = NULL; 35205b261ecSmrg info->system_bus = NULL; 35305b261ecSmrg 35405b261ecSmrg return; 35505b261ecSmrg} 35605b261ecSmrg 35705b261ecSmrgstatic struct config_hal_info hal_info; 35805b261ecSmrgstatic struct config_dbus_core_hook hook = { 35905b261ecSmrg .connect = connect_hook, 36005b261ecSmrg .disconnect = disconnect_hook, 36105b261ecSmrg .data = &hal_info, 36205b261ecSmrg}; 36305b261ecSmrg 36405b261ecSmrgint 36505b261ecSmrgconfig_hal_init(void) 36605b261ecSmrg{ 36705b261ecSmrg memset(&hal_info, 0, sizeof(hal_info)); 36805b261ecSmrg hal_info.system_bus = NULL; 36905b261ecSmrg hal_info.hal_ctx = NULL; 37005b261ecSmrg 37105b261ecSmrg if (!config_dbus_core_add_hook(&hook)) { 37205b261ecSmrg ErrorF("[config/hal] failed to add D-Bus hook\n"); 37305b261ecSmrg return 0; 37405b261ecSmrg } 37505b261ecSmrg 37605b261ecSmrg return 1; 37705b261ecSmrg} 37805b261ecSmrg 37905b261ecSmrgvoid 38005b261ecSmrgconfig_hal_fini(void) 38105b261ecSmrg{ 38205b261ecSmrg config_dbus_core_remove_hook(&hook); 38305b261ecSmrg} 384