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