1 /***************************************************************************
2  *
3  * devinfo_usb.h : USB devices
4  *
5  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
6  * Use is subject to license terms.
7  *
8  * Licensed under the Academic Free License version 2.1
9  *
10  **************************************************************************/
11 
12 #ifdef HAVE_CONFIG_H
13 #  include <config.h>
14 #endif
15 
16 #include <stdio.h>
17 #include <string.h>
18 #include <libdevinfo.h>
19 #include <unistd.h>
20 #include <dirent.h>
21 #include <sys/types.h>
22 #include <sys/mkdev.h>
23 #include <sys/stat.h>
24 #include <sys/usb/usbai.h>
25 
26 #include "../osspec.h"
27 #include "../logger.h"
28 #include "../hald.h"
29 #include "../hald_dbus.h"
30 #include "../device_info.h"
31 #include "../util.h"
32 #include "../ids.h"
33 #include "hotplug.h"
34 #include "devinfo.h"
35 #include "devinfo_usb.h"
36 
37 static HalDevice *devinfo_usb_if_add(HalDevice *d, di_node_t node, gchar *devfs_path,
38 				     gchar *if_devfs_path, int ifnum);
39 static HalDevice *devinfo_usb_scsa2usb_add(HalDevice *d, di_node_t node);
40 static HalDevice *devinfo_usb_printer_add(HalDevice *usbd, di_node_t node);
41 static HalDevice *devinfo_usb_input_add(HalDevice *usbd, di_node_t node);
42 const gchar *devinfo_printer_prnio_get_prober(HalDevice *d, int *timeout);
43 const gchar *devinfo_keyboard_get_prober(HalDevice *d, int *timeout);
44 static void set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name);
45 
46 DevinfoDevHandler devinfo_usb_handler = {
47 	devinfo_usb_add,
48 	NULL,
49 	NULL,
50 	NULL,
51 	NULL,
52 	NULL
53 };
54 
55 DevinfoDevHandler devinfo_usb_printer_handler = {
56 	devinfo_usb_add,
57 	NULL,
58 	NULL,
59 	NULL,
60 	NULL,
61 	devinfo_printer_prnio_get_prober
62 };
63 
64 DevinfoDevHandler devinfo_usb_keyboard_handler = {
65 	devinfo_usb_add,
66 	NULL,
67 	NULL,
68 	NULL,
69 	NULL,
70 	devinfo_keyboard_get_prober
71 };
72 
73 static gboolean
74 is_usb_node(di_node_t node)
75 {
76 	int rc;
77 	char *s;
78 
79 	/*
80 	 * USB device nodes will have "compatible" propety values that
81 	 * begins with "usb".
82 	 */
83 	rc = di_prop_lookup_strings(DDI_DEV_T_ANY, node, "compatible", &s);
84 	while (rc-- > 0) {
85 		if (strncmp(s, "usb", 3) == 0) {
86 			return (TRUE);
87 		}
88 		s += (strlen(s) + 1);
89 	}
90 
91 	return (FALSE);
92 }
93 
94 static char *
95 get_hid_devlink(char *devfs_path)
96 {
97 	char *result = NULL;
98 	DIR *dp;
99 
100 	if ((dp = opendir("/dev/usb")) != NULL) {
101 		struct dirent *ep;
102 
103 		while ((ep = readdir(dp)) != NULL) {
104 			char path[MAXPATHLEN], lpath[MAXPATHLEN];
105 
106 			snprintf(path, sizeof (path), "/dev/usb/%s",
107 			    ep->d_name);
108 			memset(lpath, 0, sizeof (lpath));
109 			if ((readlink(path, lpath, sizeof (lpath)) > 0) &&
110 			    (strstr(lpath, devfs_path) != NULL)) {
111 				result = strdup(path);
112 				break;
113 			}
114 		}
115 		closedir(dp);
116 	}
117 
118 	return (result);
119 }
120 
121 HalDevice *
122 devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
123 {
124 	HalDevice *d, *nd = NULL;
125 	char	*s;
126 	int	*i;
127 	char	*driver_name, *binding_name;
128 	char	if_devfs_path[HAL_PATH_MAX];
129 	di_devlink_handle_t hdl;
130 	double	k;
131 
132 	if (is_usb_node(node) == FALSE) {
133 		return (NULL);
134 	}
135 
136 	driver_name = di_driver_name (node);
137 
138 	if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface", &i) < 0) {
139 		/* It is a USB device node. */
140 
141 		d = hal_device_new ();
142 
143 		devinfo_set_default_properties (d, parent, node, devfs_path);
144 		hal_device_property_set_string (d, "info.subsystem", "usb_device");
145 		PROP_STR(d, node, s, "usb-product-name", "info.product");
146 		PROP_STR(d, node, s, "usb-product-name", "usb_device.product");
147 		PROP_STR(d, node, s, "usb-vendor-name", "usb_device.vendor");
148 		PROP_INT(d, node, i, "usb-vendor-id", "usb_device.vendor_id");
149 		PROP_INT(d, node, i, "usb-product-id", "usb_device.product_id");
150 		PROP_INT(d, node, i, "usb-revision-id", "usb_device.device_revision_bcd");
151 		PROP_STR(d, node, s, "usb-serialno", "usb_device.serial");
152 		PROP_INT(d, node, i, "usb-port-count", "usb_device.num_ports");
153 		PROP_INT(d, node, i, "usb-num-configs", "usb_device.num_configurations");
154 		PROP_INT(d, node, i, "assigned-address", "usb_device.bus_number");
155 
156 		if  (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "usb-release", &i) > 0) {
157 			k = (double)bcd(*i);
158 			hal_device_property_set_double (d, "usb_device.version", k / 100);
159 		}
160 
161 		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "low-speed", &i) >= 0) {
162 			k = 1.5;
163 		} else if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "high-speed", &i) >= 0) {
164 			k = 480.0;
165 		} else {
166 			/* It is the full speed device. */
167 			k = 12.0;
168 		}
169 		hal_device_property_set_double (d, "usb_device.speed", k);
170 
171 		set_usb_properties (d, node, devfs_path, driver_name);
172 
173 		/* wait for the ugen node's creation */
174 		if ((driver_name != NULL) && (strcmp (driver_name, "usb_mid") == 0)) {
175 			if (hdl = di_devlink_init (devfs_path, DI_MAKE_LINK)) {
176 				di_devlink_fini (&hdl);
177 			}
178 		}
179 
180 		devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
181 
182 		/* add to TDL so preprobing callouts and prober can access it */
183 		hal_device_store_add (hald_get_tdl (), d);
184 
185 		if (((binding_name = di_binding_name (node)) != NULL) &&
186 		    (strncmp (binding_name, "usbif,", sizeof ("usbif,") - 1) == 0)) {
187 
188 			snprintf (if_devfs_path, sizeof (if_devfs_path), "%s:if%d",
189 			    devfs_path, 0);
190 			if ((nd = devinfo_usb_if_add (d, node, if_devfs_path,
191 			    if_devfs_path, 0)) != NULL) {
192 				d = nd;
193 				nd = NULL;
194 				devfs_path = if_devfs_path;
195 			}
196 		}
197 	} else {
198 		/* It is a USB interface node or IA node. */
199 		int *j;
200 
201 		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface-count", &j) > 0) {
202 			/*
203 			 * The USB IA node properties are not defined in
204 			 * HAL spec so far. So IA node udi has "ia" sign
205 			 * now, different from the IF node udi with "if".
206 			 */
207 			snprintf (if_devfs_path, sizeof (if_devfs_path),
208 			    "%s:ia%d", devfs_path, *i);
209 		} else {
210 			snprintf (if_devfs_path, sizeof (if_devfs_path),
211 			    "%s:if%d", devfs_path, *i);
212 		}
213 
214 		d = devinfo_usb_if_add (parent, node, devfs_path, if_devfs_path, *i);
215 	}
216 
217 	/* driver specific */
218 	if (driver_name != NULL) {
219 		if (strcmp (driver_name, "scsa2usb") == 0) {
220 			nd = devinfo_usb_scsa2usb_add (d, node);
221 		} else if (strcmp (driver_name, "usbprn") == 0) {
222 			nd = devinfo_usb_printer_add (d, node);
223 		} else if (strcmp(driver_name, "hid") == 0) {
224 			if (hdl = di_devlink_init(devfs_path, DI_MAKE_LINK)) {
225 				di_devlink_fini(&hdl);
226 			}
227 			nd = devinfo_usb_input_add(d, node);
228 		}
229 	}
230 
231 out:
232 	if (nd != NULL) {
233 		return (nd);
234 	} else {
235 		return (d);
236 	}
237 }
238 
239 
240 static void
241 set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name)
242 {
243 	usb_dev_descr_t	*dev_descrp = NULL;	/* device descriptor */
244 	usb_cfg_descr_t	*cfg_descrp = NULL;	/* configuration descriptor */
245 	unsigned char	*rdata = NULL;
246 	char *p;
247 	int i = 0;
248 
249 	hal_device_property_set_int (d, "usb_device.port_number",
250 	    atoi (devfs_path + strlen (devfs_path) -1));
251 
252 	if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-dev-descriptor",
253 	    &rdata) > 0) {
254 		dev_descrp = (usb_dev_descr_t *)rdata;
255 
256 		if (dev_descrp != NULL) {
257 			hal_device_property_set_int (d, "usb_device.device_class",
258 			    dev_descrp->bDeviceClass);
259 			hal_device_property_set_int (d, "usb_device.device_subclass",
260 			    dev_descrp->bDeviceSubClass);
261 			hal_device_property_set_int (d, "usb_device.device_protocol",
262 			    dev_descrp->bDeviceProtocol);
263 		}
264 	}
265 
266 	if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-raw-cfg-descriptors",
267 	    &rdata) > 0) {
268 		cfg_descrp = (usb_cfg_descr_t *)(rdata);
269 
270 		if (cfg_descrp != NULL) {
271 			hal_device_property_set_int (d, "usb_device.configuration_value",
272 			    cfg_descrp->bConfigurationValue);
273 			hal_device_property_set_int (d, "usb_device.max_power",
274 			    cfg_descrp->bMaxPower);
275 			hal_device_property_set_int (d, "usb_device.num_interfaces",
276 			    cfg_descrp->bNumInterfaces);
277 			hal_device_property_set_bool (d, "usb_device.can_wake_up",
278 			    (cfg_descrp->bmAttributes & 0x20) ? TRUE : FALSE);
279 			hal_device_property_set_bool (d, "usb_device.is_self_powered",
280 			    (cfg_descrp->bmAttributes & 0x40) ? TRUE : FALSE);
281 		}
282 	}
283 
284 	/* get the node's usb tree level by counting hub numbers */
285 	do {
286 		if (p = strstr (devfs_path, "/hub@")) {
287 			devfs_path = p + strlen ("/hub@");
288 			i ++;
289 		}
290 	} while (p != NULL);
291 
292 	if ((driver_name != NULL) && (strcmp (driver_name, "hubd") == 0) && (i > 0))
293 		i --;
294 
295 	hal_device_property_set_int (d, "usb_device.level_number", i);
296 }
297 
298 
299 static usb_if_descr_t *
300 parse_usb_if_descr(di_node_t node, int ifnum)
301 {
302 	unsigned char	*rdata = NULL;
303 	usb_if_descr_t	*if_descrp=NULL;	/* interface descriptor */
304 	di_node_t	tmp_node = DI_NODE_NIL;
305 	uint8_t num, length, type;
306 	int rlen;
307 	gchar *devpath = NULL;
308 
309 	if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, node,
310 	     "usb-raw-cfg-descriptors", &rdata)) < 0) {
311 
312 		char *p;
313 		int i;
314 
315 		if ((devpath = di_devfs_path (node)) == NULL)
316 			goto out;
317 
318 		/* Look up its parent that may be a USB IA or USB mid. */
319 		for (i = 0; i < 2; i++) {
320 			p = strrchr (devpath, '/');
321 			if (p == NULL)
322 				goto out;
323 			*p = '\0';
324 
325 			if ((tmp_node = di_init (devpath, DINFOCPYALL)) == DI_NODE_NIL)
326 				goto out;
327 
328 			if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, tmp_node,
329 			     "usb-raw-cfg-descriptors", &rdata)) > 0)
330 				break;
331 
332 			di_fini (tmp_node);
333 		}
334 	}
335 
336 	if (rdata == NULL)
337 		goto out;
338 
339 	do {
340 		length = (uint8_t)*rdata;
341 		type = (uint8_t)*(rdata + 1);
342 		if (type == USB_DESCR_TYPE_IF) {
343 			num = (uint8_t)*(rdata + 2);
344 			if (num == ifnum) {
345 				if_descrp = (usb_if_descr_t *)rdata;
346 				break;
347 			}
348 		}
349 		rdata += length;
350 		rlen -= length;
351 	} while ((length > 0 ) && (rlen > 0));
352 
353 out:
354 	if (devpath != NULL)
355 		di_devfs_path_free (devpath);
356 	if (tmp_node != DI_NODE_NIL)
357 		di_fini (tmp_node);
358 	return (if_descrp);
359 }
360 
361 
362 static HalDevice *
363 devinfo_usb_if_add(HalDevice *parent, di_node_t node, gchar *devfs_path,
364 		   gchar *if_devfs_path, int ifnum)
365 {
366 	HalDevice	*d = NULL;
367 	char		udi[HAL_PATH_MAX];
368 	const char	*parent_info;
369 	usb_if_descr_t	*if_descrp=NULL;	/* interface descriptor */
370 
371 	d = hal_device_new ();
372 
373 	devinfo_set_default_properties (d, parent, node, if_devfs_path);
374 
375 	/* Set the existed physical device path. */
376 	hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
377 	hal_device_property_set_string (d, "info.subsystem", "usb");
378 	hal_device_property_set_string (d, "info.product", "USB Device Interface");
379 
380 	/* Set usb interface properties to interface node. */
381 	if (strstr (if_devfs_path, ":ia") == NULL) {
382 		if_descrp = parse_usb_if_descr (node, ifnum);
383 
384 		if (if_descrp != NULL) {
385 			hal_device_property_set_int (d, "usb.interface.class",
386 			    if_descrp->bInterfaceClass);
387 			hal_device_property_set_int (d, "usb.interface.subclass",
388 			    if_descrp->bInterfaceSubClass);
389 			hal_device_property_set_int (d, "usb.interface.protocol",
390 			    if_descrp->bInterfaceProtocol);
391 			hal_device_property_set_int (d, "usb.interface.number",
392 			    if_descrp->bInterfaceNumber);
393 		}
394 	}
395 
396 	/* copy parent's usb_device.* properties */
397 	parent_info = hal_device_property_get_string (parent, "info.subsystem");
398 	if (parent_info != NULL) {
399 		if (strcmp (parent_info, "usb_device") == 0) {
400 			hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device.");
401 		} else if (strcmp (parent_info, "usb") == 0) {
402 			/* for the case that the parent is IA node */
403 			hal_device_merge_with_rewrite (d, parent, "usb.", "usb.");
404 		}
405 	}
406 
407 	devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
408 
409 	/* add to TDL so preprobing callouts and prober can access it */
410 	hal_device_store_add (hald_get_tdl (), d);
411 
412 	return (d);
413 }
414 
415 
416 static void
417 get_dev_link_path(di_node_t node, char *nodetype, char *re, char **devlink, char **minor_path, char **minor_name)
418 {
419 	di_devlink_handle_t devlink_hdl;
420 	int	major;
421 	di_minor_t minor;
422 	dev_t	devt;
423 
424 	*devlink = NULL;
425 	*minor_path = NULL;
426 	*minor_name = NULL;
427 
428 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
429 		return;
430 	}
431 
432 	major = di_driver_major(node);
433 	minor = DI_MINOR_NIL;
434 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
435 		devt = di_minor_devt(minor);
436 		if (major != major(devt)) {
437 			continue;
438 		}
439 
440 		if (di_minor_type(minor) != DDM_MINOR) {
441 			continue;
442 		}
443 
444 		if ((*minor_path = di_devfs_minor_path(minor)) == NULL) {
445 			continue;
446 		}
447 
448 		if (strcmp(di_minor_nodetype(minor), nodetype) == 0) {
449 			*devlink = get_devlink(devlink_hdl, re, *minor_path);
450 			/*
451 			 * During hotplugging, devlink could be NULL for hid
452 			 * devices due to devlink database has not yet been
453 			 * updated when hal try to read from it although the
454 			 * actually dev link path has been created. In such a
455 			 * situation, we will read the devlink name from
456 			 * /dev/usb directory.
457 			 */
458 			if ((*devlink == NULL) && (strstr(re, "hid") != NULL)) {
459 				*devlink = get_hid_devlink(*minor_path);
460 			}
461 
462 			if (*devlink != NULL) {
463 				*minor_name = di_minor_name(minor);
464 				break;
465 			}
466 		}
467 
468 		di_devfs_path_free (*minor_path);
469 		*minor_path = NULL;
470 	}
471 	di_devlink_fini (&devlink_hdl);
472 }
473 
474 static HalDevice *
475 devinfo_usb_input_add(HalDevice *usbd, di_node_t node)
476 {
477 	HalDevice *d = NULL;
478 	int	major;
479 	di_minor_t minor;
480 	dev_t	devt;
481 	char	*devlink = NULL;
482 	char	*minor_path = NULL;
483 	char	*minor_name = NULL;
484 	char	udi[HAL_PATH_MAX];
485 
486 	get_dev_link_path(node, "ddi_pseudo",
487 	    "^usb/hid+",  &devlink, &minor_path, &minor_name);
488 
489 	if ((minor_path == NULL) || (devlink == NULL)) {
490 
491 		goto out;
492 	}
493 
494 	HAL_DEBUG(("devlink %s, minor_name %s", devlink, minor_name));
495 	if ((strcmp(minor_name, "keyboard") != 0) &&
496 	    (strcmp(minor_name, "mouse") != 0)) {
497 
498 		goto out;
499 	}
500 
501 	d = hal_device_new();
502 
503 	devinfo_set_default_properties(d, usbd, node, minor_path);
504 	hal_device_property_set_string(d, "info.subsystem", "input");
505 	hal_device_property_set_string(d, "info.category", "input");
506 
507 	hal_device_add_capability(d, "input");
508 
509 	if (strcmp(minor_name, "keyboard") == 0) {
510 		hal_device_add_capability(d, "input.keyboard");
511 		hal_device_add_capability(d, "input.keys");
512 		hal_device_add_capability(d, "button");
513 	} else if (strcmp(minor_name, "mouse") == 0) {
514 		hal_device_add_capability (d, "input.mouse");
515 	}
516 
517 	hal_device_property_set_string(d, "input.device", devlink);
518 	hal_device_property_set_string(d, "input.originating_device",
519 	    hal_device_get_udi(usbd));
520 
521 	hal_util_compute_udi(hald_get_gdl(), udi, sizeof (udi),
522 	    "%s_logicaldev_input", hal_device_get_udi(usbd));
523 
524 	hal_device_set_udi(d, udi);
525 	hal_device_property_set_string(d, "info.udi", udi);
526 
527 	if (strcmp(minor_name, "keyboard") == 0) {
528 		devinfo_add_enqueue(d, minor_path, &devinfo_usb_keyboard_handler);
529 	} else {
530 		devinfo_add_enqueue(d, minor_path, &devinfo_usb_handler);
531 	}
532 
533 	/* add to TDL so preprobing callouts and prober can access it */
534 	hal_device_store_add(hald_get_tdl(), d);
535 
536 out:
537 	if (devlink) {
538 		free(devlink);
539 	}
540 
541 	if (minor_path) {
542 		di_devfs_path_free(minor_path);
543 	}
544 
545 	return (d);
546 }
547 
548 static HalDevice *
549 devinfo_usb_scsa2usb_add(HalDevice *usbd, di_node_t node)
550 {
551 	HalDevice *d = NULL;
552 	di_devlink_handle_t devlink_hdl;
553 	int	major;
554 	di_minor_t minor;
555 	dev_t	devt;
556 	char	*minor_path = NULL;
557 	char	*minor_name = NULL;
558 	char	*devlink = NULL;
559 	char	udi[HAL_PATH_MAX];
560 
561 	get_dev_link_path(node, "ddi_ctl:devctl:scsi", NULL,  &devlink, &minor_path, &minor_name);
562 
563 	if ((devlink == NULL) || (minor_path == NULL)) {
564 		goto out;
565 	}
566 
567 	d = hal_device_new ();
568 
569 	devinfo_set_default_properties (d, usbd, node, minor_path);
570 	hal_device_property_set_string (d, "scsi_host.solaris.device", devlink);
571 	hal_device_property_set_string (d, "info.category", "scsi_host");
572 	hal_device_property_set_int (d, "scsi_host.host", 0);
573 
574 	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
575 	    "%s/scsi_host%d", hal_device_get_udi (usbd),
576 	    hal_device_property_get_int (d, "scsi_host.host"));
577 	hal_device_set_udi (d, udi);
578 	hal_device_property_set_string (d, "info.udi", udi);
579 	hal_device_property_set_string (d, "info.product", "SCSI Host Adapter");
580 
581 	devinfo_add_enqueue (d, minor_path, &devinfo_usb_handler);
582 
583 out:
584 	if (devlink) {
585 		free(devlink);
586 	}
587 	if (minor_path) {
588 		di_devfs_path_free (minor_path);
589 	}
590 
591 	return (d);
592 }
593 
594 static HalDevice *
595 devinfo_usb_printer_add(HalDevice *parent, di_node_t node)
596 {
597 	char *properties[] = { "vendor", "product", "serial", NULL };
598 	int i;
599 	HalDevice *d = NULL;
600 	char	udi[HAL_PATH_MAX];
601 	char *s;
602 	char *devlink = NULL, *minor_path = NULL, *minor_name = NULL;
603 	const char	*subsystem;
604 
605 	get_dev_link_path(node, "ddi_printer", "printers/.+", &devlink, &minor_path, &minor_name);
606 
607 	if ((devlink == NULL) || (minor_path == NULL)) {
608 		goto out;
609 	}
610 
611 	d = hal_device_new ();
612 
613 	devinfo_set_default_properties (d, parent, node, minor_path);
614 	hal_device_property_set_string (d, "info.category", "printer");
615 	hal_device_add_capability (d, "printer");
616 
617 	/* add printer properties */
618 	hal_device_property_set_string (d, "printer.device", devlink);
619 
620 	/* copy parent's selected usb* properties to printer properties */
621 	subsystem = hal_device_property_get_string (parent, "info.subsystem");
622 	for (i = 0; properties[i] != NULL; i++) {
623 		char src[32], dst[32]; /* "subsystem.property" names */
624 
625 		snprintf(src, sizeof (src), "%s.%s", subsystem, properties[i]);
626 		snprintf(dst, sizeof (dst), "printer.%s", properties[i]);
627 		hal_device_copy_property(parent, src, d, dst);
628 	}
629 
630 	devinfo_add_enqueue (d, minor_path, &devinfo_usb_printer_handler);
631 
632 out:
633 	if (devlink) {
634 		free(devlink);
635 	}
636 	if (minor_path) {
637 		di_devfs_path_free (minor_path);
638 	}
639 
640 	return (d);
641 }
642 
643 const gchar *
644 devinfo_printer_prnio_get_prober (HalDevice *d, int *timeout)
645 {
646 	*timeout = 5 * 1000;	/* 5 second timeout */
647 	return ("hald-probe-printer");
648 }
649 
650 const gchar *
651 devinfo_keyboard_get_prober(HalDevice *d, int *timeout)
652 {
653 	*timeout = 5 * 1000;	/* 5 second timeout */
654 	return ("hald-probe-xkb");
655 }
656