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