1 /***************************************************************************
2  *
3  * devinfo_usb.h : USB devices
4  *
5  * Copyright 2008 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 #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 
37 static HalDevice *devinfo_usb_if_add(HalDevice *d, di_node_t node, gchar *devfs_path,
38 				     gchar *if_devfs_path, int ifnum);
39 static HalDevice *devinfo_usb_scsa2usb_add(HalDevice *d, di_node_t node);
40 static HalDevice *devinfo_usb_printer_add(HalDevice *usbd, di_node_t node);
41 const gchar *devinfo_printer_prnio_get_prober (HalDevice *d, int *timeout);
42 static void set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name);
43 
44 DevinfoDevHandler devinfo_usb_handler = {
45 	devinfo_usb_add,
46 	NULL,
47 	NULL,
48 	NULL,
49 	NULL,
50 	NULL
51 };
52 
53 DevinfoDevHandler devinfo_usb_printer_handler = {
54 	devinfo_usb_add,
55 	NULL,
56 	NULL,
57 	NULL,
58 	NULL,
59 	devinfo_printer_prnio_get_prober
60 };
61 
62 static gboolean
63 is_usb_node(di_node_t node)
64 {
65 	int rc;
66 	char *s;
67 
68 	/*
69 	 * USB device nodes will have "compatible" propety values that
70 	 * begins with "usb".
71 	 */
72 	rc = di_prop_lookup_strings(DDI_DEV_T_ANY, node, "compatible", &s);
73 	while (rc-- > 0) {
74 		if (strncmp(s, "usb", 3) == 0) {
75 			return (TRUE);
76 		}
77 		s += (strlen(s) + 1);
78 	}
79 
80 	return (FALSE);
81 }
82 
83 HalDevice *
84 devinfo_usb_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
85 {
86 	HalDevice *d, *nd = NULL;
87 	char	*s;
88 	int	*i;
89 	char	*driver_name, *binding_name;
90 	char	if_devfs_path[HAL_PATH_MAX];
91 	di_devlink_handle_t hdl;
92 	double	k;
93 
94 	if (is_usb_node(node) == FALSE) {
95 		return (NULL);
96 	}
97 
98 	driver_name = di_driver_name (node);
99 
100 	if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface", &i) < 0) {
101 		/* It is a USB device node. */
102 
103 		d = hal_device_new ();
104 
105 		devinfo_set_default_properties (d, parent, node, devfs_path);
106 		hal_device_property_set_string (d, "info.subsystem", "usb_device");
107 		PROP_STR(d, node, s, "usb-product-name", "info.product");
108 		PROP_STR(d, node, s, "usb-product-name", "usb_device.product");
109 		PROP_STR(d, node, s, "usb-vendor-name", "usb_device.vendor");
110 		PROP_INT(d, node, i, "usb-vendor-id", "usb_device.vendor_id");
111 		PROP_INT(d, node, i, "usb-product-id", "usb_device.product_id");
112 		PROP_INT(d, node, i, "usb-revision-id", "usb_device.device_revision_bcd");
113 		PROP_STR(d, node, s, "usb-serialno", "usb_device.serial");
114 		PROP_INT(d, node, i, "usb-port-count", "usb_device.num_ports");
115 		PROP_INT(d, node, i, "usb-num-configs", "usb_device.num_configurations");
116 		PROP_INT(d, node, i, "assigned-address", "usb_device.bus_number");
117 
118 		if  (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "usb-release", &i) > 0) {
119 			k = (double)bcd(*i);
120 			hal_device_property_set_double (d, "usb_device.version", k / 100);
121 		}
122 
123 		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "low-speed", &i) >= 0) {
124 			k = 1.5;
125 		} else if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "high-speed", &i) >= 0) {
126 			k = 480.0;
127 		} else {
128 			/* It is the full speed device. */
129 			k = 12.0;
130 		}
131 		hal_device_property_set_double (d, "usb_device.speed", k);
132 
133 		set_usb_properties (d, node, devfs_path, driver_name);
134 
135 		/* wait for the ugen node's creation */
136 		if ((driver_name != NULL) && (strcmp (driver_name, "usb_mid") == 0)) {
137 			if (hdl = di_devlink_init (devfs_path, DI_MAKE_LINK)) {
138 				di_devlink_fini (&hdl);
139 			}
140 		}
141 
142 		devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
143 
144 		/* add to TDL so preprobing callouts and prober can access it */
145 		hal_device_store_add (hald_get_tdl (), d);
146 
147 		if (((binding_name = di_binding_name (node)) != NULL) &&
148 		    (strncmp (binding_name, "usbif,", sizeof ("usbif,") - 1) == 0)) {
149 
150 			snprintf (if_devfs_path, sizeof (if_devfs_path), "%s:if%d",
151 			     devfs_path, 0);
152 			if ((nd = devinfo_usb_if_add (d, node, if_devfs_path,
153 			     if_devfs_path, 0)) != NULL) {
154 
155 				d = nd;
156 				nd = NULL;
157 				devfs_path = if_devfs_path;
158 			}
159 		}
160 	} else {
161 		/* It is a USB interface node or IA node. */
162 		int *j;
163 
164 		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node, "interface-count", &j) > 0) {
165 			/*
166 			 * The USB IA node properties are not defined in
167 			 * HAL spec so far. So IA node udi has "ia" sign
168 			 * now, different from the IF node udi with "if".
169 			 */
170 			snprintf (if_devfs_path, sizeof (if_devfs_path),
171 			    "%s:ia%d", devfs_path, *i);
172 		} else {
173 			snprintf (if_devfs_path, sizeof (if_devfs_path),
174 			    "%s:if%d", devfs_path, *i);
175 		}
176 
177 		d = devinfo_usb_if_add (parent, node, devfs_path, if_devfs_path, *i);
178 	}
179 
180 	/* driver specific */
181 	if ((driver_name != NULL) && (strcmp (driver_name, "scsa2usb") == 0)) {
182 		nd = devinfo_usb_scsa2usb_add (d, node);
183 	} else if ((driver_name != NULL) &&
184 	    (strcmp (driver_name, "usbprn") == 0)) {
185 		nd = devinfo_usb_printer_add (d, node);
186 	}
187 
188 out:
189 	if (nd != NULL) {
190 		return (nd);
191 	} else {
192 		return (d);
193 	}
194 }
195 
196 
197 static void
198 set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name)
199 {
200 	usb_dev_descr_t	*dev_descrp = NULL;	/* device descriptor */
201 	usb_cfg_descr_t	*cfg_descrp = NULL;	/* configuration descriptor */
202 	unsigned char	*rdata = NULL;
203 	char *p;
204 	int i = 0;
205 
206 	hal_device_property_set_int (d, "usb_device.port_number",
207 	    atoi (devfs_path + strlen (devfs_path) -1));
208 
209 	if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-dev-descriptor",
210 	    &rdata) > 0) {
211 		dev_descrp = (usb_dev_descr_t *)rdata;
212 
213 		if (dev_descrp != NULL) {
214 			hal_device_property_set_int (d, "usb_device.device_class",
215 			    dev_descrp->bDeviceClass);
216 			hal_device_property_set_int (d, "usb_device.device_subclass",
217 			    dev_descrp->bDeviceSubClass);
218 			hal_device_property_set_int (d, "usb_device.device_protocol",
219 			    dev_descrp->bDeviceProtocol);
220 		}
221 	}
222 
223 	if (di_prop_lookup_bytes (DDI_DEV_T_ANY, node, "usb-raw-cfg-descriptors",
224 	    &rdata) > 0) {
225 		cfg_descrp = (usb_cfg_descr_t *)(rdata);
226 
227 		if (cfg_descrp != NULL) {
228 			hal_device_property_set_int (d, "usb_device.configuration_value",
229 			    cfg_descrp->bConfigurationValue);
230 			hal_device_property_set_int (d, "usb_device.max_power",
231 			    cfg_descrp->bMaxPower);
232 			hal_device_property_set_int (d, "usb_device.num_interfaces",
233 			    cfg_descrp->bNumInterfaces);
234 			hal_device_property_set_bool (d, "usb_device.can_wake_up",
235 			    (cfg_descrp->bmAttributes & 0x20) ? TRUE : FALSE);
236 			hal_device_property_set_bool (d, "usb_device.is_self_powered",
237 			    (cfg_descrp->bmAttributes & 0x40) ? TRUE : FALSE);
238 		}
239 	}
240 
241 	/* get the node's usb tree level by counting hub numbers */
242 	do {
243 		if (p = strstr (devfs_path, "/hub@")) {
244 			devfs_path = p + strlen ("/hub@");
245 			i ++;
246 		}
247 	} while (p != NULL);
248 
249 	if ((driver_name != NULL) && (strcmp (driver_name, "hubd") == 0) && (i > 0))
250 		i --;
251 
252 	hal_device_property_set_int (d, "usb_device.level_number", i);
253 }
254 
255 
256 static usb_if_descr_t *
257 parse_usb_if_descr(di_node_t node, int ifnum)
258 {
259 	unsigned char	*rdata = NULL;
260 	usb_if_descr_t	*if_descrp=NULL;	/* interface descriptor */
261 	di_node_t tmp_node = DI_NODE_NIL;
262 	uint8_t num, length, type;
263 	int rlen;
264 	gchar *devpath = NULL;
265 
266 	if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, node,
267 	     "usb-raw-cfg-descriptors", &rdata)) < 0) {
268 
269 		char *p;
270 		int i;
271 
272 		if ((devpath = di_devfs_path (node)) == NULL)
273 			goto out;
274 
275 		/* Look up its parent that may be a USB IA or USB mid. */
276 		for (i = 0; i < 2; i++) {
277 			p = strrchr (devpath, '/');
278 			if (p == NULL)
279 				goto out;
280 			*p = '\0';
281 
282 			if ((tmp_node = di_init (devpath, DINFOCPYALL)) == DI_NODE_NIL)
283 				goto out;
284 
285 			if ((rlen = di_prop_lookup_bytes (DDI_DEV_T_ANY, tmp_node,
286 			     "usb-raw-cfg-descriptors", &rdata)) > 0)
287 				break;
288 
289 			di_fini (tmp_node);
290 		}
291 	}
292 
293 	if (rdata == NULL)
294 		goto out;
295 
296 	do {
297 		length = (uint8_t)*rdata;
298 		type = (uint8_t)*(rdata + 1);
299 		if (type == USB_DESCR_TYPE_IF) {
300 			num = (uint8_t)*(rdata + 2);
301 			if (num == ifnum) {
302 				if_descrp = (usb_if_descr_t *)rdata;
303 				break;
304 			}
305 		}
306 		rdata += length;
307 		rlen -= length;
308 	} while ((length > 0 ) && (rlen > 0));
309 
310 out:
311 	if (devpath != NULL)
312 		di_devfs_path_free (devpath);
313 	if (tmp_node != DI_NODE_NIL)
314 		di_fini (tmp_node);
315 	return (if_descrp);
316 }
317 
318 
319 static HalDevice *
320 devinfo_usb_if_add(HalDevice *parent, di_node_t node, gchar *devfs_path,
321 		   gchar *if_devfs_path, int ifnum)
322 {
323 	HalDevice	*d = NULL;
324 	char		udi[HAL_PATH_MAX];
325 	const char	*parent_info;
326 	usb_if_descr_t	*if_descrp=NULL;	/* interface descriptor */
327 
328 	d = hal_device_new ();
329 
330 	devinfo_set_default_properties (d, parent, node, if_devfs_path);
331 
332 	/* Set the existed physical device path. */
333 	hal_device_property_set_string (d, "solaris.devfs_path", devfs_path);
334 	hal_device_property_set_string (d, "info.subsystem", "usb");
335 	hal_device_property_set_string (d, "info.product", "USB Device Interface");
336 
337 	/* Set usb interface properties to interface node. */
338 	if (strstr (if_devfs_path, ":ia") == NULL) {
339 		if_descrp = parse_usb_if_descr (node, ifnum);
340 
341 		if (if_descrp != NULL) {
342 			hal_device_property_set_int (d, "usb.interface.class",
343 			    if_descrp->bInterfaceClass);
344 			hal_device_property_set_int (d, "usb.interface.subclass",
345 			    if_descrp->bInterfaceSubClass);
346 			hal_device_property_set_int (d, "usb.interface.protocol",
347 			    if_descrp->bInterfaceProtocol);
348 			hal_device_property_set_int (d, "usb.interface.number",
349 			    if_descrp->bInterfaceNumber);
350 		}
351 	}
352 
353 	/* copy parent's usb_device.* properties */
354 	parent_info = hal_device_property_get_string (parent, "info.subsystem");
355 	if (parent_info != NULL) {
356 		if (strcmp (parent_info, "usb_device") == 0) {
357 			hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device.");
358 		} else if (strcmp (parent_info, "usb") == 0) {
359 			/* for the case that the parent is IA node */
360 			hal_device_merge_with_rewrite (d, parent, "usb.", "usb.");
361 		}
362 	}
363 
364 	devinfo_add_enqueue (d, devfs_path, &devinfo_usb_handler);
365 
366 	/* add to TDL so preprobing callouts and prober can access it */
367 	hal_device_store_add (hald_get_tdl (), d);
368 
369 	return (d);
370 }
371 
372 
373 static void
374 get_dev_link_path(di_node_t node, char *nodetype, char *re, char **devlink, char **minor_path)
375 {
376 	di_devlink_handle_t devlink_hdl;
377 	int	major;
378 	di_minor_t minor;
379 	dev_t	devt;
380 
381 	*devlink = NULL;
382 	*minor_path = NULL;
383 
384 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
385 		printf("di_devlink_init() failed\n");
386 		return;
387 	}
388 
389 	major = di_driver_major(node);
390 	minor = DI_MINOR_NIL;
391 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
392 		devt = di_minor_devt(minor);
393 		if (major != major(devt)) {
394 			continue;
395 		}
396 
397 		if (di_minor_type(minor) != DDM_MINOR) {
398 			continue;
399 		}
400 
401 		if ((*minor_path = di_devfs_minor_path(minor)) == NULL) {
402 			continue;
403 		}
404 
405 		if ((strcmp (di_minor_nodetype(minor), nodetype) == 0) &&
406 		    ((*devlink = get_devlink(devlink_hdl, re, *minor_path)) != NULL)) {
407 			break;
408 		}
409 		di_devfs_path_free (*minor_path);
410 		*minor_path = NULL;
411 	}
412 	di_devlink_fini (&devlink_hdl);
413 }
414 
415 static HalDevice *
416 devinfo_usb_scsa2usb_add(HalDevice *usbd, di_node_t node)
417 {
418 	HalDevice *d = NULL;
419 	di_devlink_handle_t devlink_hdl;
420 	int	major;
421 	di_minor_t minor;
422 	dev_t	devt;
423 	char	*minor_path = NULL;
424 	char	*devlink = NULL;
425 	char	udi[HAL_PATH_MAX];
426 
427 	get_dev_link_path(node, "ddi_ctl:devctl:scsi", NULL,  &devlink, &minor_path);
428 
429 	if ((devlink == NULL) || (minor_path == NULL)) {
430 		goto out;
431 	}
432 
433 	d = hal_device_new ();
434 
435 	devinfo_set_default_properties (d, usbd, node, minor_path);
436 	hal_device_property_set_string (d, "scsi_host.solaris.device", devlink);
437 	hal_device_property_set_string (d, "info.category", "scsi_host");
438 	hal_device_property_set_int (d, "scsi_host.host", 0);
439 
440 	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
441 	    "%s/scsi_host%d", hal_device_get_udi (usbd),
442 	    hal_device_property_get_int (d, "scsi_host.host"));
443 	hal_device_set_udi (d, udi);
444 	hal_device_property_set_string (d, "info.udi", udi);
445 	hal_device_property_set_string (d, "info.product", "SCSI Host Adapter");
446 
447 	devinfo_add_enqueue (d, minor_path, &devinfo_usb_handler);
448 
449 out:
450 	if (devlink) {
451 		free(devlink);
452 	}
453 	if (minor_path) {
454 		di_devfs_path_free (minor_path);
455 	}
456 
457 	return (d);
458 }
459 
460 static HalDevice *
461 devinfo_usb_printer_add(HalDevice *parent, di_node_t node)
462 {
463 	HalDevice *d = NULL;
464 	char	udi[HAL_PATH_MAX];
465 	char *s;
466 	char *devlink = NULL, *minor_path = NULL;
467 
468 	get_dev_link_path(node, "ddi_printer", "printers/.+", &devlink, &minor_path);
469 
470 	if ((devlink == NULL) || (minor_path == NULL)) {
471 		goto out;
472 	}
473 
474 	d = hal_device_new ();
475 
476 	devinfo_set_default_properties (d, parent, node, minor_path);
477 	hal_device_property_set_string (d, "info.category", "printer");
478 	hal_device_add_capability (d, "printer");
479 
480 	/* copy parent's usb_device.* properties */
481 	hal_device_merge_with_rewrite (d, parent, "usb.", "usb_device.");
482 
483 	/* add printer properties */
484 	hal_device_property_set_string (d, "printer.device", devlink);
485 	PROP_STR(d, node, s, "usb-vendor-name", "printer.vendor");
486 	PROP_STR(d, node, s, "usb-product-name", "printer.product");
487 	PROP_STR(d, node, s, "usb-serialno", "printer.serial");
488 
489 	devinfo_add_enqueue (d, minor_path, &devinfo_usb_printer_handler);
490 
491 out:
492 	if (devlink) {
493 		free(devlink);
494 	}
495 	if (minor_path) {
496 		di_devfs_path_free (minor_path);
497 	}
498 
499 	return (d);
500 }
501 
502 const gchar *
503 devinfo_printer_prnio_get_prober (HalDevice *d, int *timeout)
504 {
505 	*timeout = 5 * 1000;	/* 5 second timeout */
506 	return ("hald-probe-printer");
507 }
508