12912Sartem /***************************************************************************
22912Sartem  *
32912Sartem  * devinfo_usb.h : USB devices
42912Sartem  *
58529SNorm.Jacobs@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
62912Sartem  * Use is subject to license terms.
72912Sartem  *
82912Sartem  * Licensed under the Academic Free License version 2.1
92912Sartem  *
102912Sartem  **************************************************************************/
112912Sartem 
123121Sartem #ifdef HAVE_CONFIG_H
133121Sartem #  include <config.h>
143121Sartem #endif
153121Sartem 
162912Sartem #include <stdio.h>
172912Sartem #include <string.h>
182912Sartem #include <libdevinfo.h>
19*9608SLin.Guo@Sun.COM #include <unistd.h>
20*9608SLin.Guo@Sun.COM #include <dirent.h>
212912Sartem #include <sys/types.h>
222912Sartem #include <sys/mkdev.h>
232912Sartem #include <sys/stat.h>
246112Sqz150045 #include <sys/usb/usbai.h>
252912Sartem 
262912Sartem #include "../osspec.h"
272912Sartem #include "../logger.h"
282912Sartem #include "../hald.h"
292912Sartem #include "../hald_dbus.h"
302912Sartem #include "../device_info.h"
312912Sartem #include "../util.h"
322912Sartem #include "../ids.h"
332912Sartem #include "hotplug.h"
342912Sartem #include "devinfo.h"
352912Sartem #include "devinfo_usb.h"
362912Sartem 
376112Sqz150045 static HalDevice *devinfo_usb_if_add(HalDevice *d, di_node_t node, gchar *devfs_path,
386112Sqz150045 				     gchar *if_devfs_path, int ifnum);
396112Sqz150045 static HalDevice *devinfo_usb_scsa2usb_add(HalDevice *d, di_node_t node);
406112Sqz150045 static HalDevice *devinfo_usb_printer_add(HalDevice *usbd, di_node_t node);
41*9608SLin.Guo@Sun.COM static HalDevice *devinfo_usb_input_add(HalDevice *usbd, di_node_t node);
42*9608SLin.Guo@Sun.COM const gchar *devinfo_printer_prnio_get_prober(HalDevice *d, int *timeout);
43*9608SLin.Guo@Sun.COM const gchar *devinfo_keyboard_get_prober(HalDevice *d, int *timeout);
446112Sqz150045 static void set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name);
452912Sartem 
462912Sartem DevinfoDevHandler devinfo_usb_handler = {
476112Sqz150045 	devinfo_usb_add,
482912Sartem 	NULL,
492912Sartem 	NULL,
502912Sartem 	NULL,
512912Sartem 	NULL,
526112Sqz150045 	NULL
532912Sartem };
542912Sartem 
553536Sjacobs DevinfoDevHandler devinfo_usb_printer_handler = {
566112Sqz150045 	devinfo_usb_add,
573536Sjacobs 	NULL,
583536Sjacobs 	NULL,
593536Sjacobs 	NULL,
603536Sjacobs 	NULL,
616112Sqz150045 	devinfo_printer_prnio_get_prober
623536Sjacobs };
633536Sjacobs 
64*9608SLin.Guo@Sun.COM DevinfoDevHandler devinfo_usb_keyboard_handler = {
65*9608SLin.Guo@Sun.COM 	devinfo_usb_add,
66*9608SLin.Guo@Sun.COM 	NULL,
67*9608SLin.Guo@Sun.COM 	NULL,
68*9608SLin.Guo@Sun.COM 	NULL,
69*9608SLin.Guo@Sun.COM 	NULL,
70*9608SLin.Guo@Sun.COM 	devinfo_keyboard_get_prober
71*9608SLin.Guo@Sun.COM };
72*9608SLin.Guo@Sun.COM 
733710Sjacobs static gboolean
743710Sjacobs is_usb_node(di_node_t node)
753710Sjacobs {
763710Sjacobs 	int rc;
773710Sjacobs 	char *s;
783710Sjacobs 
793710Sjacobs 	/*
803710Sjacobs 	 * USB device nodes will have "compatible" propety values that
813710Sjacobs 	 * begins with "usb".
823710Sjacobs 	 */
836112Sqz150045 	rc = di_prop_lookup_strings(DDI_DEV_T_ANY, node, "compatible", &s);
843710Sjacobs 	while (rc-- > 0) {
853710Sjacobs 		if (strncmp(s, "usb", 3) == 0) {
863710Sjacobs 			return (TRUE);
873710Sjacobs 		}
883710Sjacobs 		s += (strlen(s) + 1);
893710Sjacobs 	}
903710Sjacobs 
913710Sjacobs 	return (FALSE);
923710Sjacobs }
933710Sjacobs 
94*9608SLin.Guo@Sun.COM static char *
95*9608SLin.Guo@Sun.COM get_hid_devlink(char *devfs_path)
96*9608SLin.Guo@Sun.COM {
97*9608SLin.Guo@Sun.COM 	char *result = NULL;
98*9608SLin.Guo@Sun.COM 	DIR *dp;
99*9608SLin.Guo@Sun.COM 
100*9608SLin.Guo@Sun.COM 	if ((dp = opendir("/dev/usb")) != NULL) {
101*9608SLin.Guo@Sun.COM 		struct dirent *ep;
102*9608SLin.Guo@Sun.COM 
103*9608SLin.Guo@Sun.COM 		while ((ep = readdir(dp)) != NULL) {
104*9608SLin.Guo@Sun.COM 			char path[MAXPATHLEN], lpath[MAXPATHLEN];
105*9608SLin.Guo@Sun.COM 
106*9608SLin.Guo@Sun.COM 			snprintf(path, sizeof (path), "/dev/usb/%s",
107*9608SLin.Guo@Sun.COM 			    ep->d_name);
108*9608SLin.Guo@Sun.COM 			memset(lpath, 0, sizeof (lpath));
109*9608SLin.Guo@Sun.COM 			if ((readlink(path, lpath, sizeof (lpath)) > 0) &&
110*9608SLin.Guo@Sun.COM 			    (strstr(lpath, devfs_path) != NULL)) {
111*9608SLin.Guo@Sun.COM 				result = strdup(path);
112*9608SLin.Guo@Sun.COM 				break;
113*9608SLin.Guo@Sun.COM 			}
114*9608SLin.Guo@Sun.COM 		}
115*9608SLin.Guo@Sun.COM 		closedir(dp);
116*9608SLin.Guo@Sun.COM 	}
117*9608SLin.Guo@Sun.COM 
118*9608SLin.Guo@Sun.COM 	return (result);
119*9608SLin.Guo@Sun.COM }
120*9608SLin.Guo@Sun.COM 
1212912Sartem HalDevice *
1222912Sartem devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
1232912Sartem {
1242912Sartem 	HalDevice *d, *nd = NULL;
1252912Sartem 	char	*s;
1263710Sjacobs 	int	*i;
1272912Sartem 	char	*driver_name, *binding_name;
1286112Sqz150045 	char	if_devfs_path[HAL_PATH_MAX];
1296112Sqz150045 	di_devlink_handle_t hdl;
1306112Sqz150045 	double	k;
1312912Sartem 
1326112Sqz150045 	if (is_usb_node(node) == FALSE) {
1332912Sartem 		return (NULL);
1342912Sartem 	}
1352912Sartem 
1366112Sqz150045 	driver_name = di_driver_name (node);
1376112Sqz150045 
1386112Sqz150045 	if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface", &i) < 0) {
1396112Sqz150045 		/* It is a USB device node. */
1406112Sqz150045 
1416112Sqz150045 		d = hal_device_new ();
1422912Sartem 
1436112Sqz150045 		devinfo_set_default_properties (d, parent, node, devfs_path);
1446112Sqz150045 		hal_device_property_set_string (d, "info.subsystem", "usb_device");
1456112Sqz150045 		PROP_STR(d, node, s, "usb-product-name", "info.product");
1466112Sqz150045 		PROP_STR(d, node, s, "usb-product-name", "usb_device.product");
1476112Sqz150045 		PROP_STR(d, node, s, "usb-vendor-name", "usb_device.vendor");
1486112Sqz150045 		PROP_INT(d, node, i, "usb-vendor-id", "usb_device.vendor_id");
1496112Sqz150045 		PROP_INT(d, node, i, "usb-product-id", "usb_device.product_id");
1506112Sqz150045 		PROP_INT(d, node, i, "usb-revision-id", "usb_device.device_revision_bcd");
1516112Sqz150045 		PROP_STR(d, node, s, "usb-serialno", "usb_device.serial");
1526112Sqz150045 		PROP_INT(d, node, i, "usb-port-count", "usb_device.num_ports");
1536112Sqz150045 		PROP_INT(d, node, i, "usb-num-configs", "usb_device.num_configurations");
1546112Sqz150045 		PROP_INT(d, node, i, "assigned-address", "usb_device.bus_number");
1556112Sqz150045 
1566112Sqz150045 		if  (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "usb-release", &i) > 0) {
1576112Sqz150045 			k = (double)bcd(*i);
1586112Sqz150045 			hal_device_property_set_double (d, "usb_device.version", k / 100);
1596112Sqz150045 		}
1606112Sqz150045 
1616112Sqz150045 		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "low-speed", &i) >= 0) {
1626112Sqz150045 			k = 1.5;
1636112Sqz150045 		} else if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "high-speed", &i) >= 0) {
1646112Sqz150045 			k = 480.0;
1656112Sqz150045 		} else {
1666112Sqz150045 			/* It is the full speed device. */
1676112Sqz150045 			k = 12.0;
1686112Sqz150045 		}
1696112Sqz150045 		hal_device_property_set_double (d, "usb_device.speed", k);
1706112Sqz150045 
1716112Sqz150045 		set_usb_properties (d, node, devfs_path, driver_name);
1722912Sartem 
1736112Sqz150045 		/* wait for the ugen node's creation */
1746112Sqz150045 		if ((driver_name != NULL) && (strcmp (driver_name, "usb_mid") == 0)) {
1756112Sqz150045 			if (hdl = di_devlink_init (devfs_path, DI_MAKE_LINK)) {
1766112Sqz150045 				di_devlink_fini (&hdl);
1776112Sqz150045 			}
1786112Sqz150045 		}
1796112Sqz150045 
1806112Sqz150045 		devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
1816112Sqz150045 
1826112Sqz150045 		/* add to TDL so preprobing callouts and prober can access it */
1836112Sqz150045 		hal_device_store_add (hald_get_tdl (), d);
1846112Sqz150045 
1856112Sqz150045 		if (((binding_name = di_binding_name (node)) != NULL) &&
1866112Sqz150045 		    (strncmp (binding_name, "usbif,", sizeof ("usbif,") - 1) == 0)) {
1876112Sqz150045 
1886112Sqz150045 			snprintf (if_devfs_path, sizeof (if_devfs_path), "%s:if%d",
189*9608SLin.Guo@Sun.COM 			    devfs_path, 0);
1906112Sqz150045 			if ((nd = devinfo_usb_if_add (d, node, if_devfs_path,
191*9608SLin.Guo@Sun.COM 			    if_devfs_path, 0)) != NULL) {
1926112Sqz150045 				d = nd;
1936112Sqz150045 				nd = NULL;
1946112Sqz150045 				devfs_path = if_devfs_path;
1956112Sqz150045 			}
1962912Sartem 		}
1976112Sqz150045 	} else {
1986112Sqz150045 		/* It is a USB interface node or IA node. */
1996112Sqz150045 		int *j;
2006112Sqz150045 
2016112Sqz150045 		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface-count", &j) > 0) {
2026112Sqz150045 			/*
2036112Sqz150045 			 * The USB IA node properties are not defined in
2046112Sqz150045 			 * HAL spec so far. So IA node udi has "ia" sign
2056112Sqz150045 			 * now, different from the IF node udi with "if".
2066112Sqz150045 			 */
2076112Sqz150045 			snprintf (if_devfs_path, sizeof (if_devfs_path),
2086112Sqz150045 			    "%s:ia%d", devfs_path, *i);
2096112Sqz150045 		} else {
2106112Sqz150045 			snprintf (if_devfs_path, sizeof (if_devfs_path),
2116112Sqz150045 			    "%s:if%d", devfs_path, *i);
2126112Sqz150045 		}
2136112Sqz150045 
2146112Sqz150045 		d = devinfo_usb_if_add (parent, node, devfs_path, if_devfs_path, *i);
2152912Sartem 	}
2162912Sartem 
2172912Sartem 	/* driver specific */
218*9608SLin.Guo@Sun.COM 	if (driver_name != NULL) {
219*9608SLin.Guo@Sun.COM 		if (strcmp (driver_name, "scsa2usb") == 0) {
220*9608SLin.Guo@Sun.COM 			nd = devinfo_usb_scsa2usb_add (d, node);
221*9608SLin.Guo@Sun.COM 		} else if (strcmp (driver_name, "usbprn") == 0) {
222*9608SLin.Guo@Sun.COM 			nd = devinfo_usb_printer_add (d, node);
223*9608SLin.Guo@Sun.COM 		} else if (strcmp(driver_name, "hid") == 0) {
224*9608SLin.Guo@Sun.COM 			if (hdl = di_devlink_init(devfs_path, DI_MAKE_LINK)) {
225*9608SLin.Guo@Sun.COM 				di_devlink_fini(&hdl);
226*9608SLin.Guo@Sun.COM 			}
227*9608SLin.Guo@Sun.COM 			nd = devinfo_usb_input_add(d, node);
228*9608SLin.Guo@Sun.COM 		}
2292912Sartem 	}
2302912Sartem 
2312912Sartem out:
2322912Sartem 	if (nd != NULL) {
2332912Sartem 		return (nd);
2342912Sartem 	} else {
2352912Sartem 		return (d);
2362912Sartem 	}
2372912Sartem }
2382912Sartem 
2396112Sqz150045 
2406112Sqz150045 static void
2416112Sqz150045 set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name)
2426112Sqz150045 {
2436112Sqz150045 	usb_dev_descr_t	*dev_descrp = NULL;	/* device descriptor */
2446112Sqz150045 	usb_cfg_descr_t	*cfg_descrp = NULL;	/* configuration descriptor */
2456112Sqz150045 	unsigned char	*rdata = NULL;
2466112Sqz150045 	char *p;
2476112Sqz150045 	int i = 0;
2486112Sqz150045 
2496112Sqz150045 	hal_device_property_set_int (d, "usb_device.port_number",
2506112Sqz150045 	    atoi (devfs_path + strlen (devfs_path) -1));
2516112Sqz150045 
2526112Sqz150045 	if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-dev-descriptor",
2536112Sqz150045 	    &rdata) > 0) {
2546112Sqz150045 		dev_descrp = (usb_dev_descr_t *)rdata;
2556112Sqz150045 
2566112Sqz150045 		if (dev_descrp != NULL) {
2576112Sqz150045 			hal_device_property_set_int (d, "usb_device.device_class",
2586112Sqz150045 			    dev_descrp->bDeviceClass);
2596112Sqz150045 			hal_device_property_set_int (d, "usb_device.device_subclass",
2606112Sqz150045 			    dev_descrp->bDeviceSubClass);
2616112Sqz150045 			hal_device_property_set_int (d, "usb_device.device_protocol",
2626112Sqz150045 			    dev_descrp->bDeviceProtocol);
2636112Sqz150045 		}
2646112Sqz150045 	}
2656112Sqz150045 
2666112Sqz150045 	if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-raw-cfg-descriptors",
2676112Sqz150045 	    &rdata) > 0) {
2686112Sqz150045 		cfg_descrp = (usb_cfg_descr_t *)(rdata);
2696112Sqz150045 
2706112Sqz150045 		if (cfg_descrp != NULL) {
2716112Sqz150045 			hal_device_property_set_int (d, "usb_device.configuration_value",
2726112Sqz150045 			    cfg_descrp->bConfigurationValue);
2736112Sqz150045 			hal_device_property_set_int (d, "usb_device.max_power",
2746112Sqz150045 			    cfg_descrp->bMaxPower);
2756112Sqz150045 			hal_device_property_set_int (d, "usb_device.num_interfaces",
2766112Sqz150045 			    cfg_descrp->bNumInterfaces);
2776112Sqz150045 			hal_device_property_set_bool (d, "usb_device.can_wake_up",
2786112Sqz150045 			    (cfg_descrp->bmAttributes & 0x20) ? TRUE : FALSE);
2796112Sqz150045 			hal_device_property_set_bool (d, "usb_device.is_self_powered",
2806112Sqz150045 			    (cfg_descrp->bmAttributes & 0x40) ? TRUE : FALSE);
2816112Sqz150045 		}
2826112Sqz150045 	}
2836112Sqz150045 
2846112Sqz150045 	/* get the node's usb tree level by counting hub numbers */
2856112Sqz150045 	do {
2866112Sqz150045 		if (p = strstr (devfs_path, "/hub@")) {
2876112Sqz150045 			devfs_path = p + strlen ("/hub@");
2886112Sqz150045 			i ++;
2896112Sqz150045 		}
2906112Sqz150045 	} while (p != NULL);
2916112Sqz150045 
2926112Sqz150045 	if ((driver_name != NULL) && (strcmp (driver_name, "hubd") == 0) && (i > 0))
2936112Sqz150045 		i --;
2946112Sqz150045 
2956112Sqz150045 	hal_device_property_set_int (d, "usb_device.level_number", i);
2966112Sqz150045 }
2976112Sqz150045 
2986112Sqz150045 
2996112Sqz150045 static usb_if_descr_t *
3006112Sqz150045 parse_usb_if_descr(di_node_t node, int ifnum)
3012912Sartem {
3026112Sqz150045 	unsigned char	*rdata = NULL;
3036112Sqz150045 	usb_if_descr_t	*if_descrp=NULL;	/* interface descriptor */
304*9608SLin.Guo@Sun.COM 	di_node_t	tmp_node = DI_NODE_NIL;
3056112Sqz150045 	uint8_t num, length, type;
3066112Sqz150045 	int rlen;
3076112Sqz150045 	gchar *devpath = NULL;
3086112Sqz150045 
3096112Sqz150045 	if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, node,
3106112Sqz150045 	     "usb-raw-cfg-descriptors", &rdata)) < 0) {
3116112Sqz150045 
3126112Sqz150045 		char *p;
3136112Sqz150045 		int i;
3146112Sqz150045 
3156112Sqz150045 		if ((devpath = di_devfs_path (node)) == NULL)
3166112Sqz150045 			goto out;
3176112Sqz150045 
3186112Sqz150045 		/* Look up its parent that may be a USB IA or USB mid. */
3196112Sqz150045 		for (i = 0; i < 2; i++) {
3206112Sqz150045 			p = strrchr (devpath, '/');
3216112Sqz150045 			if (p == NULL)
3226112Sqz150045 				goto out;
3236112Sqz150045 			*p = '\0';
3246112Sqz150045 
3256112Sqz150045 			if ((tmp_node = di_init (devpath, DINFOCPYALL)) == DI_NODE_NIL)
3266112Sqz150045 				goto out;
3276112Sqz150045 
3286112Sqz150045 			if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, tmp_node,
3296112Sqz150045 			     "usb-raw-cfg-descriptors", &rdata)) > 0)
3306112Sqz150045 				break;
3316112Sqz150045 
3326112Sqz150045 			di_fini (tmp_node);
3336112Sqz150045 		}
3346112Sqz150045 	}
3352912Sartem 
3366112Sqz150045 	if (rdata == NULL)
3376112Sqz150045 		goto out;
3386112Sqz150045 
3396112Sqz150045 	do {
3406112Sqz150045 		length = (uint8_t)*rdata;
3416112Sqz150045 		type = (uint8_t)*(rdata + 1);
3426112Sqz150045 		if (type == USB_DESCR_TYPE_IF) {
3436112Sqz150045 			num = (uint8_t)*(rdata + 2);
3446112Sqz150045 			if (num == ifnum) {
3456112Sqz150045 				if_descrp = (usb_if_descr_t *)rdata;
3466112Sqz150045 				break;
3476112Sqz150045 			}
3486112Sqz150045 		}
3496112Sqz150045 		rdata += length;
3506112Sqz150045 		rlen -= length;
3516112Sqz150045 	} while ((length > 0 ) && (rlen > 0));
3526112Sqz150045 
3536112Sqz150045 out:
3546112Sqz150045 	if (devpath != NULL)
3556112Sqz150045 		di_devfs_path_free (devpath);
3566112Sqz150045 	if (tmp_node != DI_NODE_NIL)
3576112Sqz150045 		di_fini (tmp_node);
3586112Sqz150045 	return (if_descrp);
3596112Sqz150045 }
3606112Sqz150045 
3616112Sqz150045 
3626112Sqz150045 static HalDevice *
3636112Sqz150045 devinfo_usb_if_add(HalDevice *parent, di_node_t node, gchar *devfs_path,
3646112Sqz150045 		   gchar *if_devfs_path, int ifnum)
3656112Sqz150045 {
3666112Sqz150045 	HalDevice	*d = NULL;
3676112Sqz150045 	char		udi[HAL_PATH_MAX];
3686112Sqz150045 	const char	*parent_info;
3696112Sqz150045 	usb_if_descr_t	*if_descrp=NULL;	/* interface descriptor */
3702912Sartem 
3712912Sartem 	d = hal_device_new ();
3722912Sartem 
3736112Sqz150045 	devinfo_set_default_properties (d, parent, node, if_devfs_path);
3746112Sqz150045 
3756112Sqz150045 	/* Set the existed physical device path. */
3766112Sqz150045 	hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
3776112Sqz150045 	hal_device_property_set_string (d, "info.subsystem", "usb");
3786112Sqz150045 	hal_device_property_set_string (d, "info.product", "USB Device Interface");
3796112Sqz150045 
3806112Sqz150045 	/* Set usb interface properties to interface node. */
3816112Sqz150045 	if (strstr (if_devfs_path, ":ia") == NULL) {
3826112Sqz150045 		if_descrp = parse_usb_if_descr (node, ifnum);
3836112Sqz150045 
3846112Sqz150045 		if (if_descrp != NULL) {
3856112Sqz150045 			hal_device_property_set_int (d, "usb.interface.class",
3866112Sqz150045 			    if_descrp->bInterfaceClass);
3876112Sqz150045 			hal_device_property_set_int (d, "usb.interface.subclass",
3886112Sqz150045 			    if_descrp->bInterfaceSubClass);
3896112Sqz150045 			hal_device_property_set_int (d, "usb.interface.protocol",
3906112Sqz150045 			    if_descrp->bInterfaceProtocol);
3916112Sqz150045 			hal_device_property_set_int (d, "usb.interface.number",
3926112Sqz150045 			    if_descrp->bInterfaceNumber);
3936112Sqz150045 		}
3946112Sqz150045 	}
3952912Sartem 
3962912Sartem 	/* copy parent's usb_device.* properties */
3976112Sqz150045 	parent_info = hal_device_property_get_string (parent, "info.subsystem");
3986112Sqz150045 	if (parent_info != NULL) {
3996112Sqz150045 		if (strcmp (parent_info, "usb_device") == 0) {
4006112Sqz150045 			hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device.");
4016112Sqz150045 		} else if (strcmp (parent_info, "usb") == 0) {
4026112Sqz150045 			/* for the case that the parent is IA node */
4036112Sqz150045 			hal_device_merge_with_rewrite (d, parent, "usb.", "usb.");
4046112Sqz150045 		}
4056112Sqz150045 	}
4066112Sqz150045 
4076112Sqz150045 	devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
4086112Sqz150045 
4096112Sqz150045 	/* add to TDL so preprobing callouts and prober can access it */
4106112Sqz150045 	hal_device_store_add (hald_get_tdl (), d);
4112912Sartem 
4122912Sartem 	return (d);
4132912Sartem }
4142912Sartem 
4152912Sartem 
4163536Sjacobs static void
417*9608SLin.Guo@Sun.COM get_dev_link_path(di_node_t node, char *nodetype, char *re, char **devlink, char **minor_path, char **minor_name)
4183536Sjacobs {
4193536Sjacobs 	di_devlink_handle_t devlink_hdl;
4206112Sqz150045 	int	major;
4216112Sqz150045 	di_minor_t minor;
4226112Sqz150045 	dev_t	devt;
4232912Sartem 
4243536Sjacobs 	*devlink = NULL;
4256112Sqz150045 	*minor_path = NULL;
426*9608SLin.Guo@Sun.COM 	*minor_name = NULL;
4273536Sjacobs 
4286112Sqz150045 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
4296112Sqz150045 		return;
4306112Sqz150045 	}
4312912Sartem 
4326112Sqz150045 	major = di_driver_major(node);
4336112Sqz150045 	minor = DI_MINOR_NIL;
4346112Sqz150045 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
4356112Sqz150045 		devt = di_minor_devt(minor);
4366112Sqz150045 		if (major != major(devt)) {
4376112Sqz150045 			continue;
4386112Sqz150045 		}
4393536Sjacobs 
4406112Sqz150045 		if (di_minor_type(minor) != DDM_MINOR) {
4416112Sqz150045 			continue;
4426112Sqz150045 		}
4432912Sartem 
4446112Sqz150045 		if ((*minor_path = di_devfs_minor_path(minor)) == NULL) {
4456112Sqz150045 			continue;
4466112Sqz150045 		}
4472912Sartem 
448*9608SLin.Guo@Sun.COM 		if (strcmp(di_minor_nodetype(minor), nodetype) == 0) {
449*9608SLin.Guo@Sun.COM 			*devlink = get_devlink(devlink_hdl, re, *minor_path);
450*9608SLin.Guo@Sun.COM 			/*
451*9608SLin.Guo@Sun.COM 			 * During hotplugging, devlink could be NULL for hid
452*9608SLin.Guo@Sun.COM 			 * devices due to devlink database has not yet been
453*9608SLin.Guo@Sun.COM 			 * updated when hal try to read from it although the
454*9608SLin.Guo@Sun.COM 			 * actually dev link path has been created. In such a
455*9608SLin.Guo@Sun.COM 			 * situation, we will read the devlink name from
456*9608SLin.Guo@Sun.COM 			 * /dev/usb directory.
457*9608SLin.Guo@Sun.COM 			 */
458*9608SLin.Guo@Sun.COM 			if ((*devlink == NULL) && (strstr(re, "hid") != NULL)) {
459*9608SLin.Guo@Sun.COM 				*devlink = get_hid_devlink(*minor_path);
460*9608SLin.Guo@Sun.COM 			}
461*9608SLin.Guo@Sun.COM 
462*9608SLin.Guo@Sun.COM 			if (*devlink != NULL) {
463*9608SLin.Guo@Sun.COM 				*minor_name = di_minor_name(minor);
464*9608SLin.Guo@Sun.COM 				break;
465*9608SLin.Guo@Sun.COM 			}
4663536Sjacobs 		}
467*9608SLin.Guo@Sun.COM 
4683536Sjacobs 		di_devfs_path_free (*minor_path);
4693710Sjacobs 		*minor_path = NULL;
4703536Sjacobs 	}
4713536Sjacobs 	di_devlink_fini (&devlink_hdl);
4722912Sartem }
4732912Sartem 
4742912Sartem static HalDevice *
475*9608SLin.Guo@Sun.COM devinfo_usb_input_add(HalDevice *usbd, di_node_t node)
476*9608SLin.Guo@Sun.COM {
477*9608SLin.Guo@Sun.COM 	HalDevice *d = NULL;
478*9608SLin.Guo@Sun.COM 	int	major;
479*9608SLin.Guo@Sun.COM 	di_minor_t minor;
480*9608SLin.Guo@Sun.COM 	dev_t	devt;
481*9608SLin.Guo@Sun.COM 	char	*devlink = NULL;
482*9608SLin.Guo@Sun.COM 	char	*minor_path = NULL;
483*9608SLin.Guo@Sun.COM 	char	*minor_name = NULL;
484*9608SLin.Guo@Sun.COM 	char	udi[HAL_PATH_MAX];
485*9608SLin.Guo@Sun.COM 
486*9608SLin.Guo@Sun.COM 	get_dev_link_path(node, "ddi_pseudo",
487*9608SLin.Guo@Sun.COM 	    "^usb/hid+",  &devlink, &minor_path, &minor_name);
488*9608SLin.Guo@Sun.COM 
489*9608SLin.Guo@Sun.COM 	if ((minor_path == NULL) || (devlink == NULL)) {
490*9608SLin.Guo@Sun.COM 
491*9608SLin.Guo@Sun.COM 		goto out;
492*9608SLin.Guo@Sun.COM 	}
493*9608SLin.Guo@Sun.COM 
494*9608SLin.Guo@Sun.COM 	HAL_DEBUG(("devlink %s, minor_name %s", devlink, minor_name));
495*9608SLin.Guo@Sun.COM 	if ((strcmp(minor_name, "keyboard") != 0) &&
496*9608SLin.Guo@Sun.COM 	    (strcmp(minor_name, "mouse") != 0)) {
497*9608SLin.Guo@Sun.COM 
498*9608SLin.Guo@Sun.COM 		goto out;
499*9608SLin.Guo@Sun.COM 	}
500*9608SLin.Guo@Sun.COM 
501*9608SLin.Guo@Sun.COM 	d = hal_device_new();
502*9608SLin.Guo@Sun.COM 
503*9608SLin.Guo@Sun.COM 	devinfo_set_default_properties(d, usbd, node, minor_path);
504*9608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "info.subsystem", "input");
505*9608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "info.category", "input");
506*9608SLin.Guo@Sun.COM 
507*9608SLin.Guo@Sun.COM 	hal_device_add_capability(d, "input");
508*9608SLin.Guo@Sun.COM 
509*9608SLin.Guo@Sun.COM 	if (strcmp(minor_name, "keyboard") == 0) {
510*9608SLin.Guo@Sun.COM 		hal_device_add_capability(d, "input.keyboard");
511*9608SLin.Guo@Sun.COM 		hal_device_add_capability(d, "input.keys");
512*9608SLin.Guo@Sun.COM 		hal_device_add_capability(d, "button");
513*9608SLin.Guo@Sun.COM 	} else if (strcmp(minor_name, "mouse") == 0) {
514*9608SLin.Guo@Sun.COM 		hal_device_add_capability (d, "input.mouse");
515*9608SLin.Guo@Sun.COM 	}
516*9608SLin.Guo@Sun.COM 
517*9608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "input.device", devlink);
518*9608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "input.originating_device",
519*9608SLin.Guo@Sun.COM 	    hal_device_get_udi(usbd));
520*9608SLin.Guo@Sun.COM 
521*9608SLin.Guo@Sun.COM 	hal_util_compute_udi(hald_get_gdl(), udi, sizeof (udi),
522*9608SLin.Guo@Sun.COM 	    "%s_logicaldev_input", hal_device_get_udi(usbd));
523*9608SLin.Guo@Sun.COM 
524*9608SLin.Guo@Sun.COM 	hal_device_set_udi(d, udi);
525*9608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "info.udi", udi);
526*9608SLin.Guo@Sun.COM 
527*9608SLin.Guo@Sun.COM 	if (strcmp(minor_name, "keyboard") == 0) {
528*9608SLin.Guo@Sun.COM 		devinfo_add_enqueue(d, minor_path, &devinfo_usb_keyboard_handler);
529*9608SLin.Guo@Sun.COM 	} else {
530*9608SLin.Guo@Sun.COM 		devinfo_add_enqueue(d, minor_path, &devinfo_usb_handler);
531*9608SLin.Guo@Sun.COM 	}
532*9608SLin.Guo@Sun.COM 
533*9608SLin.Guo@Sun.COM 	/* add to TDL so preprobing callouts and prober can access it */
534*9608SLin.Guo@Sun.COM 	hal_device_store_add(hald_get_tdl(), d);
535*9608SLin.Guo@Sun.COM 
536*9608SLin.Guo@Sun.COM out:
537*9608SLin.Guo@Sun.COM 	if (devlink) {
538*9608SLin.Guo@Sun.COM 		free(devlink);
539*9608SLin.Guo@Sun.COM 	}
540*9608SLin.Guo@Sun.COM 
541*9608SLin.Guo@Sun.COM 	if (minor_path) {
542*9608SLin.Guo@Sun.COM 		di_devfs_path_free(minor_path);
543*9608SLin.Guo@Sun.COM 	}
544*9608SLin.Guo@Sun.COM 
545*9608SLin.Guo@Sun.COM 	return (d);
546*9608SLin.Guo@Sun.COM }
547*9608SLin.Guo@Sun.COM 
548*9608SLin.Guo@Sun.COM static HalDevice *
5496112Sqz150045 devinfo_usb_scsa2usb_add(HalDevice *usbd, di_node_t node)
5502912Sartem {
5512912Sartem 	HalDevice *d = NULL;
5522912Sartem 	di_devlink_handle_t devlink_hdl;
5536112Sqz150045 	int	major;
5546112Sqz150045 	di_minor_t minor;
5556112Sqz150045 	dev_t	devt;
5566112Sqz150045 	char	*minor_path = NULL;
557*9608SLin.Guo@Sun.COM 	char	*minor_name = NULL;
5582912Sartem 	char	*devlink = NULL;
5596112Sqz150045 	char	udi[HAL_PATH_MAX];
5602912Sartem 
561*9608SLin.Guo@Sun.COM 	get_dev_link_path(node, "ddi_ctl:devctl:scsi", NULL,  &devlink, &minor_path, &minor_name);
5622912Sartem 
5633536Sjacobs 	if ((devlink == NULL) || (minor_path == NULL)) {
5642912Sartem 		goto out;
5652912Sartem 	}
5662912Sartem 
5672912Sartem 	d = hal_device_new ();
5682912Sartem 
5692912Sartem 	devinfo_set_default_properties (d, usbd, node, minor_path);
5706112Sqz150045 	hal_device_property_set_string (d, "scsi_host.solaris.device", devlink);
5716112Sqz150045 	hal_device_property_set_string (d, "info.category", "scsi_host");
5726112Sqz150045 	hal_device_property_set_int (d, "scsi_host.host", 0);
5732912Sartem 
5746112Sqz150045 	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
5756112Sqz150045 	    "%s/scsi_host%d", hal_device_get_udi (usbd),
5766112Sqz150045 	    hal_device_property_get_int (d, "scsi_host.host"));
5776112Sqz150045 	hal_device_set_udi (d, udi);
5786112Sqz150045 	hal_device_property_set_string (d, "info.udi", udi);
5796112Sqz150045 	hal_device_property_set_string (d, "info.product", "SCSI Host Adapter");
5802912Sartem 
5812912Sartem 	devinfo_add_enqueue (d, minor_path, &devinfo_usb_handler);
5822912Sartem 
5832912Sartem out:
5842912Sartem 	if (devlink) {
5852912Sartem 		free(devlink);
5862912Sartem 	}
5872912Sartem 	if (minor_path) {
5882912Sartem 		di_devfs_path_free (minor_path);
5892912Sartem 	}
5902912Sartem 
5912912Sartem 	return (d);
5922912Sartem }
5932912Sartem 
5943536Sjacobs static HalDevice *
5956112Sqz150045 devinfo_usb_printer_add(HalDevice *parent, di_node_t node)
5963536Sjacobs {
5978529SNorm.Jacobs@Sun.COM 	char *properties[] = { "vendor", "product", "serial", NULL };
5988529SNorm.Jacobs@Sun.COM 	int i;
5993536Sjacobs 	HalDevice *d = NULL;
6006112Sqz150045 	char	udi[HAL_PATH_MAX];
6013536Sjacobs 	char *s;
602*9608SLin.Guo@Sun.COM 	char *devlink = NULL, *minor_path = NULL, *minor_name = NULL;
6038529SNorm.Jacobs@Sun.COM 	const char	*subsystem;
6043536Sjacobs 
605*9608SLin.Guo@Sun.COM 	get_dev_link_path(node, "ddi_printer", "printers/.+", &devlink, &minor_path, &minor_name);
6063536Sjacobs 
6073536Sjacobs 	if ((devlink == NULL) || (minor_path == NULL)) {
6083536Sjacobs 		goto out;
6093536Sjacobs 	}
6103536Sjacobs 
6113536Sjacobs 	d = hal_device_new ();
6123536Sjacobs 
6133536Sjacobs 	devinfo_set_default_properties (d, parent, node, minor_path);
6146112Sqz150045 	hal_device_property_set_string (d, "info.category", "printer");
6153536Sjacobs 	hal_device_add_capability (d, "printer");
6163536Sjacobs 
6173536Sjacobs 	/* add printer properties */
6186112Sqz150045 	hal_device_property_set_string (d, "printer.device", devlink);
6198529SNorm.Jacobs@Sun.COM 
6208529SNorm.Jacobs@Sun.COM 	/* copy parent's selected usb* properties to printer properties */
6218529SNorm.Jacobs@Sun.COM 	subsystem = hal_device_property_get_string (parent, "info.subsystem");
6228529SNorm.Jacobs@Sun.COM 	for (i = 0; properties[i] != NULL; i++) {
6238529SNorm.Jacobs@Sun.COM 		char src[32], dst[32]; /* "subsystem.property" names */
6248529SNorm.Jacobs@Sun.COM 
6258529SNorm.Jacobs@Sun.COM 		snprintf(src, sizeof (src), "%s.%s", subsystem, properties[i]);
6268529SNorm.Jacobs@Sun.COM 		snprintf(dst, sizeof (dst), "printer.%s", properties[i]);
6278529SNorm.Jacobs@Sun.COM 		hal_device_copy_property(parent, src, d, dst);
6288529SNorm.Jacobs@Sun.COM 	}
6293536Sjacobs 
6303536Sjacobs 	devinfo_add_enqueue (d, minor_path, &devinfo_usb_printer_handler);
6313536Sjacobs 
6323536Sjacobs out:
6333536Sjacobs 	if (devlink) {
6343536Sjacobs 		free(devlink);
6353536Sjacobs 	}
6363536Sjacobs 	if (minor_path) {
6373536Sjacobs 		di_devfs_path_free (minor_path);
6383536Sjacobs 	}
6393536Sjacobs 
6403536Sjacobs 	return (d);
6413536Sjacobs }
6423536Sjacobs 
6433536Sjacobs const gchar *
6443536Sjacobs devinfo_printer_prnio_get_prober (HalDevice *d, int *timeout)
6453536Sjacobs {
6463536Sjacobs 	*timeout = 5 * 1000;	/* 5 second timeout */
6473536Sjacobs 	return ("hald-probe-printer");
6483536Sjacobs }
649*9608SLin.Guo@Sun.COM 
650*9608SLin.Guo@Sun.COM const gchar *
651*9608SLin.Guo@Sun.COM devinfo_keyboard_get_prober(HalDevice *d, int *timeout)
652*9608SLin.Guo@Sun.COM {
653*9608SLin.Guo@Sun.COM 	*timeout = 5 * 1000;	/* 5 second timeout */
654*9608SLin.Guo@Sun.COM 	return ("hald-probe-xkb");
655*9608SLin.Guo@Sun.COM }
656