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
37static HalDevice *devinfo_usb_if_add(HalDevice *d, di_node_t node, gchar *devfs_path,
38				     gchar *if_devfs_path, int ifnum);
39static HalDevice *devinfo_usb_scsa2usb_add(HalDevice *d, di_node_t node);
40static HalDevice *devinfo_usb_printer_add(HalDevice *usbd, di_node_t node);
41static HalDevice *devinfo_usb_input_add(HalDevice *usbd, di_node_t node);
42static HalDevice *devinfo_usb_video4linux_add(HalDevice *usbd, di_node_t node);
43const gchar *devinfo_printer_prnio_get_prober(HalDevice *d, int *timeout);
44const gchar *devinfo_keyboard_get_prober(HalDevice *d, int *timeout);
45static void set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name);
46
47DevinfoDevHandler devinfo_usb_handler = {
48	devinfo_usb_add,
49	NULL,
50	NULL,
51	NULL,
52	NULL,
53	NULL
54};
55
56DevinfoDevHandler devinfo_usb_printer_handler = {
57	devinfo_usb_add,
58	NULL,
59	NULL,
60	NULL,
61	NULL,
62	devinfo_printer_prnio_get_prober
63};
64
65DevinfoDevHandler devinfo_usb_keyboard_handler = {
66	devinfo_usb_add,
67	NULL,
68	NULL,
69	NULL,
70	NULL,
71	devinfo_keyboard_get_prober
72};
73
74static gboolean
75is_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
95static char *
96get_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
123HalDevice *
124devinfo_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
238out:
239	if (nd != NULL) {
240		return (nd);
241	} else {
242		return (d);
243	}
244}
245
246
247static void
248set_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
306static usb_if_descr_t *
307parse_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
360out:
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
369static HalDevice *
370devinfo_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
423static void
424get_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) && (re != 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
482static HalDevice *
483devinfo_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[0-9]+",  &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
533out:
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
545static HalDevice *
546devinfo_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[0-9]+",  &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
607out:
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
619static HalDevice *
620devinfo_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",
633	    "^usb/mass-storage[0-9]+", &devlink, &minor_path, &minor_name);
634
635	if ((devlink == NULL) || (minor_path == NULL)) {
636		goto out;
637	}
638
639	d = hal_device_new ();
640
641	devinfo_set_default_properties (d, usbd, node, minor_path);
642	hal_device_property_set_string (d, "scsi_host.solaris.device", devlink);
643	hal_device_property_set_string (d, "info.category", "scsi_host");
644	hal_device_property_set_int (d, "scsi_host.host", 0);
645
646	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
647	    "%s/scsi_host%d", hal_device_get_udi (usbd),
648	    hal_device_property_get_int (d, "scsi_host.host"));
649	hal_device_set_udi (d, udi);
650	hal_device_property_set_string (d, "info.udi", udi);
651	hal_device_property_set_string (d, "info.product", "SCSI Host Adapter");
652
653	devinfo_add_enqueue (d, minor_path, &devinfo_usb_handler);
654
655out:
656	if (devlink) {
657		free(devlink);
658	}
659	if (minor_path) {
660		di_devfs_path_free (minor_path);
661	}
662
663	return (d);
664}
665
666static HalDevice *
667devinfo_usb_printer_add(HalDevice *parent, di_node_t node)
668{
669	char *properties[] = { "vendor", "product", "serial", NULL };
670	int i;
671	HalDevice *d = NULL;
672	char	udi[HAL_PATH_MAX];
673	char *s;
674	char *devlink = NULL, *minor_path = NULL, *minor_name = NULL;
675	const char	*subsystem;
676
677	get_dev_link_path(node, "ddi_printer", "printers/.+", &devlink, &minor_path, &minor_name);
678
679	if ((devlink == NULL) || (minor_path == NULL)) {
680		goto out;
681	}
682
683	d = hal_device_new ();
684
685	devinfo_set_default_properties (d, parent, node, minor_path);
686	hal_device_property_set_string (d, "info.category", "printer");
687	hal_device_add_capability (d, "printer");
688
689	/* add printer properties */
690	hal_device_property_set_string (d, "printer.device", devlink);
691
692	/* copy parent's selected usb* properties to printer properties */
693	subsystem = hal_device_property_get_string (parent, "info.subsystem");
694	for (i = 0; properties[i] != NULL; i++) {
695		char src[32], dst[32]; /* "subsystem.property" names */
696
697		snprintf(src, sizeof (src), "%s.%s", subsystem, properties[i]);
698		snprintf(dst, sizeof (dst), "printer.%s", properties[i]);
699		hal_device_copy_property(parent, src, d, dst);
700	}
701
702	devinfo_add_enqueue (d, minor_path, &devinfo_usb_printer_handler);
703
704out:
705	if (devlink) {
706		free(devlink);
707	}
708	if (minor_path) {
709		di_devfs_path_free (minor_path);
710	}
711
712	return (d);
713}
714
715const gchar *
716devinfo_printer_prnio_get_prober (HalDevice *d, int *timeout)
717{
718	*timeout = 5 * 1000;	/* 5 second timeout */
719	return ("hald-probe-printer");
720}
721
722const gchar *
723devinfo_keyboard_get_prober(HalDevice *d, int *timeout)
724{
725	*timeout = 5 * 1000;	/* 5 second timeout */
726	return ("hald-probe-xkb");
727}
728