xref: /onnv-gate/usr/src/cmd/hal/hald/solaris/devinfo_usb.c (revision 10975:9dd13a7cd2e3)
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>
199608SLin.Guo@Sun.COM #include <unistd.h>
209608SLin.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);
419608SLin.Guo@Sun.COM static HalDevice *devinfo_usb_input_add(HalDevice *usbd, di_node_t node);
429794SLin.Guo@Sun.COM static HalDevice *devinfo_usb_video4linux_add(HalDevice *usbd, di_node_t node);
439608SLin.Guo@Sun.COM const gchar *devinfo_printer_prnio_get_prober(HalDevice *d, int *timeout);
449608SLin.Guo@Sun.COM const gchar *devinfo_keyboard_get_prober(HalDevice *d, int *timeout);
456112Sqz150045 static void set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name);
462912Sartem 
472912Sartem DevinfoDevHandler devinfo_usb_handler = {
486112Sqz150045 	devinfo_usb_add,
492912Sartem 	NULL,
502912Sartem 	NULL,
512912Sartem 	NULL,
522912Sartem 	NULL,
536112Sqz150045 	NULL
542912Sartem };
552912Sartem 
563536Sjacobs DevinfoDevHandler devinfo_usb_printer_handler = {
576112Sqz150045 	devinfo_usb_add,
583536Sjacobs 	NULL,
593536Sjacobs 	NULL,
603536Sjacobs 	NULL,
613536Sjacobs 	NULL,
626112Sqz150045 	devinfo_printer_prnio_get_prober
633536Sjacobs };
643536Sjacobs 
659608SLin.Guo@Sun.COM DevinfoDevHandler devinfo_usb_keyboard_handler = {
669608SLin.Guo@Sun.COM 	devinfo_usb_add,
679608SLin.Guo@Sun.COM 	NULL,
689608SLin.Guo@Sun.COM 	NULL,
699608SLin.Guo@Sun.COM 	NULL,
709608SLin.Guo@Sun.COM 	NULL,
719608SLin.Guo@Sun.COM 	devinfo_keyboard_get_prober
729608SLin.Guo@Sun.COM };
739608SLin.Guo@Sun.COM 
743710Sjacobs static gboolean
is_usb_node(di_node_t node)753710Sjacobs is_usb_node(di_node_t node)
763710Sjacobs {
773710Sjacobs 	int rc;
783710Sjacobs 	char *s;
793710Sjacobs 
803710Sjacobs 	/*
813710Sjacobs 	 * USB device nodes will have "compatible" propety values that
823710Sjacobs 	 * begins with "usb".
833710Sjacobs 	 */
846112Sqz150045 	rc = di_prop_lookup_strings(DDI_DEV_T_ANY, node, "compatible", &s);
853710Sjacobs 	while (rc-- > 0) {
863710Sjacobs 		if (strncmp(s, "usb", 3) == 0) {
873710Sjacobs 			return (TRUE);
883710Sjacobs 		}
893710Sjacobs 		s += (strlen(s) + 1);
903710Sjacobs 	}
913710Sjacobs 
923710Sjacobs 	return (FALSE);
933710Sjacobs }
943710Sjacobs 
959608SLin.Guo@Sun.COM static char *
get_usb_devlink(char * devfs_path,const char * dir_name)969794SLin.Guo@Sun.COM get_usb_devlink(char *devfs_path, const char *dir_name)
979608SLin.Guo@Sun.COM {
989608SLin.Guo@Sun.COM 	char *result = NULL;
999608SLin.Guo@Sun.COM 	DIR *dp;
1009608SLin.Guo@Sun.COM 
1019794SLin.Guo@Sun.COM 	if ((dp = opendir(dir_name)) != NULL) {
1029608SLin.Guo@Sun.COM 		struct dirent *ep;
1039608SLin.Guo@Sun.COM 
1049608SLin.Guo@Sun.COM 		while ((ep = readdir(dp)) != NULL) {
1059608SLin.Guo@Sun.COM 			char path[MAXPATHLEN], lpath[MAXPATHLEN];
1069608SLin.Guo@Sun.COM 
1079794SLin.Guo@Sun.COM 			strncpy(path, dir_name, strlen(dir_name));
1089794SLin.Guo@Sun.COM 			strncat(path, ep->d_name, strlen(ep->d_name));
1099608SLin.Guo@Sun.COM 			memset(lpath, 0, sizeof (lpath));
1109608SLin.Guo@Sun.COM 			if ((readlink(path, lpath, sizeof (lpath)) > 0) &&
1119608SLin.Guo@Sun.COM 			    (strstr(lpath, devfs_path) != NULL)) {
1129608SLin.Guo@Sun.COM 				result = strdup(path);
1139608SLin.Guo@Sun.COM 				break;
1149608SLin.Guo@Sun.COM 			}
1159794SLin.Guo@Sun.COM 			memset(path, 0, sizeof (path));
1169608SLin.Guo@Sun.COM 		}
1179608SLin.Guo@Sun.COM 		closedir(dp);
1189608SLin.Guo@Sun.COM 	}
1199608SLin.Guo@Sun.COM 
1209608SLin.Guo@Sun.COM 	return (result);
1219608SLin.Guo@Sun.COM }
1229608SLin.Guo@Sun.COM 
1232912Sartem HalDevice *
devinfo_usb_add(HalDevice * parent,di_node_t node,char * devfs_path,char * device_type)1242912Sartem devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
1252912Sartem {
1262912Sartem 	HalDevice *d, *nd = NULL;
1272912Sartem 	char	*s;
1283710Sjacobs 	int	*i;
1292912Sartem 	char	*driver_name, *binding_name;
1306112Sqz150045 	char	if_devfs_path[HAL_PATH_MAX];
1316112Sqz150045 	di_devlink_handle_t hdl;
1326112Sqz150045 	double	k;
1332912Sartem 
1346112Sqz150045 	if (is_usb_node(node) == FALSE) {
1352912Sartem 		return (NULL);
1362912Sartem 	}
1372912Sartem 
1386112Sqz150045 	driver_name = di_driver_name (node);
1396112Sqz150045 
1406112Sqz150045 	if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface", &i) < 0) {
1416112Sqz150045 		/* It is a USB device node. */
1426112Sqz150045 
1436112Sqz150045 		d = hal_device_new ();
1442912Sartem 
1456112Sqz150045 		devinfo_set_default_properties (d, parent, node, devfs_path);
1466112Sqz150045 		hal_device_property_set_string (d, "info.subsystem", "usb_device");
1476112Sqz150045 		PROP_STR(d, node, s, "usb-product-name", "info.product");
1486112Sqz150045 		PROP_STR(d, node, s, "usb-product-name", "usb_device.product");
1496112Sqz150045 		PROP_STR(d, node, s, "usb-vendor-name", "usb_device.vendor");
1506112Sqz150045 		PROP_INT(d, node, i, "usb-vendor-id", "usb_device.vendor_id");
1516112Sqz150045 		PROP_INT(d, node, i, "usb-product-id", "usb_device.product_id");
1526112Sqz150045 		PROP_INT(d, node, i, "usb-revision-id", "usb_device.device_revision_bcd");
1536112Sqz150045 		PROP_STR(d, node, s, "usb-serialno", "usb_device.serial");
1546112Sqz150045 		PROP_INT(d, node, i, "usb-port-count", "usb_device.num_ports");
1556112Sqz150045 		PROP_INT(d, node, i, "usb-num-configs", "usb_device.num_configurations");
1566112Sqz150045 		PROP_INT(d, node, i, "assigned-address", "usb_device.bus_number");
1576112Sqz150045 
1586112Sqz150045 		if  (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "usb-release", &i) > 0) {
1596112Sqz150045 			k = (double)bcd(*i);
1606112Sqz150045 			hal_device_property_set_double (d, "usb_device.version", k / 100);
1616112Sqz150045 		}
1626112Sqz150045 
1636112Sqz150045 		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "low-speed", &i) >= 0) {
1646112Sqz150045 			k = 1.5;
1656112Sqz150045 		} else if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "high-speed", &i) >= 0) {
1666112Sqz150045 			k = 480.0;
1676112Sqz150045 		} else {
1686112Sqz150045 			/* It is the full speed device. */
1696112Sqz150045 			k = 12.0;
1706112Sqz150045 		}
1716112Sqz150045 		hal_device_property_set_double (d, "usb_device.speed", k);
1726112Sqz150045 
1736112Sqz150045 		set_usb_properties (d, node, devfs_path, driver_name);
1742912Sartem 
1756112Sqz150045 		/* wait for the ugen node's creation */
1766112Sqz150045 		if ((driver_name != NULL) && (strcmp (driver_name, "usb_mid") == 0)) {
1776112Sqz150045 			if (hdl = di_devlink_init (devfs_path, DI_MAKE_LINK)) {
1786112Sqz150045 				di_devlink_fini (&hdl);
1796112Sqz150045 			}
1806112Sqz150045 		}
1816112Sqz150045 
1826112Sqz150045 		devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
1836112Sqz150045 
1846112Sqz150045 		/* add to TDL so preprobing callouts and prober can access it */
1856112Sqz150045 		hal_device_store_add (hald_get_tdl (), d);
1866112Sqz150045 
1876112Sqz150045 		if (((binding_name = di_binding_name (node)) != NULL) &&
1886112Sqz150045 		    (strncmp (binding_name, "usbif,", sizeof ("usbif,") - 1) == 0)) {
1896112Sqz150045 
1906112Sqz150045 			snprintf (if_devfs_path, sizeof (if_devfs_path), "%s:if%d",
1919608SLin.Guo@Sun.COM 			    devfs_path, 0);
1926112Sqz150045 			if ((nd = devinfo_usb_if_add (d, node, if_devfs_path,
1939608SLin.Guo@Sun.COM 			    if_devfs_path, 0)) != NULL) {
1946112Sqz150045 				d = nd;
1956112Sqz150045 				nd = NULL;
1966112Sqz150045 				devfs_path = if_devfs_path;
1976112Sqz150045 			}
1982912Sartem 		}
1996112Sqz150045 	} else {
2006112Sqz150045 		/* It is a USB interface node or IA node. */
2016112Sqz150045 		int *j;
2026112Sqz150045 
2036112Sqz150045 		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface-count", &j) > 0) {
2046112Sqz150045 			/*
2056112Sqz150045 			 * The USB IA node properties are not defined in
2066112Sqz150045 			 * HAL spec so far. So IA node udi has "ia" sign
2076112Sqz150045 			 * now, different from the IF node udi with "if".
2086112Sqz150045 			 */
2096112Sqz150045 			snprintf (if_devfs_path, sizeof (if_devfs_path),
2106112Sqz150045 			    "%s:ia%d", devfs_path, *i);
2116112Sqz150045 		} else {
2126112Sqz150045 			snprintf (if_devfs_path, sizeof (if_devfs_path),
2136112Sqz150045 			    "%s:if%d", devfs_path, *i);
2146112Sqz150045 		}
2156112Sqz150045 
2166112Sqz150045 		d = devinfo_usb_if_add (parent, node, devfs_path, if_devfs_path, *i);
2172912Sartem 	}
2182912Sartem 
2192912Sartem 	/* driver specific */
2209608SLin.Guo@Sun.COM 	if (driver_name != NULL) {
2219608SLin.Guo@Sun.COM 		if (strcmp (driver_name, "scsa2usb") == 0) {
2229608SLin.Guo@Sun.COM 			nd = devinfo_usb_scsa2usb_add (d, node);
2239608SLin.Guo@Sun.COM 		} else if (strcmp (driver_name, "usbprn") == 0) {
2249608SLin.Guo@Sun.COM 			nd = devinfo_usb_printer_add (d, node);
2259608SLin.Guo@Sun.COM 		} else if (strcmp(driver_name, "hid") == 0) {
2269608SLin.Guo@Sun.COM 			if (hdl = di_devlink_init(devfs_path, DI_MAKE_LINK)) {
2279608SLin.Guo@Sun.COM 				di_devlink_fini(&hdl);
2289608SLin.Guo@Sun.COM 			}
2299608SLin.Guo@Sun.COM 			nd = devinfo_usb_input_add(d, node);
2309794SLin.Guo@Sun.COM 		} else if (strcmp(driver_name, "usbvc") == 0) {
2319794SLin.Guo@Sun.COM 			if (hdl = di_devlink_init(devfs_path, DI_MAKE_LINK)) {
2329794SLin.Guo@Sun.COM 				di_devlink_fini(&hdl);
2339794SLin.Guo@Sun.COM 			}
2349794SLin.Guo@Sun.COM 			nd = devinfo_usb_video4linux_add(d, node);
2359608SLin.Guo@Sun.COM 		}
2362912Sartem 	}
2372912Sartem 
2382912Sartem out:
2392912Sartem 	if (nd != NULL) {
2402912Sartem 		return (nd);
2412912Sartem 	} else {
2422912Sartem 		return (d);
2432912Sartem 	}
2442912Sartem }
2452912Sartem 
2466112Sqz150045 
2476112Sqz150045 static void
set_usb_properties(HalDevice * d,di_node_t node,gchar * devfs_path,char * driver_name)2486112Sqz150045 set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name)
2496112Sqz150045 {
2506112Sqz150045 	usb_dev_descr_t	*dev_descrp = NULL;	/* device descriptor */
2516112Sqz150045 	usb_cfg_descr_t	*cfg_descrp = NULL;	/* configuration descriptor */
2526112Sqz150045 	unsigned char	*rdata = NULL;
2536112Sqz150045 	char *p;
2546112Sqz150045 	int i = 0;
2556112Sqz150045 
2566112Sqz150045 	hal_device_property_set_int (d, "usb_device.port_number",
2576112Sqz150045 	    atoi (devfs_path + strlen (devfs_path) -1));
2586112Sqz150045 
2596112Sqz150045 	if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-dev-descriptor",
2606112Sqz150045 	    &rdata) > 0) {
2616112Sqz150045 		dev_descrp = (usb_dev_descr_t *)rdata;
2626112Sqz150045 
2636112Sqz150045 		if (dev_descrp != NULL) {
2646112Sqz150045 			hal_device_property_set_int (d, "usb_device.device_class",
2656112Sqz150045 			    dev_descrp->bDeviceClass);
2666112Sqz150045 			hal_device_property_set_int (d, "usb_device.device_subclass",
2676112Sqz150045 			    dev_descrp->bDeviceSubClass);
2686112Sqz150045 			hal_device_property_set_int (d, "usb_device.device_protocol",
2696112Sqz150045 			    dev_descrp->bDeviceProtocol);
2706112Sqz150045 		}
2716112Sqz150045 	}
2726112Sqz150045 
2736112Sqz150045 	if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-raw-cfg-descriptors",
2746112Sqz150045 	    &rdata) > 0) {
2756112Sqz150045 		cfg_descrp = (usb_cfg_descr_t *)(rdata);
2766112Sqz150045 
2776112Sqz150045 		if (cfg_descrp != NULL) {
2786112Sqz150045 			hal_device_property_set_int (d, "usb_device.configuration_value",
2796112Sqz150045 			    cfg_descrp->bConfigurationValue);
2806112Sqz150045 			hal_device_property_set_int (d, "usb_device.max_power",
2816112Sqz150045 			    cfg_descrp->bMaxPower);
2826112Sqz150045 			hal_device_property_set_int (d, "usb_device.num_interfaces",
2836112Sqz150045 			    cfg_descrp->bNumInterfaces);
2846112Sqz150045 			hal_device_property_set_bool (d, "usb_device.can_wake_up",
2856112Sqz150045 			    (cfg_descrp->bmAttributes & 0x20) ? TRUE : FALSE);
2866112Sqz150045 			hal_device_property_set_bool (d, "usb_device.is_self_powered",
2876112Sqz150045 			    (cfg_descrp->bmAttributes & 0x40) ? TRUE : FALSE);
2886112Sqz150045 		}
2896112Sqz150045 	}
2906112Sqz150045 
2916112Sqz150045 	/* get the node's usb tree level by counting hub numbers */
2926112Sqz150045 	do {
2936112Sqz150045 		if (p = strstr (devfs_path, "/hub@")) {
2946112Sqz150045 			devfs_path = p + strlen ("/hub@");
2956112Sqz150045 			i ++;
2966112Sqz150045 		}
2976112Sqz150045 	} while (p != NULL);
2986112Sqz150045 
2996112Sqz150045 	if ((driver_name != NULL) && (strcmp (driver_name, "hubd") == 0) && (i > 0))
3006112Sqz150045 		i --;
3016112Sqz150045 
3026112Sqz150045 	hal_device_property_set_int (d, "usb_device.level_number", i);
3036112Sqz150045 }
3046112Sqz150045 
3056112Sqz150045 
3066112Sqz150045 static usb_if_descr_t *
parse_usb_if_descr(di_node_t node,int ifnum)3076112Sqz150045 parse_usb_if_descr(di_node_t node, int ifnum)
3082912Sartem {
3096112Sqz150045 	unsigned char	*rdata = NULL;
3106112Sqz150045 	usb_if_descr_t	*if_descrp=NULL;	/* interface descriptor */
3119608SLin.Guo@Sun.COM 	di_node_t	tmp_node = DI_NODE_NIL;
3126112Sqz150045 	uint8_t num, length, type;
3136112Sqz150045 	int rlen;
3146112Sqz150045 	gchar *devpath = NULL;
3156112Sqz150045 
3166112Sqz150045 	if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, node,
3176112Sqz150045 	     "usb-raw-cfg-descriptors", &rdata)) < 0) {
3186112Sqz150045 
3196112Sqz150045 		char *p;
3206112Sqz150045 		int i;
3216112Sqz150045 
3226112Sqz150045 		if ((devpath = di_devfs_path (node)) == NULL)
3236112Sqz150045 			goto out;
3246112Sqz150045 
3256112Sqz150045 		/* Look up its parent that may be a USB IA or USB mid. */
3266112Sqz150045 		for (i = 0; i < 2; i++) {
3276112Sqz150045 			p = strrchr (devpath, '/');
3286112Sqz150045 			if (p == NULL)
3296112Sqz150045 				goto out;
3306112Sqz150045 			*p = '\0';
3319794SLin.Guo@Sun.COM 
3326112Sqz150045 			if ((tmp_node = di_init (devpath, DINFOCPYALL)) == DI_NODE_NIL)
3336112Sqz150045 				goto out;
3346112Sqz150045 
3356112Sqz150045 			if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, tmp_node,
3366112Sqz150045 			     "usb-raw-cfg-descriptors", &rdata)) > 0)
3376112Sqz150045 				break;
3386112Sqz150045 
3396112Sqz150045 			di_fini (tmp_node);
3406112Sqz150045 		}
3416112Sqz150045 	}
3422912Sartem 
3436112Sqz150045 	if (rdata == NULL)
3446112Sqz150045 		goto out;
3456112Sqz150045 
3466112Sqz150045 	do {
3476112Sqz150045 		length = (uint8_t)*rdata;
3486112Sqz150045 		type = (uint8_t)*(rdata + 1);
3496112Sqz150045 		if (type == USB_DESCR_TYPE_IF) {
3506112Sqz150045 			num = (uint8_t)*(rdata + 2);
3516112Sqz150045 			if (num == ifnum) {
3526112Sqz150045 				if_descrp = (usb_if_descr_t *)rdata;
3536112Sqz150045 				break;
3546112Sqz150045 			}
3556112Sqz150045 		}
3566112Sqz150045 		rdata += length;
3576112Sqz150045 		rlen -= length;
3586112Sqz150045 	} while ((length > 0 ) && (rlen > 0));
3596112Sqz150045 
3606112Sqz150045 out:
3616112Sqz150045 	if (devpath != NULL)
3626112Sqz150045 		di_devfs_path_free (devpath);
3636112Sqz150045 	if (tmp_node != DI_NODE_NIL)
3646112Sqz150045 		di_fini (tmp_node);
3656112Sqz150045 	return (if_descrp);
3666112Sqz150045 }
3676112Sqz150045 
3686112Sqz150045 
3696112Sqz150045 static HalDevice *
devinfo_usb_if_add(HalDevice * parent,di_node_t node,gchar * devfs_path,gchar * if_devfs_path,int ifnum)3706112Sqz150045 devinfo_usb_if_add(HalDevice *parent, di_node_t node, gchar *devfs_path,
3716112Sqz150045 		   gchar *if_devfs_path, int ifnum)
3726112Sqz150045 {
3736112Sqz150045 	HalDevice	*d = NULL;
3746112Sqz150045 	char		udi[HAL_PATH_MAX];
3756112Sqz150045 	const char	*parent_info;
3766112Sqz150045 	usb_if_descr_t	*if_descrp=NULL;	/* interface descriptor */
3772912Sartem 
3782912Sartem 	d = hal_device_new ();
3792912Sartem 
3806112Sqz150045 	devinfo_set_default_properties (d, parent, node, if_devfs_path);
3816112Sqz150045 
3826112Sqz150045 	/* Set the existed physical device path. */
3836112Sqz150045 	hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
3846112Sqz150045 	hal_device_property_set_string (d, "info.subsystem", "usb");
3856112Sqz150045 	hal_device_property_set_string (d, "info.product", "USB Device Interface");
3866112Sqz150045 
3876112Sqz150045 	/* Set usb interface properties to interface node. */
3886112Sqz150045 	if (strstr (if_devfs_path, ":ia") == NULL) {
3896112Sqz150045 		if_descrp = parse_usb_if_descr (node, ifnum);
3906112Sqz150045 
3916112Sqz150045 		if (if_descrp != NULL) {
3926112Sqz150045 			hal_device_property_set_int (d, "usb.interface.class",
3936112Sqz150045 			    if_descrp->bInterfaceClass);
3946112Sqz150045 			hal_device_property_set_int (d, "usb.interface.subclass",
3956112Sqz150045 			    if_descrp->bInterfaceSubClass);
3966112Sqz150045 			hal_device_property_set_int (d, "usb.interface.protocol",
3976112Sqz150045 			    if_descrp->bInterfaceProtocol);
3986112Sqz150045 			hal_device_property_set_int (d, "usb.interface.number",
3996112Sqz150045 			    if_descrp->bInterfaceNumber);
4006112Sqz150045 		}
4016112Sqz150045 	}
4022912Sartem 
4032912Sartem 	/* copy parent's usb_device.* properties */
4046112Sqz150045 	parent_info = hal_device_property_get_string (parent, "info.subsystem");
4056112Sqz150045 	if (parent_info != NULL) {
4066112Sqz150045 		if (strcmp (parent_info, "usb_device") == 0) {
4076112Sqz150045 			hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device.");
4086112Sqz150045 		} else if (strcmp (parent_info, "usb") == 0) {
4096112Sqz150045 			/* for the case that the parent is IA node */
4106112Sqz150045 			hal_device_merge_with_rewrite (d, parent, "usb.", "usb.");
4116112Sqz150045 		}
4126112Sqz150045 	}
4136112Sqz150045 
4146112Sqz150045 	devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
4156112Sqz150045 
4166112Sqz150045 	/* add to TDL so preprobing callouts and prober can access it */
4176112Sqz150045 	hal_device_store_add (hald_get_tdl (), d);
4182912Sartem 
4192912Sartem 	return (d);
4202912Sartem }
4212912Sartem 
4222912Sartem 
4233536Sjacobs static void
get_dev_link_path(di_node_t node,char * nodetype,char * re,char ** devlink,char ** minor_path,char ** minor_name)4249608SLin.Guo@Sun.COM get_dev_link_path(di_node_t node, char *nodetype, char *re, char **devlink, char **minor_path, char **minor_name)
4253536Sjacobs {
4263536Sjacobs 	di_devlink_handle_t devlink_hdl;
4276112Sqz150045 	int	major;
4286112Sqz150045 	di_minor_t minor;
4296112Sqz150045 	dev_t	devt;
4302912Sartem 
4313536Sjacobs 	*devlink = NULL;
4326112Sqz150045 	*minor_path = NULL;
4339608SLin.Guo@Sun.COM 	*minor_name = NULL;
4343536Sjacobs 
4356112Sqz150045 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
4366112Sqz150045 		return;
4376112Sqz150045 	}
4382912Sartem 
4396112Sqz150045 	major = di_driver_major(node);
4406112Sqz150045 	minor = DI_MINOR_NIL;
4416112Sqz150045 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
4426112Sqz150045 		devt = di_minor_devt(minor);
4436112Sqz150045 		if (major != major(devt)) {
4446112Sqz150045 			continue;
4456112Sqz150045 		}
4463536Sjacobs 
4476112Sqz150045 		if (di_minor_type(minor) != DDM_MINOR) {
4486112Sqz150045 			continue;
4496112Sqz150045 		}
4502912Sartem 
4516112Sqz150045 		if ((*minor_path = di_devfs_minor_path(minor)) == NULL) {
4526112Sqz150045 			continue;
4536112Sqz150045 		}
4542912Sartem 
4559608SLin.Guo@Sun.COM 		if (strcmp(di_minor_nodetype(minor), nodetype) == 0) {
4569608SLin.Guo@Sun.COM 			*devlink = get_devlink(devlink_hdl, re, *minor_path);
4579608SLin.Guo@Sun.COM 			/*
4589794SLin.Guo@Sun.COM 			 * During hotplugging, devlink could be NULL for usb
4599608SLin.Guo@Sun.COM 			 * devices due to devlink database has not yet been
4609608SLin.Guo@Sun.COM 			 * updated when hal try to read from it although the
4619608SLin.Guo@Sun.COM 			 * actually dev link path has been created. In such a
4629608SLin.Guo@Sun.COM 			 * situation, we will read the devlink name from
4639608SLin.Guo@Sun.COM 			 * /dev/usb directory.
4649608SLin.Guo@Sun.COM 			 */
465*10975SRaymond.Chen@Sun.COM 			if ((*devlink == NULL) && (re != NULL) &&
4669794SLin.Guo@Sun.COM 			    ((strstr(re, "hid") != NULL) || (strstr(re, "video") != NULL))) {
4679794SLin.Guo@Sun.COM 				*devlink = get_usb_devlink(*minor_path, "/dev/usb/");
4689608SLin.Guo@Sun.COM 			}
4699608SLin.Guo@Sun.COM 
4709608SLin.Guo@Sun.COM 			if (*devlink != NULL) {
4719608SLin.Guo@Sun.COM 				*minor_name = di_minor_name(minor);
4729608SLin.Guo@Sun.COM 				break;
4739608SLin.Guo@Sun.COM 			}
4743536Sjacobs 		}
4759608SLin.Guo@Sun.COM 
4763536Sjacobs 		di_devfs_path_free (*minor_path);
4773710Sjacobs 		*minor_path = NULL;
4783536Sjacobs 	}
4793536Sjacobs 	di_devlink_fini (&devlink_hdl);
4802912Sartem }
4812912Sartem 
4822912Sartem static HalDevice *
devinfo_usb_video4linux_add(HalDevice * usbd,di_node_t node)4839794SLin.Guo@Sun.COM devinfo_usb_video4linux_add(HalDevice *usbd, di_node_t node)
4849794SLin.Guo@Sun.COM {
4859794SLin.Guo@Sun.COM 	HalDevice *d = NULL;
4869794SLin.Guo@Sun.COM 	int	major;
4879794SLin.Guo@Sun.COM 	di_minor_t minor;
4889794SLin.Guo@Sun.COM 	dev_t	devt;
4899794SLin.Guo@Sun.COM 	char	*devlink = NULL;
4909794SLin.Guo@Sun.COM 	char	*dev_videolink = NULL;
4919794SLin.Guo@Sun.COM 	char	*minor_path = NULL;
4929794SLin.Guo@Sun.COM 	char	*minor_name = NULL;
4939794SLin.Guo@Sun.COM 	char	udi[HAL_PATH_MAX];
4949794SLin.Guo@Sun.COM 	char	*s;
4959794SLin.Guo@Sun.COM 
4969794SLin.Guo@Sun.COM 	get_dev_link_path(node, "usb_video",
497*10975SRaymond.Chen@Sun.COM 	    "^usb/video[0-9]+",  &devlink, &minor_path, &minor_name);
4989794SLin.Guo@Sun.COM 
4999794SLin.Guo@Sun.COM 	if ((minor_path == NULL) || (devlink == NULL)) {
5009794SLin.Guo@Sun.COM 
5019794SLin.Guo@Sun.COM 		goto out;
5029794SLin.Guo@Sun.COM 	}
5039794SLin.Guo@Sun.COM 
5049794SLin.Guo@Sun.COM 	HAL_DEBUG(("devlink %s, minor_name %s", devlink, minor_name));
5059794SLin.Guo@Sun.COM 	if (strcmp(minor_name, "usbvc") != 0) {
5069794SLin.Guo@Sun.COM 
5079794SLin.Guo@Sun.COM 		goto out;
5089794SLin.Guo@Sun.COM 	}
5099794SLin.Guo@Sun.COM 
5109794SLin.Guo@Sun.COM 	d = hal_device_new();
5119794SLin.Guo@Sun.COM 
5129794SLin.Guo@Sun.COM 	devinfo_set_default_properties(d, usbd, node, minor_path);
5139794SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "info.subsystem", "video4linux");
5149794SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "info.category", "video4linux");
5159794SLin.Guo@Sun.COM 
5169794SLin.Guo@Sun.COM 	hal_device_add_capability(d, "video4linux");
5179794SLin.Guo@Sun.COM 
5189794SLin.Guo@Sun.COM 	/* Get logic link under /dev (/dev/video+) */
5199794SLin.Guo@Sun.COM 	dev_videolink = get_usb_devlink(strstr(devlink, "usb"), "/dev/");
5209794SLin.Guo@Sun.COM 
5219794SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "video4linux.device", dev_videolink);
5229794SLin.Guo@Sun.COM 
5239794SLin.Guo@Sun.COM 	hal_util_compute_udi(hald_get_gdl(), udi, sizeof (udi),
5249794SLin.Guo@Sun.COM 	    "%s_video4linux", hal_device_get_udi(usbd));
5259794SLin.Guo@Sun.COM 
5269794SLin.Guo@Sun.COM 	hal_device_set_udi(d, udi);
5279794SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "info.udi", udi);
5289794SLin.Guo@Sun.COM 	PROP_STR(d, node, s, "usb-product-name", "info.product");
5299794SLin.Guo@Sun.COM 
5309794SLin.Guo@Sun.COM 	devinfo_add_enqueue(d, minor_path, &devinfo_usb_handler);
5319794SLin.Guo@Sun.COM 
5329794SLin.Guo@Sun.COM 
5339794SLin.Guo@Sun.COM out:
5349794SLin.Guo@Sun.COM 	if (devlink) {
5359794SLin.Guo@Sun.COM 		free(devlink);
5369794SLin.Guo@Sun.COM 	}
5379794SLin.Guo@Sun.COM 
5389794SLin.Guo@Sun.COM 	if (minor_path) {
5399794SLin.Guo@Sun.COM 		di_devfs_path_free(minor_path);
5409794SLin.Guo@Sun.COM 	}
5419794SLin.Guo@Sun.COM 
5429794SLin.Guo@Sun.COM 	return (d);
5439794SLin.Guo@Sun.COM }
5449794SLin.Guo@Sun.COM 
5459794SLin.Guo@Sun.COM static HalDevice *
devinfo_usb_input_add(HalDevice * usbd,di_node_t node)5469608SLin.Guo@Sun.COM devinfo_usb_input_add(HalDevice *usbd, di_node_t node)
5479608SLin.Guo@Sun.COM {
5489608SLin.Guo@Sun.COM 	HalDevice *d = NULL;
5499608SLin.Guo@Sun.COM 	int	major;
5509608SLin.Guo@Sun.COM 	di_minor_t minor;
5519608SLin.Guo@Sun.COM 	dev_t	devt;
5529608SLin.Guo@Sun.COM 	char	*devlink = NULL;
5539608SLin.Guo@Sun.COM 	char	*minor_path = NULL;
5549608SLin.Guo@Sun.COM 	char	*minor_name = NULL;
5559608SLin.Guo@Sun.COM 	char	udi[HAL_PATH_MAX];
5569608SLin.Guo@Sun.COM 
5579608SLin.Guo@Sun.COM 	get_dev_link_path(node, "ddi_pseudo",
558*10975SRaymond.Chen@Sun.COM 	    "^usb/hid[0-9]+",  &devlink, &minor_path, &minor_name);
5599608SLin.Guo@Sun.COM 
5609608SLin.Guo@Sun.COM 	if ((minor_path == NULL) || (devlink == NULL)) {
5619608SLin.Guo@Sun.COM 
5629608SLin.Guo@Sun.COM 		goto out;
5639608SLin.Guo@Sun.COM 	}
5649608SLin.Guo@Sun.COM 
5659608SLin.Guo@Sun.COM 	HAL_DEBUG(("devlink %s, minor_name %s", devlink, minor_name));
5669608SLin.Guo@Sun.COM 	if ((strcmp(minor_name, "keyboard") != 0) &&
5679608SLin.Guo@Sun.COM 	    (strcmp(minor_name, "mouse") != 0)) {
5689608SLin.Guo@Sun.COM 
5699608SLin.Guo@Sun.COM 		goto out;
5709608SLin.Guo@Sun.COM 	}
5719794SLin.Guo@Sun.COM 
5729608SLin.Guo@Sun.COM 	d = hal_device_new();
5739608SLin.Guo@Sun.COM 
5749608SLin.Guo@Sun.COM 	devinfo_set_default_properties(d, usbd, node, minor_path);
5759608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "info.subsystem", "input");
5769608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "info.category", "input");
5779608SLin.Guo@Sun.COM 
5789608SLin.Guo@Sun.COM 	hal_device_add_capability(d, "input");
5799608SLin.Guo@Sun.COM 
5809608SLin.Guo@Sun.COM 	if (strcmp(minor_name, "keyboard") == 0) {
5819608SLin.Guo@Sun.COM 		hal_device_add_capability(d, "input.keyboard");
5829608SLin.Guo@Sun.COM 		hal_device_add_capability(d, "input.keys");
5839608SLin.Guo@Sun.COM 		hal_device_add_capability(d, "button");
5849608SLin.Guo@Sun.COM 	} else if (strcmp(minor_name, "mouse") == 0) {
5859608SLin.Guo@Sun.COM 		hal_device_add_capability (d, "input.mouse");
5869608SLin.Guo@Sun.COM 	}
5879608SLin.Guo@Sun.COM 
5889608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "input.device", devlink);
5899608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "input.originating_device",
5909608SLin.Guo@Sun.COM 	    hal_device_get_udi(usbd));
5919608SLin.Guo@Sun.COM 
5929608SLin.Guo@Sun.COM 	hal_util_compute_udi(hald_get_gdl(), udi, sizeof (udi),
5939608SLin.Guo@Sun.COM 	    "%s_logicaldev_input", hal_device_get_udi(usbd));
5949794SLin.Guo@Sun.COM 
5959608SLin.Guo@Sun.COM 	hal_device_set_udi(d, udi);
5969608SLin.Guo@Sun.COM 	hal_device_property_set_string(d, "info.udi", udi);
5979608SLin.Guo@Sun.COM 
5989608SLin.Guo@Sun.COM 	if (strcmp(minor_name, "keyboard") == 0) {
5999608SLin.Guo@Sun.COM 		devinfo_add_enqueue(d, minor_path, &devinfo_usb_keyboard_handler);
6009608SLin.Guo@Sun.COM 	} else {
6019608SLin.Guo@Sun.COM 		devinfo_add_enqueue(d, minor_path, &devinfo_usb_handler);
6029608SLin.Guo@Sun.COM 	}
6039608SLin.Guo@Sun.COM 
6049608SLin.Guo@Sun.COM 	/* add to TDL so preprobing callouts and prober can access it */
6059608SLin.Guo@Sun.COM 	hal_device_store_add(hald_get_tdl(), d);
6069608SLin.Guo@Sun.COM 
6079608SLin.Guo@Sun.COM out:
6089608SLin.Guo@Sun.COM 	if (devlink) {
6099608SLin.Guo@Sun.COM 		free(devlink);
6109608SLin.Guo@Sun.COM 	}
6119608SLin.Guo@Sun.COM 
6129608SLin.Guo@Sun.COM 	if (minor_path) {
6139608SLin.Guo@Sun.COM 		di_devfs_path_free(minor_path);
6149608SLin.Guo@Sun.COM 	}
6159608SLin.Guo@Sun.COM 
6169608SLin.Guo@Sun.COM 	return (d);
6179608SLin.Guo@Sun.COM }
6189608SLin.Guo@Sun.COM 
6199608SLin.Guo@Sun.COM static HalDevice *
devinfo_usb_scsa2usb_add(HalDevice * usbd,di_node_t node)6206112Sqz150045 devinfo_usb_scsa2usb_add(HalDevice *usbd, di_node_t node)
6212912Sartem {
6222912Sartem 	HalDevice *d = NULL;
6232912Sartem 	di_devlink_handle_t devlink_hdl;
6246112Sqz150045 	int	major;
6256112Sqz150045 	di_minor_t minor;
6266112Sqz150045 	dev_t	devt;
6276112Sqz150045 	char	*minor_path = NULL;
6289608SLin.Guo@Sun.COM 	char	*minor_name = NULL;
6292912Sartem 	char	*devlink = NULL;
6306112Sqz150045 	char	udi[HAL_PATH_MAX];
6312912Sartem 
632*10975SRaymond.Chen@Sun.COM 	get_dev_link_path(node, "ddi_ctl:devctl:scsi",
633*10975SRaymond.Chen@Sun.COM 	    "^usb/mass-storage[0-9]+", &devlink, &minor_path, &minor_name);
6342912Sartem 
6353536Sjacobs 	if ((devlink == NULL) || (minor_path == NULL)) {
6362912Sartem 		goto out;
6372912Sartem 	}
6382912Sartem 
6392912Sartem 	d = hal_device_new ();
6402912Sartem 
6412912Sartem 	devinfo_set_default_properties (d, usbd, node, minor_path);
6426112Sqz150045 	hal_device_property_set_string (d, "scsi_host.solaris.device", devlink);
6436112Sqz150045 	hal_device_property_set_string (d, "info.category", "scsi_host");
6446112Sqz150045 	hal_device_property_set_int (d, "scsi_host.host", 0);
6452912Sartem 
6466112Sqz150045 	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
6476112Sqz150045 	    "%s/scsi_host%d", hal_device_get_udi (usbd),
6486112Sqz150045 	    hal_device_property_get_int (d, "scsi_host.host"));
6496112Sqz150045 	hal_device_set_udi (d, udi);
6506112Sqz150045 	hal_device_property_set_string (d, "info.udi", udi);
6516112Sqz150045 	hal_device_property_set_string (d, "info.product", "SCSI Host Adapter");
6522912Sartem 
6532912Sartem 	devinfo_add_enqueue (d, minor_path, &devinfo_usb_handler);
6542912Sartem 
6552912Sartem out:
6562912Sartem 	if (devlink) {
6572912Sartem 		free(devlink);
6582912Sartem 	}
6592912Sartem 	if (minor_path) {
6602912Sartem 		di_devfs_path_free (minor_path);
6612912Sartem 	}
6622912Sartem 
6632912Sartem 	return (d);
6642912Sartem }
6652912Sartem 
6663536Sjacobs static HalDevice *
devinfo_usb_printer_add(HalDevice * parent,di_node_t node)6676112Sqz150045 devinfo_usb_printer_add(HalDevice *parent, di_node_t node)
6683536Sjacobs {
6698529SNorm.Jacobs@Sun.COM 	char *properties[] = { "vendor", "product", "serial", NULL };
6708529SNorm.Jacobs@Sun.COM 	int i;
6713536Sjacobs 	HalDevice *d = NULL;
6726112Sqz150045 	char	udi[HAL_PATH_MAX];
6733536Sjacobs 	char *s;
6749608SLin.Guo@Sun.COM 	char *devlink = NULL, *minor_path = NULL, *minor_name = NULL;
6758529SNorm.Jacobs@Sun.COM 	const char	*subsystem;
6763536Sjacobs 
6779608SLin.Guo@Sun.COM 	get_dev_link_path(node, "ddi_printer", "printers/.+", &devlink, &minor_path, &minor_name);
6783536Sjacobs 
6793536Sjacobs 	if ((devlink == NULL) || (minor_path == NULL)) {
6803536Sjacobs 		goto out;
6813536Sjacobs 	}
6823536Sjacobs 
6833536Sjacobs 	d = hal_device_new ();
6843536Sjacobs 
6853536Sjacobs 	devinfo_set_default_properties (d, parent, node, minor_path);
6866112Sqz150045 	hal_device_property_set_string (d, "info.category", "printer");
6873536Sjacobs 	hal_device_add_capability (d, "printer");
6883536Sjacobs 
6893536Sjacobs 	/* add printer properties */
6906112Sqz150045 	hal_device_property_set_string (d, "printer.device", devlink);
6918529SNorm.Jacobs@Sun.COM 
6928529SNorm.Jacobs@Sun.COM 	/* copy parent's selected usb* properties to printer properties */
6938529SNorm.Jacobs@Sun.COM 	subsystem = hal_device_property_get_string (parent, "info.subsystem");
6948529SNorm.Jacobs@Sun.COM 	for (i = 0; properties[i] != NULL; i++) {
6958529SNorm.Jacobs@Sun.COM 		char src[32], dst[32]; /* "subsystem.property" names */
6968529SNorm.Jacobs@Sun.COM 
6978529SNorm.Jacobs@Sun.COM 		snprintf(src, sizeof (src), "%s.%s", subsystem, properties[i]);
6988529SNorm.Jacobs@Sun.COM 		snprintf(dst, sizeof (dst), "printer.%s", properties[i]);
6998529SNorm.Jacobs@Sun.COM 		hal_device_copy_property(parent, src, d, dst);
7008529SNorm.Jacobs@Sun.COM 	}
7013536Sjacobs 
7023536Sjacobs 	devinfo_add_enqueue (d, minor_path, &devinfo_usb_printer_handler);
7033536Sjacobs 
7043536Sjacobs out:
7053536Sjacobs 	if (devlink) {
7063536Sjacobs 		free(devlink);
7073536Sjacobs 	}
7083536Sjacobs 	if (minor_path) {
7093536Sjacobs 		di_devfs_path_free (minor_path);
7103536Sjacobs 	}
7113536Sjacobs 
7123536Sjacobs 	return (d);
7133536Sjacobs }
7143536Sjacobs 
7153536Sjacobs const gchar *
devinfo_printer_prnio_get_prober(HalDevice * d,int * timeout)7163536Sjacobs devinfo_printer_prnio_get_prober (HalDevice *d, int *timeout)
7173536Sjacobs {
7183536Sjacobs 	*timeout = 5 * 1000;	/* 5 second timeout */
7193536Sjacobs 	return ("hald-probe-printer");
7203536Sjacobs }
7219608SLin.Guo@Sun.COM 
7229608SLin.Guo@Sun.COM const gchar *
devinfo_keyboard_get_prober(HalDevice * d,int * timeout)7239608SLin.Guo@Sun.COM devinfo_keyboard_get_prober(HalDevice *d, int *timeout)
7249608SLin.Guo@Sun.COM {
7259608SLin.Guo@Sun.COM 	*timeout = 5 * 1000;	/* 5 second timeout */
7269608SLin.Guo@Sun.COM 	return ("hald-probe-xkb");
7279608SLin.Guo@Sun.COM }
728