xref: /OK3568_Linux_fs/external/xserver/config/hal.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright © 2007 Daniel Stone
3*4882a593Smuzhiyun  * Copyright © 2007 Red Hat, Inc.
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Permission is hereby granted, free of charge, to any person obtaining a
6*4882a593Smuzhiyun  * copy of this software and associated documentation files (the "Software"),
7*4882a593Smuzhiyun  * to deal in the Software without restriction, including without limitation
8*4882a593Smuzhiyun  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9*4882a593Smuzhiyun  * and/or sell copies of the Software, and to permit persons to whom the
10*4882a593Smuzhiyun  * Software is furnished to do so, subject to the following conditions:
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * The above copyright notice and this permission notice (including the next
13*4882a593Smuzhiyun  * paragraph) shall be included in all copies or substantial portions of the
14*4882a593Smuzhiyun  * Software.
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*4882a593Smuzhiyun  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*4882a593Smuzhiyun  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19*4882a593Smuzhiyun  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*4882a593Smuzhiyun  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21*4882a593Smuzhiyun  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22*4882a593Smuzhiyun  * DEALINGS IN THE SOFTWARE.
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * Author: Daniel Stone <daniel@fooishbar.org>
25*4882a593Smuzhiyun  */
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun #ifdef HAVE_DIX_CONFIG_H
28*4882a593Smuzhiyun #include <dix-config.h>
29*4882a593Smuzhiyun #endif
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #include <dbus/dbus.h>
32*4882a593Smuzhiyun #include <hal/libhal.h>
33*4882a593Smuzhiyun #include <string.h>
34*4882a593Smuzhiyun #include <sys/select.h>
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #include "dbus-core.h"
37*4882a593Smuzhiyun #include "input.h"
38*4882a593Smuzhiyun #include "inputstr.h"
39*4882a593Smuzhiyun #include "hotplug.h"
40*4882a593Smuzhiyun #include "config-backends.h"
41*4882a593Smuzhiyun #include "os.h"
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #define LIBHAL_PROP_KEY "input.x11_options."
44*4882a593Smuzhiyun #define LIBHAL_XKB_PROP_KEY "input.xkb."
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun struct config_hal_info {
47*4882a593Smuzhiyun     DBusConnection *system_bus;
48*4882a593Smuzhiyun     LibHalContext *hal_ctx;
49*4882a593Smuzhiyun };
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun /* Used for special handling of xkb options. */
52*4882a593Smuzhiyun struct xkb_options {
53*4882a593Smuzhiyun     char *layout;
54*4882a593Smuzhiyun     char *model;
55*4882a593Smuzhiyun     char *rules;
56*4882a593Smuzhiyun     char *variant;
57*4882a593Smuzhiyun     char *options;
58*4882a593Smuzhiyun };
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun static void
device_removed(LibHalContext * ctx,const char * udi)61*4882a593Smuzhiyun device_removed(LibHalContext * ctx, const char *udi)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun     char *value;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun     if (asprintf(&value, "hal:%s", udi) == -1)
66*4882a593Smuzhiyun         return;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun     remove_devices("hal", value);
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun     free(value);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun static char *
get_prop_string(LibHalContext * hal_ctx,const char * udi,const char * name)74*4882a593Smuzhiyun get_prop_string(LibHalContext * hal_ctx, const char *udi, const char *name)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun     char *prop, *ret;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun     prop = libhal_device_get_property_string(hal_ctx, udi, name, NULL);
79*4882a593Smuzhiyun     LogMessageVerb(X_INFO, 10, "config/hal: getting %s on %s returned %s\n",
80*4882a593Smuzhiyun                    name, udi, prop ? prop : "(null)");
81*4882a593Smuzhiyun     if (prop) {
82*4882a593Smuzhiyun         ret = strdup(prop);
83*4882a593Smuzhiyun         libhal_free_string(prop);
84*4882a593Smuzhiyun     }
85*4882a593Smuzhiyun     else {
86*4882a593Smuzhiyun         return NULL;
87*4882a593Smuzhiyun     }
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun     return ret;
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun static char *
get_prop_string_array(LibHalContext * hal_ctx,const char * udi,const char * prop)93*4882a593Smuzhiyun get_prop_string_array(LibHalContext * hal_ctx, const char *udi,
94*4882a593Smuzhiyun                       const char *prop)
95*4882a593Smuzhiyun {
96*4882a593Smuzhiyun     char **props, *ret, *str;
97*4882a593Smuzhiyun     int i, len = 0;
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun     props = libhal_device_get_property_strlist(hal_ctx, udi, prop, NULL);
100*4882a593Smuzhiyun     if (props) {
101*4882a593Smuzhiyun         for (i = 0; props[i]; i++)
102*4882a593Smuzhiyun             len += strlen(props[i]);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun         ret = calloc(sizeof(char), len + i);    /* i - 1 commas, 1 NULL */
105*4882a593Smuzhiyun         if (!ret) {
106*4882a593Smuzhiyun             libhal_free_string_array(props);
107*4882a593Smuzhiyun             return NULL;
108*4882a593Smuzhiyun         }
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun         str = ret;
111*4882a593Smuzhiyun         for (i = 0; props[i]; i++) {
112*4882a593Smuzhiyun             strcpy(str, props[i]);
113*4882a593Smuzhiyun             str += strlen(props[i]);
114*4882a593Smuzhiyun             *str++ = ',';
115*4882a593Smuzhiyun         }
116*4882a593Smuzhiyun         *(str - 1) = '\0';
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun         libhal_free_string_array(props);
119*4882a593Smuzhiyun     }
120*4882a593Smuzhiyun     else {
121*4882a593Smuzhiyun         return NULL;
122*4882a593Smuzhiyun     }
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun     return ret;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun static void
device_added(LibHalContext * hal_ctx,const char * udi)128*4882a593Smuzhiyun device_added(LibHalContext * hal_ctx, const char *udi)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun     char *path = NULL, *driver = NULL, *name = NULL, *config_info = NULL;
131*4882a593Smuzhiyun     char *hal_tags, *parent;
132*4882a593Smuzhiyun     InputOption *input_options = NULL;
133*4882a593Smuzhiyun     InputAttributes attrs = { 0 };
134*4882a593Smuzhiyun     DeviceIntPtr dev = NULL;
135*4882a593Smuzhiyun     DBusError error;
136*4882a593Smuzhiyun     struct xkb_options xkb_opts = { 0 };
137*4882a593Smuzhiyun     int rc;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun     LibHalPropertySet *set = NULL;
140*4882a593Smuzhiyun     LibHalPropertySetIterator set_iter;
141*4882a593Smuzhiyun     char *psi_key = NULL, *tmp_val;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun     dbus_error_init(&error);
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun     driver = get_prop_string(hal_ctx, udi, "input.x11_driver");
146*4882a593Smuzhiyun     if (!driver) {
147*4882a593Smuzhiyun         /* verbose, don't tell the user unless they _want_ to see it */
148*4882a593Smuzhiyun         LogMessageVerb(X_INFO, 7,
149*4882a593Smuzhiyun                        "config/hal: no driver specified for device %s\n", udi);
150*4882a593Smuzhiyun         goto unwind;
151*4882a593Smuzhiyun     }
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun     path = get_prop_string(hal_ctx, udi, "input.device");
154*4882a593Smuzhiyun     if (!path) {
155*4882a593Smuzhiyun         LogMessage(X_WARNING,
156*4882a593Smuzhiyun                    "config/hal: no driver or path specified for %s\n", udi);
157*4882a593Smuzhiyun         goto unwind;
158*4882a593Smuzhiyun     }
159*4882a593Smuzhiyun     attrs.device = strdup(path);
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun     name = get_prop_string(hal_ctx, udi, "info.product");
162*4882a593Smuzhiyun     if (!name)
163*4882a593Smuzhiyun         name = strdup("(unnamed)");
164*4882a593Smuzhiyun     else
165*4882a593Smuzhiyun         attrs.product = strdup(name);
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun     attrs.vendor = get_prop_string(hal_ctx, udi, "info.vendor");
168*4882a593Smuzhiyun     hal_tags = get_prop_string(hal_ctx, udi, "input.tags");
169*4882a593Smuzhiyun     attrs.tags = xstrtokenize(hal_tags, ",");
170*4882a593Smuzhiyun     free(hal_tags);
171*4882a593Smuzhiyun 
172*4882a593Smuzhiyun     if (libhal_device_query_capability(hal_ctx, udi, "input.keys", NULL))
173*4882a593Smuzhiyun         attrs.flags |= ATTR_KEY | ATTR_KEYBOARD;
174*4882a593Smuzhiyun     if (libhal_device_query_capability(hal_ctx, udi, "input.mouse", NULL))
175*4882a593Smuzhiyun         attrs.flags |= ATTR_POINTER;
176*4882a593Smuzhiyun     if (libhal_device_query_capability(hal_ctx, udi, "input.joystick", NULL))
177*4882a593Smuzhiyun         attrs.flags |= ATTR_JOYSTICK;
178*4882a593Smuzhiyun     if (libhal_device_query_capability(hal_ctx, udi, "input.tablet", NULL))
179*4882a593Smuzhiyun         attrs.flags |= ATTR_TABLET;
180*4882a593Smuzhiyun     if (libhal_device_query_capability(hal_ctx, udi, "input.tablet_pad", NULL))
181*4882a593Smuzhiyun         attrs.flags |= ATTR_TABLET_PAD;
182*4882a593Smuzhiyun     if (libhal_device_query_capability(hal_ctx, udi, "input.touchpad", NULL))
183*4882a593Smuzhiyun         attrs.flags |= ATTR_TOUCHPAD;
184*4882a593Smuzhiyun     if (libhal_device_query_capability(hal_ctx, udi, "input.touchscreen", NULL))
185*4882a593Smuzhiyun         attrs.flags |= ATTR_TOUCHSCREEN;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun     parent = get_prop_string(hal_ctx, udi, "info.parent");
188*4882a593Smuzhiyun     if (parent) {
189*4882a593Smuzhiyun         int usb_vendor, usb_product;
190*4882a593Smuzhiyun         char *old_parent;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun         /* construct USB ID in lowercase - "0000:ffff" */
193*4882a593Smuzhiyun         usb_vendor = libhal_device_get_property_int(hal_ctx, parent,
194*4882a593Smuzhiyun                                                     "usb.vendor_id", NULL);
195*4882a593Smuzhiyun         LogMessageVerb(X_INFO, 10,
196*4882a593Smuzhiyun                        "config/hal: getting usb.vendor_id on %s "
197*4882a593Smuzhiyun                        "returned %04x\n", parent, usb_vendor);
198*4882a593Smuzhiyun         usb_product = libhal_device_get_property_int(hal_ctx, parent,
199*4882a593Smuzhiyun                                                      "usb.product_id", NULL);
200*4882a593Smuzhiyun         LogMessageVerb(X_INFO, 10,
201*4882a593Smuzhiyun                        "config/hal: getting usb.product_id on %s "
202*4882a593Smuzhiyun                        "returned %04x\n", parent, usb_product);
203*4882a593Smuzhiyun         if (usb_vendor && usb_product)
204*4882a593Smuzhiyun             if (asprintf(&attrs.usb_id, "%04x:%04x", usb_vendor, usb_product)
205*4882a593Smuzhiyun                 == -1)
206*4882a593Smuzhiyun                 attrs.usb_id = NULL;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun         attrs.pnp_id = get_prop_string(hal_ctx, parent, "pnp.id");
209*4882a593Smuzhiyun         old_parent = parent;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun         while (!attrs.pnp_id &&
212*4882a593Smuzhiyun                (parent = get_prop_string(hal_ctx, parent, "info.parent"))) {
213*4882a593Smuzhiyun             attrs.pnp_id = get_prop_string(hal_ctx, parent, "pnp.id");
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun             free(old_parent);
216*4882a593Smuzhiyun             old_parent = parent;
217*4882a593Smuzhiyun         }
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun         free(old_parent);
220*4882a593Smuzhiyun     }
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun     input_options = input_option_new(NULL, "_source", "server/hal");
223*4882a593Smuzhiyun     if (!input_options) {
224*4882a593Smuzhiyun         LogMessage(X_ERROR,
225*4882a593Smuzhiyun                    "config/hal: couldn't allocate first key/value pair\n");
226*4882a593Smuzhiyun         goto unwind;
227*4882a593Smuzhiyun     }
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun     /* most drivers use device.. not path. evdev uses both however, but the
230*4882a593Smuzhiyun      * path version isn't documented apparently. support both for now. */
231*4882a593Smuzhiyun     input_options = input_option_new(input_options, "path", path);
232*4882a593Smuzhiyun     input_options = input_option_new(input_options, "device", path);
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun     input_options = input_option_new(input_options, "driver", driver);
235*4882a593Smuzhiyun     input_options = input_option_new(input_options, "name", name);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun     if (asprintf(&config_info, "hal:%s", udi) == -1) {
238*4882a593Smuzhiyun         config_info = NULL;
239*4882a593Smuzhiyun         LogMessage(X_ERROR, "config/hal: couldn't allocate name\n");
240*4882a593Smuzhiyun         goto unwind;
241*4882a593Smuzhiyun     }
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun     /* Check for duplicate devices */
244*4882a593Smuzhiyun     if (device_is_duplicate(config_info)) {
245*4882a593Smuzhiyun         LogMessage(X_WARNING,
246*4882a593Smuzhiyun                    "config/hal: device %s already added. Ignoring.\n", name);
247*4882a593Smuzhiyun         goto unwind;
248*4882a593Smuzhiyun     }
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun     /* ok, grab options from hal.. iterate through all properties
251*4882a593Smuzhiyun      * and lets see if any of them are options that we can add */
252*4882a593Smuzhiyun     set = libhal_device_get_all_properties(hal_ctx, udi, &error);
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun     if (!set) {
255*4882a593Smuzhiyun         LogMessage(X_ERROR,
256*4882a593Smuzhiyun                    "config/hal: couldn't get property list for %s: %s (%s)\n",
257*4882a593Smuzhiyun                    udi, error.name, error.message);
258*4882a593Smuzhiyun         goto unwind;
259*4882a593Smuzhiyun     }
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun     libhal_psi_init(&set_iter, set);
262*4882a593Smuzhiyun     while (libhal_psi_has_more(&set_iter)) {
263*4882a593Smuzhiyun         /* we are looking for supported keys.. extract and add to options */
264*4882a593Smuzhiyun         psi_key = libhal_psi_get_key(&set_iter);
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun         if (psi_key) {
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun             /* normal options first (input.x11_options.<propname>) */
269*4882a593Smuzhiyun             if (!strncasecmp
270*4882a593Smuzhiyun                 (psi_key, LIBHAL_PROP_KEY, sizeof(LIBHAL_PROP_KEY) - 1)) {
271*4882a593Smuzhiyun                 char *tmp;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun                 /* only support strings for all values */
274*4882a593Smuzhiyun                 tmp_val = get_prop_string(hal_ctx, udi, psi_key);
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun                 if (tmp_val) {
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun                     /* xkb needs special handling. HAL specs include
279*4882a593Smuzhiyun                      * input.xkb.xyz options, but the x11-input.fdi specifies
280*4882a593Smuzhiyun                      * input.x11_options.Xkbxyz options. By default, we use
281*4882a593Smuzhiyun                      * the former, unless the specific X11 ones are specified.
282*4882a593Smuzhiyun                      * Since we can't predict the order in which the keys
283*4882a593Smuzhiyun                      * arrive, we need to store them.
284*4882a593Smuzhiyun                      */
285*4882a593Smuzhiyun                     if ((tmp = strcasestr(psi_key, "xkb")) && strlen(tmp) >= 4) {
286*4882a593Smuzhiyun                         if (!strcasecmp(&tmp[3], "layout")) {
287*4882a593Smuzhiyun                             free(xkb_opts.layout);
288*4882a593Smuzhiyun                             xkb_opts.layout = strdup(tmp_val);
289*4882a593Smuzhiyun                         }
290*4882a593Smuzhiyun                         else if (!strcasecmp(&tmp[3], "model")) {
291*4882a593Smuzhiyun                             free(xkb_opts.model);
292*4882a593Smuzhiyun                             xkb_opts.model = strdup(tmp_val);
293*4882a593Smuzhiyun                         }
294*4882a593Smuzhiyun                         else if (!strcasecmp(&tmp[3], "rules")) {
295*4882a593Smuzhiyun                             free(xkb_opts.rules);
296*4882a593Smuzhiyun                             xkb_opts.rules = strdup(tmp_val);
297*4882a593Smuzhiyun                         }
298*4882a593Smuzhiyun                         else if (!strcasecmp(&tmp[3], "variant")) {
299*4882a593Smuzhiyun                             free(xkb_opts.variant);
300*4882a593Smuzhiyun                             xkb_opts.variant = strdup(tmp_val);
301*4882a593Smuzhiyun                         }
302*4882a593Smuzhiyun                         else if (!strcasecmp(&tmp[3], "options")) {
303*4882a593Smuzhiyun                             free(xkb_opts.options);
304*4882a593Smuzhiyun                             xkb_opts.options = strdup(tmp_val);
305*4882a593Smuzhiyun                         }
306*4882a593Smuzhiyun                     }
307*4882a593Smuzhiyun                     else {
308*4882a593Smuzhiyun                         /* all others */
309*4882a593Smuzhiyun                         input_options =
310*4882a593Smuzhiyun                             input_option_new(input_options,
311*4882a593Smuzhiyun                                              psi_key + sizeof(LIBHAL_PROP_KEY) -
312*4882a593Smuzhiyun                                              1, tmp_val);
313*4882a593Smuzhiyun                         free(tmp_val);
314*4882a593Smuzhiyun                     }
315*4882a593Smuzhiyun                 }
316*4882a593Smuzhiyun                 else {
317*4882a593Smuzhiyun                     /* server 1.4 had xkb_options as strlist. */
318*4882a593Smuzhiyun                     if ((tmp = strcasestr(psi_key, "xkb")) &&
319*4882a593Smuzhiyun                         (strlen(tmp) >= 4) &&
320*4882a593Smuzhiyun                         (!strcasecmp(&tmp[3], "options")) &&
321*4882a593Smuzhiyun                         (tmp_val =
322*4882a593Smuzhiyun                          get_prop_string_array(hal_ctx, udi, psi_key))) {
323*4882a593Smuzhiyun                         free(xkb_opts.options);
324*4882a593Smuzhiyun                         xkb_opts.options = strdup(tmp_val);
325*4882a593Smuzhiyun                     }
326*4882a593Smuzhiyun                 }
327*4882a593Smuzhiyun             }
328*4882a593Smuzhiyun             else if (!strncasecmp
329*4882a593Smuzhiyun                      (psi_key, LIBHAL_XKB_PROP_KEY,
330*4882a593Smuzhiyun                       sizeof(LIBHAL_XKB_PROP_KEY) - 1)) {
331*4882a593Smuzhiyun                 char *tmp;
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun                 /* only support strings for all values */
334*4882a593Smuzhiyun                 tmp_val = get_prop_string(hal_ctx, udi, psi_key);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun                 if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) {
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun                     tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1];
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun                     if (!strcasecmp(tmp, "layout")) {
341*4882a593Smuzhiyun                         if (!xkb_opts.layout)
342*4882a593Smuzhiyun                             xkb_opts.layout = strdup(tmp_val);
343*4882a593Smuzhiyun                     }
344*4882a593Smuzhiyun                     else if (!strcasecmp(tmp, "rules")) {
345*4882a593Smuzhiyun                         if (!xkb_opts.rules)
346*4882a593Smuzhiyun                             xkb_opts.rules = strdup(tmp_val);
347*4882a593Smuzhiyun                     }
348*4882a593Smuzhiyun                     else if (!strcasecmp(tmp, "variant")) {
349*4882a593Smuzhiyun                         if (!xkb_opts.variant)
350*4882a593Smuzhiyun                             xkb_opts.variant = strdup(tmp_val);
351*4882a593Smuzhiyun                     }
352*4882a593Smuzhiyun                     else if (!strcasecmp(tmp, "model")) {
353*4882a593Smuzhiyun                         if (!xkb_opts.model)
354*4882a593Smuzhiyun                             xkb_opts.model = strdup(tmp_val);
355*4882a593Smuzhiyun                     }
356*4882a593Smuzhiyun                     else if (!strcasecmp(tmp, "options")) {
357*4882a593Smuzhiyun                         if (!xkb_opts.options)
358*4882a593Smuzhiyun                             xkb_opts.options = strdup(tmp_val);
359*4882a593Smuzhiyun                     }
360*4882a593Smuzhiyun                     free(tmp_val);
361*4882a593Smuzhiyun                 }
362*4882a593Smuzhiyun                 else {
363*4882a593Smuzhiyun                     /* server 1.4 had xkb options as strlist */
364*4882a593Smuzhiyun                     tmp_val = get_prop_string_array(hal_ctx, udi, psi_key);
365*4882a593Smuzhiyun                     if (tmp_val &&
366*4882a593Smuzhiyun                         strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) {
367*4882a593Smuzhiyun                         tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1];
368*4882a593Smuzhiyun                         if (!strcasecmp(tmp, ".options") && (!xkb_opts.options))
369*4882a593Smuzhiyun                             xkb_opts.options = strdup(tmp_val);
370*4882a593Smuzhiyun                     }
371*4882a593Smuzhiyun                     free(tmp_val);
372*4882a593Smuzhiyun                 }
373*4882a593Smuzhiyun             }
374*4882a593Smuzhiyun         }
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun         /* psi_key doesn't need to be freed */
377*4882a593Smuzhiyun         libhal_psi_next(&set_iter);
378*4882a593Smuzhiyun     }
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun     /* Now add xkb options */
381*4882a593Smuzhiyun     if (xkb_opts.layout)
382*4882a593Smuzhiyun         input_options =
383*4882a593Smuzhiyun             input_option_new(input_options, "xkb_layout", xkb_opts.layout);
384*4882a593Smuzhiyun     if (xkb_opts.rules)
385*4882a593Smuzhiyun         input_options =
386*4882a593Smuzhiyun             input_option_new(input_options, "xkb_rules", xkb_opts.rules);
387*4882a593Smuzhiyun     if (xkb_opts.variant)
388*4882a593Smuzhiyun         input_options =
389*4882a593Smuzhiyun             input_option_new(input_options, "xkb_variant", xkb_opts.variant);
390*4882a593Smuzhiyun     if (xkb_opts.model)
391*4882a593Smuzhiyun         input_options =
392*4882a593Smuzhiyun             input_option_new(input_options, "xkb_model", xkb_opts.model);
393*4882a593Smuzhiyun     if (xkb_opts.options)
394*4882a593Smuzhiyun         input_options =
395*4882a593Smuzhiyun             input_option_new(input_options, "xkb_options", xkb_opts.options);
396*4882a593Smuzhiyun     input_options = input_option_new(input_options, "config_info", config_info);
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun     /* this isn't an error, but how else do you output something that the user can see? */
399*4882a593Smuzhiyun     LogMessage(X_INFO, "config/hal: Adding input device %s\n", name);
400*4882a593Smuzhiyun     if ((rc = NewInputDeviceRequest(input_options, &attrs, &dev)) != Success) {
401*4882a593Smuzhiyun         LogMessage(X_ERROR, "config/hal: NewInputDeviceRequest failed (%d)\n",
402*4882a593Smuzhiyun                    rc);
403*4882a593Smuzhiyun         dev = NULL;
404*4882a593Smuzhiyun         goto unwind;
405*4882a593Smuzhiyun     }
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun  unwind:
408*4882a593Smuzhiyun     if (set)
409*4882a593Smuzhiyun         libhal_free_property_set(set);
410*4882a593Smuzhiyun     free(path);
411*4882a593Smuzhiyun     free(driver);
412*4882a593Smuzhiyun     free(name);
413*4882a593Smuzhiyun     free(config_info);
414*4882a593Smuzhiyun     input_option_free_list(&input_options);
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun     free(attrs.product);
417*4882a593Smuzhiyun     free(attrs.vendor);
418*4882a593Smuzhiyun     free(attrs.device);
419*4882a593Smuzhiyun     free(attrs.pnp_id);
420*4882a593Smuzhiyun     free(attrs.usb_id);
421*4882a593Smuzhiyun     if (attrs.tags) {
422*4882a593Smuzhiyun         char **tag = attrs.tags;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun         while (*tag) {
425*4882a593Smuzhiyun             free(*tag);
426*4882a593Smuzhiyun             tag++;
427*4882a593Smuzhiyun         }
428*4882a593Smuzhiyun         free(attrs.tags);
429*4882a593Smuzhiyun     }
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun     free(xkb_opts.layout);
432*4882a593Smuzhiyun     free(xkb_opts.rules);
433*4882a593Smuzhiyun     free(xkb_opts.model);
434*4882a593Smuzhiyun     free(xkb_opts.variant);
435*4882a593Smuzhiyun     free(xkb_opts.options);
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun     dbus_error_free(&error);
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun     return;
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun static void
disconnect_hook(void * data)443*4882a593Smuzhiyun disconnect_hook(void *data)
444*4882a593Smuzhiyun {
445*4882a593Smuzhiyun     DBusError error;
446*4882a593Smuzhiyun     struct config_hal_info *info = data;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun     if (info->hal_ctx) {
449*4882a593Smuzhiyun         if (dbus_connection_get_is_connected(info->system_bus)) {
450*4882a593Smuzhiyun             dbus_error_init(&error);
451*4882a593Smuzhiyun             if (!libhal_ctx_shutdown(info->hal_ctx, &error))
452*4882a593Smuzhiyun                 LogMessage(X_WARNING,
453*4882a593Smuzhiyun                            "config/hal: disconnect_hook couldn't shut down context: %s (%s)\n",
454*4882a593Smuzhiyun                            error.name, error.message);
455*4882a593Smuzhiyun             dbus_error_free(&error);
456*4882a593Smuzhiyun         }
457*4882a593Smuzhiyun         libhal_ctx_free(info->hal_ctx);
458*4882a593Smuzhiyun     }
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun     info->hal_ctx = NULL;
461*4882a593Smuzhiyun     info->system_bus = NULL;
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun static BOOL
connect_and_register(DBusConnection * connection,struct config_hal_info * info)465*4882a593Smuzhiyun connect_and_register(DBusConnection * connection, struct config_hal_info *info)
466*4882a593Smuzhiyun {
467*4882a593Smuzhiyun     DBusError error;
468*4882a593Smuzhiyun     char **devices;
469*4882a593Smuzhiyun     int num_devices, i;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun     if (info->hal_ctx)
472*4882a593Smuzhiyun         return TRUE;            /* already registered, pretend we did something */
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun     info->system_bus = connection;
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun     dbus_error_init(&error);
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun     info->hal_ctx = libhal_ctx_new();
479*4882a593Smuzhiyun     if (!info->hal_ctx) {
480*4882a593Smuzhiyun         LogMessage(X_ERROR, "config/hal: couldn't create HAL context\n");
481*4882a593Smuzhiyun         goto out_err;
482*4882a593Smuzhiyun     }
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun     if (!libhal_ctx_set_dbus_connection(info->hal_ctx, info->system_bus)) {
485*4882a593Smuzhiyun         LogMessage(X_ERROR,
486*4882a593Smuzhiyun                    "config/hal: couldn't associate HAL context with bus\n");
487*4882a593Smuzhiyun         goto out_err;
488*4882a593Smuzhiyun     }
489*4882a593Smuzhiyun     if (!libhal_ctx_init(info->hal_ctx, &error)) {
490*4882a593Smuzhiyun         LogMessage(X_ERROR,
491*4882a593Smuzhiyun                    "config/hal: couldn't initialise context: %s (%s)\n",
492*4882a593Smuzhiyun                    error.name ? error.name : "unknown error",
493*4882a593Smuzhiyun                    error.message ? error.message : "null");
494*4882a593Smuzhiyun         goto out_err;
495*4882a593Smuzhiyun     }
496*4882a593Smuzhiyun     if (!libhal_device_property_watch_all(info->hal_ctx, &error)) {
497*4882a593Smuzhiyun         LogMessage(X_ERROR,
498*4882a593Smuzhiyun                    "config/hal: couldn't watch all properties: %s (%s)\n",
499*4882a593Smuzhiyun                    error.name ? error.name : "unknown error",
500*4882a593Smuzhiyun                    error.message ? error.message : "null");
501*4882a593Smuzhiyun         goto out_ctx;
502*4882a593Smuzhiyun     }
503*4882a593Smuzhiyun     libhal_ctx_set_device_added(info->hal_ctx, device_added);
504*4882a593Smuzhiyun     libhal_ctx_set_device_removed(info->hal_ctx, device_removed);
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun     devices = libhal_find_device_by_capability(info->hal_ctx, "input",
507*4882a593Smuzhiyun                                                &num_devices, &error);
508*4882a593Smuzhiyun     /* FIXME: Get default devices if error is set. */
509*4882a593Smuzhiyun     if (dbus_error_is_set(&error)) {
510*4882a593Smuzhiyun         LogMessage(X_ERROR, "config/hal: couldn't find input device: %s (%s)\n",
511*4882a593Smuzhiyun                    error.name ? error.name : "unknown error",
512*4882a593Smuzhiyun                    error.message ? error.message : "null");
513*4882a593Smuzhiyun         goto out_ctx;
514*4882a593Smuzhiyun     }
515*4882a593Smuzhiyun     for (i = 0; i < num_devices; i++)
516*4882a593Smuzhiyun         device_added(info->hal_ctx, devices[i]);
517*4882a593Smuzhiyun     libhal_free_string_array(devices);
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun     dbus_error_free(&error);
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun     return TRUE;
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun  out_ctx:
524*4882a593Smuzhiyun     dbus_error_free(&error);
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun     if (!libhal_ctx_shutdown(info->hal_ctx, &error)) {
527*4882a593Smuzhiyun         LogMessage(X_WARNING,
528*4882a593Smuzhiyun                    "config/hal: couldn't shut down context: %s (%s)\n",
529*4882a593Smuzhiyun                    error.name ? error.name : "unknown error",
530*4882a593Smuzhiyun                    error.message ? error.message : "null");
531*4882a593Smuzhiyun         dbus_error_free(&error);
532*4882a593Smuzhiyun     }
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun  out_err:
535*4882a593Smuzhiyun     dbus_error_free(&error);
536*4882a593Smuzhiyun 
537*4882a593Smuzhiyun     if (info->hal_ctx) {
538*4882a593Smuzhiyun         libhal_ctx_free(info->hal_ctx);
539*4882a593Smuzhiyun     }
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun     info->hal_ctx = NULL;
542*4882a593Smuzhiyun     info->system_bus = NULL;
543*4882a593Smuzhiyun 
544*4882a593Smuzhiyun     return FALSE;
545*4882a593Smuzhiyun }
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun /**
548*4882a593Smuzhiyun  * Handle NewOwnerChanged signals to deal with HAL startup at X server runtime.
549*4882a593Smuzhiyun  *
550*4882a593Smuzhiyun  * NewOwnerChanged is send once when HAL shuts down, and once again when it
551*4882a593Smuzhiyun  * comes back up. Message has three arguments, first is the name
552*4882a593Smuzhiyun  * (org.freedesktop.Hal), the second one is the old owner, third one is new
553*4882a593Smuzhiyun  * owner.
554*4882a593Smuzhiyun  */
555*4882a593Smuzhiyun static DBusHandlerResult
ownerchanged_handler(DBusConnection * connection,DBusMessage * message,void * data)556*4882a593Smuzhiyun ownerchanged_handler(DBusConnection * connection, DBusMessage * message,
557*4882a593Smuzhiyun                      void *data)
558*4882a593Smuzhiyun {
559*4882a593Smuzhiyun     int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun     if (dbus_message_is_signal(message,
562*4882a593Smuzhiyun                                "org.freedesktop.DBus", "NameOwnerChanged")) {
563*4882a593Smuzhiyun         DBusError error;
564*4882a593Smuzhiyun         char *name, *old_owner, *new_owner;
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun         dbus_error_init(&error);
567*4882a593Smuzhiyun         dbus_message_get_args(message, &error,
568*4882a593Smuzhiyun                               DBUS_TYPE_STRING, &name,
569*4882a593Smuzhiyun                               DBUS_TYPE_STRING, &old_owner,
570*4882a593Smuzhiyun                               DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID);
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun         if (dbus_error_is_set(&error)) {
573*4882a593Smuzhiyun             ErrorF
574*4882a593Smuzhiyun                 ("[config/hal] failed to get NameOwnerChanged args: %s (%s)\n",
575*4882a593Smuzhiyun                  error.name, error.message);
576*4882a593Smuzhiyun         }
577*4882a593Smuzhiyun         else if (name && strcmp(name, "org.freedesktop.Hal") == 0) {
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun             if (!old_owner || !strlen(old_owner)) {
580*4882a593Smuzhiyun                 DebugF("[config/hal] HAL startup detected.\n");
581*4882a593Smuzhiyun                 if (connect_and_register
582*4882a593Smuzhiyun                     (connection, (struct config_hal_info *) data))
583*4882a593Smuzhiyun                     dbus_connection_unregister_object_path(connection,
584*4882a593Smuzhiyun                                                            "/org/freedesktop/DBus");
585*4882a593Smuzhiyun                 else
586*4882a593Smuzhiyun                     ErrorF("[config/hal] Failed to connect to HAL bus.\n");
587*4882a593Smuzhiyun             }
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun             ret = DBUS_HANDLER_RESULT_HANDLED;
590*4882a593Smuzhiyun         }
591*4882a593Smuzhiyun         dbus_error_free(&error);
592*4882a593Smuzhiyun     }
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun     return ret;
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun /**
598*4882a593Smuzhiyun  * Register a handler for the NameOwnerChanged signal.
599*4882a593Smuzhiyun  */
600*4882a593Smuzhiyun static BOOL
listen_for_startup(DBusConnection * connection,void * data)601*4882a593Smuzhiyun listen_for_startup(DBusConnection * connection, void *data)
602*4882a593Smuzhiyun {
603*4882a593Smuzhiyun     DBusObjectPathVTable vtable = {.message_function = ownerchanged_handler, };
604*4882a593Smuzhiyun     DBusError error;
605*4882a593Smuzhiyun     const char MATCH_RULE[] = "sender='org.freedesktop.DBus',"
606*4882a593Smuzhiyun         "interface='org.freedesktop.DBus',"
607*4882a593Smuzhiyun         "type='signal',"
608*4882a593Smuzhiyun         "path='/org/freedesktop/DBus'," "member='NameOwnerChanged'";
609*4882a593Smuzhiyun     int rc = FALSE;
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun     dbus_error_init(&error);
612*4882a593Smuzhiyun     dbus_bus_add_match(connection, MATCH_RULE, &error);
613*4882a593Smuzhiyun     if (!dbus_error_is_set(&error)) {
614*4882a593Smuzhiyun         if (dbus_connection_register_object_path(connection,
615*4882a593Smuzhiyun                                                  "/org/freedesktop/DBus",
616*4882a593Smuzhiyun                                                  &vtable, data))
617*4882a593Smuzhiyun             rc = TRUE;
618*4882a593Smuzhiyun         else
619*4882a593Smuzhiyun             ErrorF("[config/hal] cannot register object path.\n");
620*4882a593Smuzhiyun     }
621*4882a593Smuzhiyun     else {
622*4882a593Smuzhiyun         ErrorF("[config/hal] couldn't add match rule: %s (%s)\n", error.name,
623*4882a593Smuzhiyun                error.message);
624*4882a593Smuzhiyun         ErrorF("[config/hal] cannot detect a HAL startup.\n");
625*4882a593Smuzhiyun     }
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun     dbus_error_free(&error);
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun     return rc;
630*4882a593Smuzhiyun }
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun static void
connect_hook(DBusConnection * connection,void * data)633*4882a593Smuzhiyun connect_hook(DBusConnection * connection, void *data)
634*4882a593Smuzhiyun {
635*4882a593Smuzhiyun     struct config_hal_info *info = data;
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun     if (listen_for_startup(connection, data) &&
638*4882a593Smuzhiyun         connect_and_register(connection, info))
639*4882a593Smuzhiyun         dbus_connection_unregister_object_path(connection,
640*4882a593Smuzhiyun                                                "/org/freedesktop/DBus");
641*4882a593Smuzhiyun 
642*4882a593Smuzhiyun     return;
643*4882a593Smuzhiyun }
644*4882a593Smuzhiyun 
645*4882a593Smuzhiyun static struct config_hal_info hal_info;
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun static struct dbus_core_hook hook = {
648*4882a593Smuzhiyun     .connect = connect_hook,
649*4882a593Smuzhiyun     .disconnect = disconnect_hook,
650*4882a593Smuzhiyun     .data = &hal_info,
651*4882a593Smuzhiyun };
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun int
config_hal_init(void)654*4882a593Smuzhiyun config_hal_init(void)
655*4882a593Smuzhiyun {
656*4882a593Smuzhiyun     memset(&hal_info, 0, sizeof(hal_info));
657*4882a593Smuzhiyun     hal_info.system_bus = NULL;
658*4882a593Smuzhiyun     hal_info.hal_ctx = NULL;
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun     if (!dbus_core_add_hook(&hook)) {
661*4882a593Smuzhiyun         LogMessage(X_ERROR, "config/hal: failed to add D-Bus hook\n");
662*4882a593Smuzhiyun         return 0;
663*4882a593Smuzhiyun     }
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun     /* verbose message */
666*4882a593Smuzhiyun     LogMessageVerb(X_INFO, 7, "config/hal: initialized\n");
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun     return 1;
669*4882a593Smuzhiyun }
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun void
config_hal_fini(void)672*4882a593Smuzhiyun config_hal_fini(void)
673*4882a593Smuzhiyun {
674*4882a593Smuzhiyun     dbus_core_remove_hook(&hook);
675*4882a593Smuzhiyun }
676