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