1 /***************************************************************************
2  *
3  * devinfo_usb.h : USB devices
4  *
5  * Copyright 2006 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 #ifdef HAVE_CONFIG_H
15 #  include <config.h>
16 #endif
17 
18 #include <stdio.h>
19 #include <string.h>
20 #include <libdevinfo.h>
21 #include <sys/types.h>
22 #include <sys/mkdev.h>
23 #include <sys/stat.h>
24 
25 #include "../osspec.h"
26 #include "../logger.h"
27 #include "../hald.h"
28 #include "../hald_dbus.h"
29 #include "../device_info.h"
30 #include "../util.h"
31 #include "../ids.h"
32 #include "hotplug.h"
33 #include "devinfo.h"
34 #include "devinfo_usb.h"
35 
36 HalDevice *devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
37 static HalDevice *devinfo_usb_if_add(HalDevice *d, di_node_t node, gchar *devfs_path, int ifnum);
38 static HalDevice *devinfo_usb_scsa2usb_add(HalDevice *d, di_node_t node, gchar *devfs_path);
39 
40 DevinfoDevHandler devinfo_usb_handler = {
41         devinfo_usb_add,
42 	NULL,
43 	NULL,
44 	NULL,
45 	NULL,
46         NULL
47 };
48 
49 HalDevice *
50 devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
51 {
52 	HalDevice *d, *nd = NULL;
53 	char	*s;
54 	int	*i, *vid;
55 	char	*driver_name, *binding_name;
56         char    if_devfs_path[HAL_PATH_MAX];
57 
58 	/*
59 	 * we distinguish USB devices by presence of "usb-vendor-id"
60 	 * property. should USB devices have "device_type"?
61 	 */
62         if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-vendor-id", &vid) <= 0) {
63 		return (NULL);
64 	}
65 
66 	d = hal_device_new ();
67 
68 	devinfo_set_default_properties (d, parent, node, devfs_path);
69 	hal_device_property_set_string (d, "info.bus", "usb_device");
70 	PROP_STR(d, node, s, "usb-product-name", "info.product");
71 	PROP_STR(d, node, s, "usb-product-name", "usb_device.product");
72 	PROP_STR(d, node, s, "usb-vendor-name", "usb_device.vendor");
73 	PROP_INT(d, node, i, "usb-vendor-id", "usb_device.vendor_id");
74 	PROP_INT(d, node, i, "usb-product-id", "usb_device.product_id");
75 	PROP_INT(d, node, i, "usb-revision-id", "usb_device.device_revision_bcd");
76 	PROP_INT(d, node, i, "usb-release-id", "usb_device.version_bcd");
77 	PROP_STR(d, node, s, "usb-serialno", "usb_device.serial");
78 
79 	/* class, subclass */
80 	/* hal_device_property_set_int (d, "usb_device.device_class", 8); */
81 
82 	/* binding name tells us if driver is bound to interface or device */
83 	if (((binding_name = di_binding_name(node)) != NULL) &&
84 	    (strncmp(binding_name, "usbif,", sizeof ("usbif,") - 1) == 0)) {
85 		snprintf(if_devfs_path, sizeof (if_devfs_path), "%s:if%d", devfs_path, 0);
86 		if ((nd = devinfo_usb_if_add(d, node, if_devfs_path, 0)) != NULL) {
87 			d = nd;
88 			nd = NULL;
89 			devfs_path = if_devfs_path;
90 		}
91 	}
92 
93 	/* driver specific */
94 	driver_name = di_driver_name (node);
95 	if ((driver_name != NULL) && (strcmp (driver_name, "scsa2usb") == 0)) {
96 		nd = devinfo_usb_scsa2usb_add (d, node, devfs_path);
97 	} else {
98 		devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
99 	}
100 
101 out:
102 	if (nd != NULL) {
103 		return (nd);
104 	} else {
105 		return (d);
106 	}
107 }
108 
109 static HalDevice *
110 devinfo_usb_if_add(HalDevice *parent, di_node_t node, gchar *devfs_path, int ifnum)
111 {
112 	HalDevice *d = NULL;
113         char    udi[HAL_PATH_MAX];
114 
115 	devinfo_add_enqueue (parent, devfs_path, &devinfo_usb_handler);
116 
117 	d = hal_device_new ();
118 
119 	devinfo_set_default_properties (d, parent, node, devfs_path);
120         hal_device_property_set_string (d, "info.bus", "usb");
121 
122         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
123 		"%s_if%d", hal_device_get_udi (parent), ifnum);
124         hal_device_set_udi (d, udi);
125         hal_device_property_set_string (d, "info.udi", udi);
126         hal_device_property_set_string (d, "info.product", "USB Device Interface");
127 
128 	/* copy parent's usb_device.* properties */
129 	hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device.");
130 
131 	return (d);
132 }
133 
134 static int
135 walk_devlinks(di_devlink_t devlink, void *arg)
136 {
137         char **path = (char **)arg;
138 
139         *path = strdup(di_devlink_path(devlink));
140 
141         return (DI_WALK_TERMINATE);
142 }
143 
144 static char *
145 get_devlink(di_devlink_handle_t devlink_hdl, char *path)
146 {
147 	char *devlink = NULL;
148 
149         (void) di_devlink_walk(devlink_hdl, NULL, path,
150             DI_PRIMARY_LINK, &devlink, walk_devlinks);
151 
152         return (devlink);
153 }
154 
155 static HalDevice *
156 devinfo_usb_scsa2usb_add(HalDevice *usbd, di_node_t node, gchar *devfs_path)
157 {
158 	HalDevice *d = NULL;
159 	di_devlink_handle_t devlink_hdl;
160         int     major;
161         di_minor_t minor;
162         dev_t   devt;
163         char    *minor_path = NULL;
164 	char	*devlink = NULL;
165         char    udi[HAL_PATH_MAX];
166 
167 	devinfo_add_enqueue (usbd, devfs_path, &devinfo_usb_handler);
168 
169         if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
170                 printf("di_devlink_init() failed\n");
171                 return (NULL);
172         }
173 
174         major = di_driver_major(node);
175         minor = DI_MINOR_NIL;
176         while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
177                 devt = di_minor_devt(minor);
178                 if (major != major(devt)) {
179                         continue;
180                 }
181                 if ((minor_path = di_devfs_minor_path(minor)) == NULL) {
182                         continue;
183                 }
184                 if (di_minor_type(minor) != DDM_MINOR) {
185                         continue;
186                 }
187 		if (strcmp (di_minor_nodetype(minor),
188 		    "ddi_ctl:devctl:scsi") == 0) {
189                 	devlink = get_devlink(devlink_hdl, minor_path);
190                 	if (devlink == NULL) {
191 				devlink = strdup("");
192 			}
193 			break;
194 		}
195 		di_devfs_path_free (minor_path);
196 		minor_path = NULL;
197         }
198 
199 	di_devlink_fini (&devlink_hdl);
200 
201 	if (devlink == NULL) {
202 		goto out;
203 	}
204 
205 	d = hal_device_new ();
206 
207 	devinfo_set_default_properties (d, usbd, node, minor_path);
208        	hal_device_property_set_string (d, "scsi_host.solaris.device", devlink);
209         hal_device_property_set_string (d, "info.category", "scsi_host");
210         hal_device_property_set_int (d, "scsi_host.host", 0);
211 
212         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
213 		"%s/scsi_host%d", hal_device_get_udi (usbd),
214 		hal_device_property_get_int (d, "scsi_host.host"));
215         hal_device_set_udi (d, udi);
216         hal_device_property_set_string (d, "info.udi", udi);
217         hal_device_property_set_string (d, "info.product", "SCSI Host Adapter");
218 
219 	devinfo_add_enqueue (d, minor_path, &devinfo_usb_handler);
220 
221 out:
222 	if (devlink) {
223 		free(devlink);
224 	}
225 	if (minor_path) {
226 		di_devfs_path_free (minor_path);
227 	}
228 
229 	return (d);
230 }
231 
232