1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2019, Joyent, Inc.
14  */
15 
16 /*
17  * The purpose of this module is to build topology information for USB devices.
18  * USB devices are more complicated to build topology information for, as there
19  * are multiple sources of information needed to correctly understand the
20  * topology, and the way they present themselves is not always straightforward.
21  *
22  * We enumerate two different types of devices:
23  *
24  *   o USB ports
25  *   o USB devices
26  *
27  * A USB port represents a logical port, while a USB device represents an actual
28  * device that's been plugged in. If a device is a hub, then we'll enumerate
29  * that device as well.
30  *
31  * Now, some basics. There are several different USB controllers that exist in
32  * the system. Some are part of the chipset, while others may be present via
33  * add-on cards. The system interfaces initially with USB devices through a host
34  * controller. Prior to USB 3.0/xhci, a single controller only supported a
35  * single protocol. With USB 3.0, it is possible for a port to share wiring with
36  * both USB 2.0 devices and USB 3.0 devices. However, to the host controller
37  * this appears as two different logical ports.
38  *
39  * To make matters worse, during the transition to USB 3, the ports that were
40  * controlled could be routed to and from a USB 2 controller to a USB 3
41  * controller. This means that there are a lot of ways for ports to overlap.
42  *
43  * In the first case, controllers define a way to perform this mapping by
44  * leveraging ACPI information. Of course, this only helps us if the platform
45  * provides ACPI information, which it may not. When we do know that two ports
46  * are actually the same port, either because of ACPI or because of a
47  * product-specific mapping file, then we'll use that to say two ports are the
48  * same. Otherwise, we'll enumerate them as two separate logical ports.
49  *
50  * To perform the actual enumeration, the first time we're asked to enumerate a
51  * node, we go through and put together an entire picture of all of the USB
52  * devices in the system. This is done so we can make sure to enumerate devices
53  * under specific devices.  The actual topology is determined in a few different
54  * passes.
55  *
56  * Before we walk any trees, we look to see if we have a topo USB metadata file
57  * and if present, load it. However, we do not apply any information from it.
58  *
59  * The first pass uses the devinfo tree to determine all of the USB controllers
60  * and devices that are in the system. We use properties in the devices tree to
61  * identify whether items are a root hub. When a root hub is found, we walk all
62  * of its children and make a note of all of the logical ports under it.
63  *
64  * Next, we walk the information provided by ACPI to try and reduplicate
65  * information about the ports on the system. If the USB topology metadata tells
66  * us that we should not skip ACPI, then we use it. This is done by walking the
67  * /devices/fw tree, looking for USB nodes and then linking them to their
68  * corresponding entries found from the first devinfo walk.
69  *
70  * Finally, we go back and apply metadata to ports that match.
71  *
72  *
73  * To logically keep track of all of this, we have several different structures:
74  *
75  *  topo_usb_controller_t  - Represents a physical controller.
76  *  topo_usb_port_t	   - Represents a physical port. This is a synthetic
77  *			     construct that we put together based on ACPI
78  *			     information.
79  *  topo_usb_lport_t	   - Represents a logical port. This is what the OS
80  *			     actually detects and sees. Each logical port
81  *			     belongs to a corresponding topo_usb_port_t.
82  *  topo_usb_t		   - Represents the overall topology enumeration state.
83  *
84  *
85  * This topo module is invoked at three different points by the surrounding code
86  * and logic. Specifically:
87  *
88  *   * Dynamically by the pcibus enumerator when we encounter PCI add on cards
89  *     which are present in a physical slot. Traditional chipset devices are not
90  *     considered a part of this.
91  *
92  *   * Statically under the motherboard. All ports that don't belong to a PCI
93  *     device are assumed to belong under the motherboard, unless a
94  *     platform-specific topology map maps them under the chassis.
95  *
96  *   * Statically under the chassis. Ports are only placed under the chassis if
97  *     a platform-specific topology file indicates that the port is a part of
98  *     the chassis.
99  */
100 
101 #include <libdevinfo.h>
102 #include <strings.h>
103 #include <sys/types.h>
104 #include <sys/stat.h>
105 #include <fcntl.h>
106 #include <dirent.h>
107 #include <sys/debug.h>
108 #include <unistd.h>
109 
110 #include <sys/fm/protocol.h>
111 #include <fm/topo_mod.h>
112 #include <fm/topo_list.h>
113 #include <fm/topo_method.h>
114 
115 #include <topo_port.h>
116 
117 #include "topo_usb.h"
118 #include "topo_usb_int.h"
119 
120 typedef enum topo_usb_type {
121 	TOPO_USB_PCI,
122 	TOPO_USB_MOBO,
123 	TOPO_USB_CHASSIS
124 } topo_usb_type_t;
125 
126 typedef enum topo_usb_cdrv {
127 	TOPO_USB_D_UNKNOWN,
128 	TOPO_USB_D_UHCI,
129 	TOPO_USB_D_OHCI,
130 	TOPO_USB_D_EHCI,
131 	TOPO_USB_D_XHCI
132 } topo_usb_cdrv_t;
133 
134 typedef enum topo_usb_protocol {
135 	TOPO_USB_P_UNKNOWN,
136 	TOPO_USB_P_1x,
137 	TOPO_USB_P_20,
138 	TOPO_USB_P_30,
139 	TOPO_USB_P_31
140 } topo_usb_protocol_t;
141 
142 typedef enum topo_usb_port_connected {
143 	TOPO_USB_C_UNKNOWN,
144 	TOPO_USB_C_DISCONNECTED,
145 	TOPO_USB_C_CONNECTED
146 } topo_usb_port_connected_t;
147 
148 typedef struct topo_usb_port {
149 	topo_list_t	tup_link;
150 	uint_t		tup_nlports;
151 	topo_list_t	tup_lports;
152 	boolean_t	tup_pld_valid;
153 	acpi_pld_info_t	tup_pld;
154 	uint_t		tup_port_type;
155 	topo_usb_port_connected_t	tup_port_connected;
156 	topo_usb_meta_port_t	*tup_meta;
157 } topo_usb_port_t;
158 
159 typedef struct topo_usb_lport {
160 	topo_list_t		tul_link;
161 	uint_t			tul_portno;
162 	topo_usb_protocol_t	tul_protocol;
163 	di_node_t		tul_device;
164 	di_node_t		tul_acpi_device;
165 	topo_usb_port_t		*tul_port;
166 	uint_t			tul_nhubd_ports;
167 	uint_t			tul_nports;
168 	topo_list_t		tul_ports;
169 	char			tul_name[PATH_MAX];
170 	const char		*tul_acpi_name;
171 } topo_usb_lport_t;
172 
173 typedef struct topo_usb_controller {
174 	topo_list_t	tuc_link;
175 	di_node_t	tuc_devinfo;
176 	char		*tuc_path;
177 	char		*tuc_acpi_path;
178 	char		tuc_name[PATH_MAX];
179 	topo_usb_cdrv_t	tuc_driver;
180 	/*
181 	 * Number of actual ports we've created (some of the logical ports are
182 	 * deduped).
183 	 */
184 	uint_t		tuc_nports;
185 	topo_list_t	tuc_ports;
186 	/*
187 	 * Total number of logical ports we expect to exist on this controller.
188 	 * This may be greater than the number of actual ports we've created
189 	 * under it because some physical ports represent more than one logical
190 	 * port (xhci with USB2/3).
191 	 */
192 	uint_t		tuc_nhubd_ports;
193 	/*
194 	 * Keep track of port number and offset information. This is only done
195 	 * for xhci.
196 	 */
197 	uint_t		tuc_nusb20;
198 	uint_t		tuc_fusb20;
199 	uint_t		tuc_nusb30;
200 	uint_t		tuc_fusb30;
201 	uint_t		tuc_nusb31;
202 	uint_t		tuc_fusb31;
203 	boolean_t	tuc_enumed;
204 } topo_usb_controller_t;
205 
206 typedef struct topo_usb {
207 	topo_list_t	tu_controllers;
208 	boolean_t	tu_enum_done;
209 	di_node_t	tu_devinfo;
210 	topo_list_t	tu_metadata;
211 	topo_usb_meta_flags_t	tu_meta_flags;
212 	topo_list_t	tu_chassis_ports;
213 	uint_t		tu_nchassis_ports;
214 } topo_usb_t;
215 
216 typedef struct topo_usb_devcfg_arg {
217 	topo_usb_t	*tda_usb;
218 	topo_mod_t	*tda_mod;
219 	boolean_t	tda_fatal;
220 } topo_usb_devcfg_arg_t;
221 
222 static const topo_pgroup_info_t topo_usb_port_pgroup = {
223 	TOPO_PGROUP_USB_PORT,
224 	TOPO_STABILITY_PRIVATE,
225 	TOPO_STABILITY_PRIVATE,
226 	1
227 };
228 
229 static const topo_pgroup_info_t topo_io_pgroup = {
230 	TOPO_PGROUP_IO,
231 	TOPO_STABILITY_PRIVATE,
232 	TOPO_STABILITY_PRIVATE,
233 	1
234 };
235 
236 static const topo_pgroup_info_t topo_binding_pgroup = {
237 	TOPO_PGROUP_BINDING,
238 	TOPO_STABILITY_PRIVATE,
239 	TOPO_STABILITY_PRIVATE,
240 	1
241 };
242 
243 static const topo_pgroup_info_t topo_usb_props_pgroup = {
244 	TOPO_PGROUP_USB_PROPS,
245 	TOPO_STABILITY_PRIVATE,
246 	TOPO_STABILITY_PRIVATE,
247 	1
248 };
249 
250 /* Required forwards */
251 static int topo_usb_enum_device(topo_mod_t *, tnode_t *, topo_usb_port_t *);
252 
253 /*
254  * Defines the maximum number of USB ports that can exist. Ports are basically
255  * defined by a uint8_t, meaning that we can go up to UINT8_MAX inclusively.
256  */
257 #define	USB_TOPO_PORT_MAX	256
258 
259 /*
260  * Default value to indicate that a USB port has no valid type.
261  */
262 #define	USB_TOPO_PORT_TYPE_DEFAULT	0xff
263 
264 /*
265  * These come from the ACPI 6.2 / Table 9-290 UPC Return Package Values.
266  */
267 static const char *
topo_usb_port_type_to_string(int type)268 topo_usb_port_type_to_string(int type)
269 {
270 	switch (type) {
271 	case 0x00:
272 		return ("Type A connector");
273 	case 0x01:
274 		return ("Mini-AB connector");
275 	case 0x02:
276 		return ("ExpressCard");
277 	case 0x03:
278 		return ("USB 3 Standard-A connector");
279 	case 0x04:
280 		return ("USB 3 Standard-B connector");
281 	case 0x05:
282 		return ("USB 3 Micro-B connector");
283 	case 0x06:
284 		return ("USB 3 Micro-AB connector");
285 	case 0x07:
286 		return ("USB 3 Power-B connector");
287 	case 0x08:
288 		return ("Type C connector - USB2-only");
289 	case 0x09:
290 		return ("Type C connector - USB2 and SS with Switch");
291 	case 0x0A:
292 		return ("Type C connector - USB2 and SS without Switch");
293 	/* 0x0B->0xFE are reserved. Treat them like 0xFF */
294 	case 0xFF:
295 	default:
296 		return ("Unknown");
297 	}
298 }
299 
300 /*
301  * Searches the list of ports at a given layer (not recursively) for the
302  * specific port id.
303  */
304 static topo_usb_lport_t *
topo_usb_lport_find(topo_list_t * plist,uint_t logid)305 topo_usb_lport_find(topo_list_t *plist, uint_t logid)
306 {
307 	topo_usb_port_t *p;
308 
309 	for (p = topo_list_next(plist); p != NULL; p = topo_list_next(p)) {
310 		topo_usb_lport_t *l;
311 
312 		for (l = topo_list_next(&p->tup_lports); l != NULL;
313 		    l = topo_list_next(l)) {
314 			if (l->tul_portno == logid)
315 				return (l);
316 		}
317 	}
318 	return (NULL);
319 }
320 
321 /*
322  * Create an instance of a controller and seed the basic information.
323  */
324 static topo_usb_controller_t *
topo_usb_controller_create(topo_mod_t * mod,topo_usb_t * usb,di_node_t node)325 topo_usb_controller_create(topo_mod_t *mod, topo_usb_t *usb, di_node_t node)
326 {
327 	int *pcount, inst;
328 	char *drvname, *acpi;
329 	topo_usb_controller_t *c;
330 
331 	/*
332 	 * If we can't get the port count or the driver, then this node is
333 	 * uninteresting.
334 	 */
335 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-port-count",
336 	    &pcount) != 1) {
337 		return (NULL);
338 	}
339 
340 	if ((drvname = di_driver_name(node)) == NULL ||
341 	    (inst = di_instance(node) == -1))
342 		return (NULL);
343 
344 	if ((c = topo_mod_zalloc(mod, sizeof (topo_usb_controller_t))) ==
345 	    NULL || *pcount <= 0) {
346 		return (NULL);
347 	}
348 
349 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "acpi-namespace",
350 	    &acpi) == 1) {
351 		c->tuc_acpi_path = acpi;
352 	}
353 
354 	c->tuc_nhubd_ports = (uint_t)*pcount;
355 	c->tuc_devinfo = node;
356 	c->tuc_path = di_devfs_path(node);
357 	(void) snprintf(c->tuc_name, sizeof (c->tuc_name), "%s%d", drvname,
358 	    inst);
359 	if (strcmp(drvname, "xhci") == 0) {
360 		int *p;
361 
362 		c->tuc_driver = TOPO_USB_D_XHCI;
363 
364 		/*
365 		 * Grab the properties that we need so we can better do a port
366 		 * speed mapping.
367 		 */
368 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
369 		    "usb2.0-port-count", &p) == 1 && *p > 0) {
370 			c->tuc_nusb20 = (uint_t)*p;
371 		}
372 
373 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
374 		    "usb2.0-first-port", &p) == 1 && *p > 0) {
375 			c->tuc_fusb20 = (uint_t)*p;
376 		}
377 
378 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
379 		    "usb3.0-port-count", &p) == 1 && *p > 0) {
380 			c->tuc_nusb30 = (uint_t)*p;
381 		}
382 
383 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
384 		    "usb3.0-first-port", &p) == 1 && *p > 0) {
385 			c->tuc_fusb30 = (uint_t)*p;
386 		}
387 
388 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
389 		    "usb3.1-port-count", &p) == 1 && *p > 0) {
390 			c->tuc_nusb31 = (uint_t)*p;
391 		}
392 
393 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
394 		    "usb3.1-first-port", &p) == 1 && *p > 0) {
395 			c->tuc_fusb31 = (uint_t)*p;
396 		}
397 	} else if (strcmp(drvname, "ehci") == 0) {
398 		c->tuc_driver = TOPO_USB_D_EHCI;
399 	} else if (strcmp(drvname, "uhci") == 0) {
400 		c->tuc_driver = TOPO_USB_D_UHCI;
401 	} else if (strcmp(drvname, "ohci") == 0) {
402 		c->tuc_driver = TOPO_USB_D_OHCI;
403 	} else {
404 		c->tuc_driver = TOPO_USB_D_UNKNOWN;
405 	}
406 	topo_list_append(&usb->tu_controllers, c);
407 	topo_mod_dprintf(mod, "created new USB controller at %s", c->tuc_path);
408 
409 	return (c);
410 }
411 
412 /*
413  * Process this port and any others that might exist.
414  */
415 static boolean_t
topo_usb_gather_acpi_port(topo_mod_t * mod,topo_usb_t * usb,topo_list_t * plist,uint_t * nports,topo_usb_controller_t * tuc,di_node_t portinfo)416 topo_usb_gather_acpi_port(topo_mod_t *mod, topo_usb_t *usb, topo_list_t *plist,
417     uint_t *nports, topo_usb_controller_t *tuc, di_node_t portinfo)
418 {
419 	int64_t *portno;
420 	uchar_t *loc;
421 	int loclen, *type;
422 	char *acpi;
423 	acpi_pld_info_t pld;
424 	boolean_t pld_valid = B_FALSE;
425 	topo_usb_port_t *port = NULL;
426 	topo_usb_lport_t *lport;
427 	di_node_t child;
428 
429 	/*
430 	 * Get the port's address, it's a required value. Because this is coming
431 	 * from firmware, we cannot trust the port's value to be correct.
432 	 */
433 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, portinfo, "acpi-address",
434 	    &portno) != 1 || *portno < 1 || *portno >= USB_TOPO_PORT_MAX) {
435 		return (B_FALSE);
436 	}
437 
438 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, portinfo, "acpi-namespace",
439 	    &acpi) != 1) {
440 		return (B_FALSE);
441 	}
442 
443 	/*
444 	 * Check to see if we have any ACPI location information. If we do, we
445 	 * can decode it.
446 	 */
447 	if ((loclen = di_prop_lookup_bytes(DDI_DEV_T_ANY, portinfo,
448 	    "acpi-physical-location", &loc)) >= ACPI_PLD_REV1_BUFFER_SIZE &&
449 	    usbtopo_decode_pld(loc, loclen, &pld)) {
450 		pld_valid = B_TRUE;
451 	}
452 
453 	/*
454 	 * Find the corresponding lport. If this node doesn't happen to match
455 	 * something we've enumerated from the hub. Warn about that fact and
456 	 * consider this bad data.
457 	 */
458 	lport = topo_usb_lport_find(plist, (uint_t)*portno);
459 	if (lport == NULL) {
460 		topo_mod_dprintf(mod, "failed to find physical usb port for "
461 		    "%s/%u", acpi, (uint_t)*portno);
462 		return (B_TRUE);
463 	}
464 
465 	if (lport->tul_acpi_device != DI_NODE_NIL) {
466 		topo_mod_dprintf(mod, "logical port already bound to %s, not "
467 		    "binding to %s", lport->tul_acpi_name, acpi);
468 		return (B_FALSE);
469 	}
470 
471 	lport->tul_acpi_device = portinfo;
472 	lport->tul_acpi_name = acpi;
473 	port = lport->tul_port;
474 
475 	if (pld_valid) {
476 		port->tup_pld_valid = B_TRUE;
477 		port->tup_pld = pld;
478 	}
479 
480 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, portinfo, "usb-port-type",
481 	    &type) == 1 && *type >= 0) {
482 		port->tup_port_type = *type;
483 	} else {
484 		port->tup_port_type = USB_TOPO_PORT_TYPE_DEFAULT;
485 	}
486 
487 	if (di_prop_find(DDI_DEV_T_ANY, portinfo,
488 	    "usb-port-connectable") != DI_PROP_NIL) {
489 		port->tup_port_connected = TOPO_USB_C_CONNECTED;
490 	} else {
491 		port->tup_port_connected = TOPO_USB_C_DISCONNECTED;
492 	}
493 
494 	for (child = di_child_node(portinfo); child != NULL;
495 	    child = di_sibling_node(child)) {
496 		const char *pname;
497 
498 		pname = di_node_name(child);
499 		if (pname == NULL || strcmp(pname, "port") != 0) {
500 			continue;
501 		}
502 
503 		if (!topo_usb_gather_acpi_port(mod, usb, &lport->tul_ports,
504 		    &lport->tul_nports, tuc, child)) {
505 			return (B_FALSE);
506 		}
507 	}
508 
509 	topo_mod_dprintf(mod, "discovered %u ACPI usb child ports",
510 	    lport->tul_nports);
511 
512 	return (B_TRUE);
513 }
514 
515 /*
516  * First, bootstrap all of our information by reading the ACPI information
517  * exposed in the devinfo tree. All of the nodes we care about will be under
518  * /fw/sb@XX/usbrootub@YYY/port@ZZZ
519  */
520 static boolean_t
topo_usb_gather_acpi(topo_mod_t * mod,topo_usb_t * usb)521 topo_usb_gather_acpi(topo_mod_t *mod, topo_usb_t *usb)
522 {
523 	di_node_t fwroot, sbnode;
524 
525 	/*
526 	 * If we can't find the /fw node, that's fine. We may not have any ACPI
527 	 * information on the system.
528 	 */
529 	fwroot = di_lookup_node(usb->tu_devinfo, "/fw");
530 	if (fwroot == DI_NODE_NIL)
531 		return (B_TRUE);
532 
533 	for (sbnode = di_child_node(fwroot); sbnode != DI_NODE_NIL;
534 	    sbnode = di_sibling_node(sbnode)) {
535 		const char *sbname;
536 		di_node_t hub;
537 
538 		sbname = di_node_name(sbnode);
539 		if (sbname == NULL || strcmp(sbname, "sb") != 0) {
540 			continue;
541 		}
542 
543 		for (hub = di_child_node(sbnode); hub != DI_NODE_NIL;
544 		    hub = di_sibling_node(hub)) {
545 			const char *hubname;
546 			char *acpi;
547 			topo_usb_controller_t *tuc;
548 			di_node_t port;
549 
550 			hubname = di_node_name(hub);
551 			if (hubname == NULL ||
552 			    strcmp(hubname, "usbroothub") != 0) {
553 				continue;
554 			}
555 
556 			if (di_prop_lookup_strings(DDI_DEV_T_ANY, hub,
557 			    "acpi-controller-name", &acpi) != 1) {
558 				continue;
559 			}
560 
561 			for (tuc = topo_list_next(&usb->tu_controllers);
562 			    tuc != NULL;
563 			    tuc = topo_list_next(tuc)) {
564 				if (tuc->tuc_acpi_path != NULL &&
565 				    strcmp(acpi, tuc->tuc_acpi_path) == 0)
566 					break;
567 			}
568 
569 			if (tuc == NULL) {
570 				topo_mod_dprintf(mod, "failed to find USB "
571 				    "controller for ACPI path %s", acpi);
572 				continue;
573 			}
574 
575 			for (port = di_child_node(hub); port != NULL;
576 			    port = di_sibling_node(port)) {
577 				const char *pname;
578 
579 				pname = di_node_name(port);
580 				if (pname == NULL ||
581 				    strcmp(pname, "port") != 0) {
582 					continue;
583 				}
584 
585 				if (!topo_usb_gather_acpi_port(mod, usb,
586 				    &tuc->tuc_ports, &tuc->tuc_nports, tuc,
587 				    port)) {
588 					return (B_FALSE);
589 				}
590 			}
591 
592 			topo_mod_dprintf(mod, "found ACPI usb controller %s "
593 			    "with %d top-level ports", tuc->tuc_path,
594 			    tuc->tuc_nports);
595 		}
596 	}
597 
598 	return (B_TRUE);
599 }
600 
601 static topo_usb_port_t *
topo_usb_port_create(topo_mod_t * mod,uint_t portno,const char * parent,char sep)602 topo_usb_port_create(topo_mod_t *mod, uint_t portno, const char *parent,
603     char sep)
604 {
605 	topo_usb_lport_t *l;
606 	topo_usb_port_t *p;
607 
608 	if ((l = topo_mod_zalloc(mod, sizeof (topo_usb_lport_t))) == NULL) {
609 		return (NULL);
610 	}
611 	l->tul_portno = portno;
612 	if (snprintf(l->tul_name, sizeof (l->tul_name), "%s%c%u", parent, sep,
613 	    portno) >= sizeof (l->tul_name)) {
614 		topo_mod_free(mod, l, sizeof (topo_usb_lport_t));
615 		return (NULL);
616 	}
617 
618 	if ((p = topo_mod_zalloc(mod, sizeof (topo_usb_port_t))) == NULL) {
619 		topo_mod_free(mod, l, sizeof (topo_usb_lport_t));
620 		return (NULL);
621 	}
622 	l->tul_port = p;
623 	p->tup_port_type = USB_TOPO_PORT_TYPE_DEFAULT;
624 	topo_list_append(&p->tup_lports, l);
625 	p->tup_nlports++;
626 
627 	return (p);
628 }
629 
630 /*
631  * Set the protocol of a port that belongs to a root hub.
632  */
633 static void
topo_usb_set_rhub_port_protocol(topo_mod_t * mod,topo_usb_controller_t * tuc,topo_usb_lport_t * lport)634 topo_usb_set_rhub_port_protocol(topo_mod_t *mod, topo_usb_controller_t *tuc,
635     topo_usb_lport_t *lport)
636 {
637 	switch (tuc->tuc_driver) {
638 	case TOPO_USB_D_XHCI:
639 		break;
640 	case TOPO_USB_D_UHCI:
641 	case TOPO_USB_D_OHCI:
642 		lport->tul_protocol = TOPO_USB_P_1x;
643 		return;
644 	case TOPO_USB_D_EHCI:
645 		lport->tul_protocol = TOPO_USB_P_20;
646 		return;
647 	case TOPO_USB_D_UNKNOWN:
648 	default:
649 		lport->tul_protocol = TOPO_USB_P_UNKNOWN;
650 		return;
651 	}
652 
653 	/*
654 	 * The xHCI controller can support multiple different, protocols. It
655 	 * communicates this information to us via devinfo properties. It's
656 	 * possible that a port that is within max ports is not within the range
657 	 * here. If that's the case, we'll set it to unknown.
658 	 */
659 	if (lport->tul_portno >= tuc->tuc_fusb20 &&
660 	    lport->tul_portno < tuc->tuc_fusb20 + tuc->tuc_nusb20) {
661 		lport->tul_protocol = TOPO_USB_P_20;
662 	} else if (lport->tul_portno >= tuc->tuc_fusb30 &&
663 	    lport->tul_portno < tuc->tuc_fusb30 + tuc->tuc_nusb30) {
664 		lport->tul_protocol = TOPO_USB_P_30;
665 	} else if (lport->tul_portno >= tuc->tuc_fusb31 &&
666 	    lport->tul_portno < tuc->tuc_fusb31 + tuc->tuc_nusb31) {
667 		lport->tul_protocol = TOPO_USB_P_31;
668 	} else {
669 		lport->tul_protocol = TOPO_USB_P_UNKNOWN;
670 	}
671 }
672 
673 /*
674  * We've found a node on the list. Attempt to find its corresponding port. If we
675  * find a hub, then we will descend further down this part of the tree.
676  */
677 static int
topo_usb_gather_devcfg_port(topo_mod_t * mod,topo_usb_controller_t * c,topo_list_t * plist,di_node_t node)678 topo_usb_gather_devcfg_port(topo_mod_t *mod, topo_usb_controller_t *c,
679     topo_list_t *plist, di_node_t node)
680 {
681 	int *vend, *reg, *nports;
682 	topo_usb_lport_t *l;
683 	char *drvname;
684 
685 	/*
686 	 * Look for the presence of the usb-vendor-id property to determine
687 	 * whether or not this is a usb device node. usba always adds this
688 	 * to the devices that it enumerates.
689 	 */
690 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-vendor-id",
691 	    &vend) != 1) {
692 		topo_mod_dprintf(mod, "failed to find usb-vendor-id property "
693 		    "for child");
694 		return (0);
695 	}
696 
697 	/*
698 	 * For usb-devices, the reg property is one entry long and it has the
699 	 * logical port that the controller sees.
700 	 */
701 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &reg) != 1 ||
702 	    *reg <= 0) {
703 		topo_mod_dprintf(mod, "got bad \"reg\" property");
704 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
705 	}
706 
707 	if ((l = topo_usb_lport_find(plist, (uint_t)*reg)) == NULL) {
708 		topo_mod_dprintf(mod, "failed to find topo_usb_lport_t for "
709 		    "port %d", *reg);
710 		return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
711 	}
712 
713 	l->tul_device = node;
714 
715 	/*
716 	 * Check to see if we have a hub and if so, process it.
717 	 */
718 	if ((drvname = di_driver_name(node)) != NULL &&
719 	    strcmp(drvname, "hubd") == 0 &&
720 	    di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-port-count",
721 	    &nports) == 1 && *nports >= 1) {
722 		di_node_t child;
723 
724 		/*
725 		 * First go through and try and discover and create all the
726 		 * logical ports that exist. It is possible that these ports
727 		 * already exist and that we have ACPI information about them.
728 		 * This would happen when a root port is connected into a set of
729 		 * hubs that are built-in.
730 		 */
731 		l->tul_nhubd_ports = (uint_t)*nports;
732 		for (uint_t i = 1; i <= l->tul_nhubd_ports; i++) {
733 			topo_usb_lport_t *clport;
734 			topo_usb_port_t *cport;
735 
736 			if ((cport = topo_usb_port_create(mod, i, l->tul_name,
737 			    '.')) == NULL) {
738 				return (topo_mod_seterrno(mod, EMOD_NOMEM));
739 			}
740 
741 			clport = topo_list_next(&cport->tup_lports);
742 			topo_list_append(&l->tul_ports, cport);
743 			l->tul_nports++;
744 
745 			clport->tul_protocol = l->tul_protocol;
746 		}
747 
748 		/*
749 		 * Now go through and discover its children.
750 		 */
751 		for (child = di_child_node(node); child != NULL;
752 		    child = di_sibling_node(child)) {
753 			int ret;
754 
755 			if ((ret = topo_usb_gather_devcfg_port(mod, c,
756 			    &l->tul_ports, child)) != 0) {
757 				return (-1);
758 			}
759 		}
760 	}
761 
762 	return (0);
763 }
764 
765 static int
topo_usb_gather_devcfg_cb(di_node_t node,void * arg)766 topo_usb_gather_devcfg_cb(di_node_t node, void *arg)
767 {
768 	uint_t i;
769 	topo_usb_controller_t *tuc;
770 	di_prop_t prop = DI_PROP_NIL;
771 	boolean_t rh = B_FALSE, pc = B_FALSE;
772 	topo_usb_devcfg_arg_t *tda = arg;
773 	topo_usb_t *usb = tda->tda_usb;
774 	topo_mod_t *mod = tda->tda_mod;
775 	di_node_t child;
776 
777 	while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
778 		const char *name = di_prop_name(prop);
779 		int *ports;
780 
781 		if (strcmp(name, "root-hub") == 0 &&
782 		    di_prop_type(prop) == DI_PROP_TYPE_BOOLEAN) {
783 			rh = B_TRUE;
784 		} else if (strcmp(name, "usb-port-count") == 0 &&
785 		    di_prop_ints(prop, &ports) == 1 && *ports > 0 &&
786 		    *ports < USB_TOPO_PORT_MAX) {
787 			pc = B_TRUE;
788 		}
789 	}
790 
791 	if (!rh || !pc)
792 		return (DI_WALK_CONTINUE);
793 
794 	if ((tuc = topo_usb_controller_create(mod, usb, node)) == NULL) {
795 		tda->tda_fatal = B_TRUE;
796 		return (DI_WALK_TERMINATE);
797 	}
798 
799 	/*
800 	 * Check to make sure that every logical port exists at this level and
801 	 * that we have its speed information filled in. If it does not exist,
802 	 * create it.
803 	 */
804 	for (i = 1; i <= tuc->tuc_nhubd_ports; i++) {
805 		topo_usb_lport_t *l;
806 		topo_usb_port_t *p;
807 
808 		topo_mod_dprintf(mod, "attempting to discover lport %u on "
809 		    "controller %s", i, tuc->tuc_path);
810 
811 		if ((p = topo_usb_port_create(mod, i, tuc->tuc_name, '@')) ==
812 		    NULL) {
813 			topo_mod_dprintf(mod, "failed to create "
814 			    "port %u", i);
815 			tda->tda_fatal = B_TRUE;
816 			return (DI_WALK_TERMINATE);
817 		}
818 
819 		topo_list_append(&tuc->tuc_ports, p);
820 		tuc->tuc_nports++;
821 		l = topo_list_next(&p->tup_lports);
822 
823 		topo_usb_set_rhub_port_protocol(mod, tuc, l);
824 	}
825 
826 	for (child = di_child_node(tuc->tuc_devinfo); child != NULL;
827 	    child = di_sibling_node(child)) {
828 		int ret;
829 
830 		if ((ret = topo_usb_gather_devcfg_port(mod, tuc,
831 		    &tuc->tuc_ports, child)) != 0) {
832 			tda->tda_fatal = B_TRUE;
833 			return (DI_WALK_TERMINATE);
834 		}
835 	}
836 
837 	return (DI_WALK_PRUNECHILD);
838 }
839 
840 /*
841  * To find all the controllers in the system, look for device nodes that have
842  * the 'root-hub' property and also a valid usb-port-count property.
843  */
844 static boolean_t
topo_usb_gather_devcfg(topo_mod_t * mod,topo_usb_t * usb)845 topo_usb_gather_devcfg(topo_mod_t *mod, topo_usb_t *usb)
846 {
847 	topo_usb_devcfg_arg_t tda;
848 
849 	tda.tda_usb = usb;
850 	tda.tda_mod = mod;
851 	tda.tda_fatal = B_FALSE;
852 
853 	(void) di_walk_node(usb->tu_devinfo, DI_WALK_CLDFIRST,
854 	    &tda, topo_usb_gather_devcfg_cb);
855 
856 	return (!tda.tda_fatal);
857 }
858 
859 /*
860  * For more information on the matching logic here, see xHCI r1.1 / Appendix D -
861  * Port to Connector Mapping.
862  */
863 static boolean_t
topo_usb_acpi_pld_match(const acpi_pld_info_t * l,const acpi_pld_info_t * r)864 topo_usb_acpi_pld_match(const acpi_pld_info_t *l, const acpi_pld_info_t *r)
865 {
866 	if (l->Panel == r->Panel &&
867 	    l->VerticalPosition == r->VerticalPosition &&
868 	    l->HorizontalPosition == r->HorizontalPosition &&
869 	    l->Shape == r->Shape &&
870 	    l->GroupOrientation == r->GroupOrientation &&
871 	    l->GroupPosition == r->GroupPosition &&
872 	    l->GroupToken == r->GroupToken) {
873 		return (B_TRUE);
874 	}
875 
876 	return (B_FALSE);
877 }
878 
879 typedef boolean_t (*topo_usb_port_match_f)(topo_usb_port_t *, void *);
880 
881 static topo_usb_port_t *
topo_usb_port_match_lport(topo_usb_lport_t * lport,boolean_t remove,topo_usb_port_match_f func,void * arg)882 topo_usb_port_match_lport(topo_usb_lport_t *lport, boolean_t remove,
883     topo_usb_port_match_f func, void *arg)
884 {
885 	topo_usb_port_t *p;
886 
887 	for (p = topo_list_next(&lport->tul_ports); p != NULL;
888 	    p = topo_list_next(p)) {
889 		topo_usb_lport_t *l;
890 		topo_usb_port_t *ret;
891 
892 		if (func(p, arg)) {
893 			if (remove) {
894 				topo_list_delete(&lport->tul_ports, p);
895 				lport->tul_nports--;
896 			}
897 
898 			return (p);
899 		}
900 
901 		for (l = topo_list_next(&p->tup_lports); l != NULL;
902 		    l = topo_list_next(l)) {
903 			if ((ret = topo_usb_port_match_lport(l,
904 			    remove, func, arg)) != NULL) {
905 				return (ret);
906 			}
907 		}
908 	}
909 
910 	return (NULL);
911 }
912 
913 static topo_usb_port_t *
topo_usb_port_match_controller(topo_usb_controller_t * c,boolean_t remove,topo_usb_port_match_f func,void * arg)914 topo_usb_port_match_controller(topo_usb_controller_t *c, boolean_t remove,
915     topo_usb_port_match_f func, void *arg)
916 {
917 	topo_usb_port_t *p;
918 
919 	for (p = topo_list_next(&c->tuc_ports); p != NULL;
920 	    p = topo_list_next(p)) {
921 		topo_usb_lport_t *l;
922 		topo_usb_port_t *ret;
923 
924 		if (func(p, arg)) {
925 			if (remove) {
926 				topo_list_delete(&c->tuc_ports, p);
927 				c->tuc_nports--;
928 			}
929 
930 			return (p);
931 		}
932 
933 		for (l = topo_list_next(&p->tup_lports); l != NULL;
934 		    l = topo_list_next(l)) {
935 			if ((ret = topo_usb_port_match_lport(l,
936 			    remove, func, arg)) != NULL) {
937 				return (ret);
938 			}
939 		}
940 	}
941 
942 	return (NULL);
943 }
944 
945 static topo_usb_port_t *
topo_usb_port_match(topo_usb_t * usb,boolean_t remove,topo_usb_port_match_f func,void * arg)946 topo_usb_port_match(topo_usb_t *usb, boolean_t remove,
947     topo_usb_port_match_f func, void *arg)
948 {
949 	topo_usb_controller_t *c;
950 
951 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
952 	    c = topo_list_next(c)) {
953 		topo_usb_port_t *p;
954 
955 		if ((p = topo_usb_port_match_controller(c, remove, func,
956 		    arg)) != NULL)
957 			return (p);
958 	}
959 	return (NULL);
960 }
961 
962 /*
963  * Merge all of the local ports and information in source, to sink.
964  */
965 static void
topo_usb_port_merge(topo_usb_port_t * sink,topo_usb_port_t * source)966 topo_usb_port_merge(topo_usb_port_t *sink, topo_usb_port_t *source)
967 {
968 	topo_usb_lport_t *l;
969 
970 	while ((l = topo_list_next(&source->tup_lports)) != NULL) {
971 		topo_list_delete(&source->tup_lports, l);
972 		source->tup_nlports--;
973 		topo_list_append(&sink->tup_lports, l);
974 		sink->tup_nlports++;
975 	}
976 
977 	if (sink->tup_port_type == USB_TOPO_PORT_TYPE_DEFAULT) {
978 		sink->tup_port_type = source->tup_port_type;
979 	}
980 
981 	if (sink->tup_port_connected == TOPO_USB_C_UNKNOWN) {
982 		sink->tup_port_connected = source->tup_port_connected;
983 	}
984 }
985 
986 static boolean_t
topo_usb_acpi_port_match(topo_usb_port_t * port,void * arg)987 topo_usb_acpi_port_match(topo_usb_port_t *port, void *arg)
988 {
989 	topo_usb_port_t *target = arg;
990 
991 	return (port != target && port->tup_pld_valid &&
992 	    topo_usb_acpi_pld_match(&port->tup_pld, &target->tup_pld));
993 }
994 
995 /*
996  * Ports on an xhci controller can match up. If we've been told that we should
997  * do so, attempt to perform that match. We only try to find matches in the top
998  * level ports of an xhci controller as that's what's most common on systems,
999  * though we'll search all the descendants.
1000  */
1001 static void
topo_usb_acpi_match(topo_mod_t * mod,topo_usb_controller_t * tuc)1002 topo_usb_acpi_match(topo_mod_t *mod, topo_usb_controller_t *tuc)
1003 {
1004 	topo_usb_port_t *p;
1005 
1006 	for (p = topo_list_next(&tuc->tuc_ports); p != NULL;
1007 	    p = topo_list_next(p)) {
1008 		topo_usb_port_t *match;
1009 
1010 		if ((match = topo_usb_port_match_controller(tuc, B_TRUE,
1011 		    topo_usb_acpi_port_match, p)) != NULL) {
1012 			VERIFY3P(p, !=, match);
1013 			topo_usb_port_merge(p, match);
1014 			topo_mod_free(mod, match, sizeof (topo_usb_port_t));
1015 		}
1016 	}
1017 }
1018 
1019 static boolean_t
topo_usb_metadata_match(topo_usb_port_t * port,void * arg)1020 topo_usb_metadata_match(topo_usb_port_t *port, void *arg)
1021 {
1022 	topo_usb_meta_port_path_t *path = arg;
1023 	topo_usb_lport_t *l;
1024 
1025 	if (path->tmpp_type != TOPO_USB_T_ACPI)
1026 		return (B_FALSE);
1027 
1028 	for (l = topo_list_next(&port->tup_lports); l != NULL;
1029 	    l = topo_list_next(l)) {
1030 		if (l->tul_acpi_name != NULL && strcmp(path->tmpp_path,
1031 		    l->tul_acpi_name) == 0) {
1032 			return (B_TRUE);
1033 		}
1034 	}
1035 
1036 	return (B_FALSE);
1037 }
1038 
1039 /*
1040  * We've found metadata describing the USB ports. We need to now go through and
1041  * try to match that data up to actual nodes.
1042  */
1043 static void
topo_usb_apply_metadata(topo_mod_t * mod,topo_usb_t * usb)1044 topo_usb_apply_metadata(topo_mod_t *mod, topo_usb_t *usb)
1045 {
1046 	topo_usb_meta_port_t *m;
1047 
1048 	for (m = topo_list_next(&usb->tu_metadata); m != NULL;
1049 	    m = topo_list_next(m)) {
1050 		topo_usb_port_t *p, *sink = NULL;
1051 		topo_usb_meta_port_path_t *path;
1052 		boolean_t remove = B_FALSE;
1053 
1054 		/*
1055 		 * If this is a chassis node, we'll remove the port and move it
1056 		 * to the chassis.
1057 		 */
1058 		if (m->tmp_flags & TOPO_USB_F_CHASSIS) {
1059 			remove = B_TRUE;
1060 		}
1061 
1062 		for (path = topo_list_next(&m->tmp_paths); path != NULL;
1063 		    path = topo_list_next(path)) {
1064 			topo_mod_dprintf(mod, "considering metadata path %s",
1065 			    path->tmpp_path);
1066 			if ((p = topo_usb_port_match(usb, remove,
1067 			    topo_usb_metadata_match, path)) == NULL)
1068 				continue;
1069 			topo_mod_dprintf(mod, "matched path to a logical port");
1070 			p->tup_meta = m;
1071 
1072 			/*
1073 			 * Check if we can move this to the Chassis. We should
1074 			 * always do this on the first port in a group. However,
1075 			 * if it's a match candidate, then it will have already
1076 			 * been appended.
1077 			 */
1078 			if ((m->tmp_flags & TOPO_USB_F_CHASSIS) != 0 &&
1079 			    sink == NULL) {
1080 				topo_list_append(&usb->tu_chassis_ports, p);
1081 				usb->tu_nchassis_ports++;
1082 			}
1083 
1084 			if ((usb->tu_meta_flags & TOPO_USB_M_METADATA_MATCH) !=
1085 			    0) {
1086 				if (sink == NULL) {
1087 					sink = p;
1088 					remove = B_TRUE;
1089 				} else {
1090 					VERIFY3P(p, !=, sink);
1091 					topo_usb_port_merge(sink, p);
1092 					topo_mod_free(mod, p,
1093 					    sizeof (topo_usb_port_t));
1094 				}
1095 				continue;
1096 			}
1097 
1098 			break;
1099 		}
1100 
1101 	}
1102 }
1103 
1104 static int
topo_usb_gather(topo_mod_t * mod,topo_usb_t * usb,tnode_t * pnode)1105 topo_usb_gather(topo_mod_t *mod, topo_usb_t *usb, tnode_t *pnode)
1106 {
1107 	int ret;
1108 
1109 	if ((ret = topo_usb_load_metadata(mod, pnode, &usb->tu_metadata,
1110 	    &usb->tu_meta_flags)) != 0) {
1111 		topo_mod_dprintf(mod, "failed to read usb metadata");
1112 		return (-1);
1113 	}
1114 	topo_mod_dprintf(mod, "loaded metadata flags: %d", usb->tu_meta_flags);
1115 
1116 	if (!topo_usb_gather_devcfg(mod, usb)) {
1117 		topo_mod_dprintf(mod, "encountered fatal error while "
1118 		    "gathering physical data");
1119 		return (-1);
1120 	}
1121 
1122 	if ((usb->tu_meta_flags & TOPO_USB_M_NO_ACPI) == 0 &&
1123 	    !topo_usb_gather_acpi(mod, usb)) {
1124 		topo_mod_dprintf(mod, "encountered fatal error while "
1125 		    "gathering ACPI data");
1126 		return (-1);
1127 	}
1128 
1129 	if ((usb->tu_meta_flags & TOPO_USB_M_ACPI_MATCH) != 0) {
1130 		topo_usb_controller_t *c;
1131 
1132 		for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1133 		    c = topo_list_next(c)) {
1134 			if (c->tuc_driver == TOPO_USB_D_XHCI) {
1135 				topo_usb_acpi_match(mod, c);
1136 			}
1137 		}
1138 	}
1139 
1140 	topo_usb_apply_metadata(mod, usb);
1141 
1142 	return (0);
1143 }
1144 
1145 static int
topo_usb_port_properties(topo_mod_t * mod,tnode_t * tn,topo_usb_port_t * port)1146 topo_usb_port_properties(topo_mod_t *mod, tnode_t *tn, topo_usb_port_t *port)
1147 {
1148 	int err;
1149 	char **strs = NULL;
1150 	uint_t i;
1151 	topo_usb_lport_t *l;
1152 	char *label;
1153 	const char *ptype;
1154 	size_t strlen;
1155 
1156 	strlen = sizeof (char *) * MAX(port->tup_nlports,
1157 	    TOPO_PROP_USB_PORT_NATTRS);
1158 	if ((strs = topo_mod_zalloc(mod, strlen)) == NULL) {
1159 		return (-1);
1160 	}
1161 
1162 	label = NULL;
1163 	if (port->tup_meta != NULL) {
1164 		label = port->tup_meta->tmp_label;
1165 	}
1166 
1167 	if (port->tup_meta != NULL && port->tup_meta->tmp_port_type !=
1168 	    USB_TOPO_PORT_TYPE_DEFAULT) {
1169 		ptype =
1170 		    topo_usb_port_type_to_string(port->tup_meta->tmp_port_type);
1171 	} else {
1172 		ptype = topo_usb_port_type_to_string(port->tup_port_type);
1173 	}
1174 
1175 	if (topo_pgroup_create(tn, &topo_usb_port_pgroup, &err) != 0) {
1176 		topo_mod_dprintf(mod, "failed to create property group %s: "
1177 		    "%s\n", TOPO_PGROUP_USB_PORT, topo_strerror(err));
1178 		goto error;
1179 
1180 	}
1181 
1182 	if (label != NULL && topo_node_label_set(tn, label, &err) != 0) {
1183 		topo_mod_dprintf(mod, "failed to set label on port: %s",
1184 		    topo_strerror(err));
1185 		goto error;
1186 	}
1187 
1188 	if (ptype != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PORT,
1189 	    TOPO_PROP_USB_PORT_TYPE, TOPO_PROP_IMMUTABLE, ptype, &err) != 0) {
1190 		topo_mod_dprintf(mod, "failed to set %s property: %s",
1191 		    TOPO_PROP_USB_PORT_TYPE, topo_strerror(err));
1192 		goto error;
1193 	}
1194 
1195 	for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL;
1196 	    l = topo_list_next(l)) {
1197 		char *vers;
1198 		int j;
1199 
1200 		switch (l->tul_protocol) {
1201 		case TOPO_USB_P_1x:
1202 			vers = "1.x";
1203 			break;
1204 		case TOPO_USB_P_20:
1205 			vers = "2.0";
1206 			break;
1207 		case TOPO_USB_P_30:
1208 			vers = "3.0";
1209 			break;
1210 		case TOPO_USB_P_31:
1211 			vers = "3.1";
1212 			break;
1213 		default:
1214 			continue;
1215 		}
1216 
1217 		/*
1218 		 * Make sure we don't already have this string. This can happen
1219 		 * when we have an ehci port and xhci support that both provide
1220 		 * USB 2.0 service.
1221 		 */
1222 		for (j = 0; j < i; j++) {
1223 			if (strcmp(strs[j], vers) == 0)
1224 				break;
1225 		}
1226 
1227 		if (j < i)
1228 			continue;
1229 		strs[i++] = vers;
1230 	}
1231 
1232 	if (i > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PORT,
1233 	    TOPO_PROP_USB_PORT_VERSIONS, TOPO_PROP_IMMUTABLE,
1234 	    (const char **)strs, i, &err) != 0) {
1235 		topo_mod_dprintf(mod, "failed to set %s property: %s",
1236 		    TOPO_PROP_USB_PORT_VERSIONS, topo_strerror(err));
1237 		goto error;
1238 	}
1239 
1240 	i = 0;
1241 	if (port->tup_pld_valid && port->tup_pld.UserVisible != 0 &&
1242 	    port->tup_port_connected == TOPO_USB_C_CONNECTED) {
1243 		strs[i++] = TOPO_PROP_USB_PORT_A_VISIBLE;
1244 	} else if (port->tup_port_connected == TOPO_USB_C_CONNECTED) {
1245 		strs[i++] = TOPO_PROP_USB_PORT_A_CONNECTED;
1246 	} else if (port->tup_port_connected == TOPO_USB_C_DISCONNECTED) {
1247 		strs[i++] = TOPO_PROP_USB_PORT_A_DISCONNECTED;
1248 	}
1249 
1250 	if (port->tup_meta != NULL) {
1251 		if (port->tup_meta->tmp_flags & TOPO_USB_F_INTERNAL) {
1252 			strs[i++] = TOPO_PROP_USB_PORT_A_INTERNAL;
1253 		}
1254 
1255 		if (port->tup_meta->tmp_flags & TOPO_USB_F_EXTERNAL) {
1256 			strs[i++] = TOPO_PROP_USB_PORT_A_EXTERNAL;
1257 		}
1258 	}
1259 
1260 	if (i > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PORT,
1261 	    TOPO_PROP_USB_PORT_ATTRIBUTES, TOPO_PROP_IMMUTABLE,
1262 	    (const char **)strs, i, &err) != 0) {
1263 		topo_mod_dprintf(mod, "failed to set %s property: %s",
1264 		    TOPO_PROP_USB_PORT_VERSIONS, topo_strerror(err));
1265 		goto error;
1266 	}
1267 
1268 	for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL;
1269 	    l = topo_list_next(l)) {
1270 		strs[i++] = l->tul_name;
1271 	}
1272 
1273 	if (i > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PORT,
1274 	    TOPO_PROP_USB_PORT_LPORTS, TOPO_PROP_IMMUTABLE,
1275 	    (const char **)strs, i, &err) != 0) {
1276 		topo_mod_dprintf(mod, "failed to set %s propert: %s",
1277 		    TOPO_PROP_USB_PORT_LPORTS, topo_strerror(err));
1278 		goto error;
1279 	}
1280 
1281 	err = 0;
1282 error:
1283 	if (strs != NULL) {
1284 		topo_mod_free(mod, strs, strlen);
1285 	}
1286 
1287 	if (err != 0) {
1288 		return (topo_mod_seterrno(mod, err));
1289 	}
1290 
1291 	return (err);
1292 }
1293 
1294 /*
1295  * Create a disk node under the scsa2usb node. When we have an scsa2usb node,
1296  * we'll have a child devinfo which is a disk. To successfully enumerate this,
1297  * we need to find the child node (which should be our only direct descendent)
1298  * and get its devfs path. From there we can construct a 'binding' property
1299  * group with the 'occupantpath' property that points to the module. At that
1300  * point we can invoke the disk enumerator.
1301  */
1302 static int
topo_usb_enum_scsa2usb(topo_mod_t * mod,tnode_t * tn,topo_usb_lport_t * lport)1303 topo_usb_enum_scsa2usb(topo_mod_t *mod, tnode_t *tn, topo_usb_lport_t *lport)
1304 {
1305 	int ret;
1306 	di_node_t child;
1307 	char *devfs = NULL;
1308 	topo_instance_t min = 0, max = 0;
1309 
1310 	if ((child = di_child_node(lport->tul_device)) == DI_NODE_NIL ||
1311 	    strcmp("disk", di_node_name(child)) != 0) {
1312 		return (0);
1313 	}
1314 
1315 	if ((devfs = di_devfs_path(child)) == NULL) {
1316 		topo_mod_dprintf(mod, "failed to get USB disk child device "
1317 		    "devfs path");
1318 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1319 	}
1320 
1321 	if (topo_mod_load(mod, DISK, TOPO_VERSION) == NULL) {
1322 		topo_mod_dprintf(mod, "failed to load disk module: %s",
1323 		    topo_mod_errmsg(mod));
1324 		goto error;
1325 	}
1326 
1327 	if (topo_pgroup_create(tn, &topo_binding_pgroup, &ret) != 0) {
1328 		topo_mod_dprintf(mod, "failed to create \"binding\" "
1329 		    "property group: %s", topo_strerror(ret));
1330 		goto error;
1331 	}
1332 
1333 	if (topo_prop_set_string(tn, TOPO_PGROUP_BINDING,
1334 	    TOPO_BINDING_OCCUPANT, TOPO_PROP_IMMUTABLE, devfs, &ret) !=
1335 	    0) {
1336 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1337 		    TOPO_IO_MODULE, topo_strerror(ret));
1338 		goto error;
1339 	}
1340 	di_devfs_path_free(devfs);
1341 	devfs = NULL;
1342 
1343 	if (topo_node_range_create(mod, tn, DISK, min, max) != 0) {
1344 		topo_mod_dprintf(mod, "failed to create disk node range %s: %s",
1345 		    devfs, topo_mod_errmsg(mod));
1346 		goto error;
1347 	}
1348 
1349 	if (topo_mod_enumerate(mod, tn, DISK, DISK, min, max, NULL) != 0) {
1350 		topo_mod_dprintf(mod, "failed to create disk node %s: %s",
1351 		    devfs, topo_mod_errmsg(mod));
1352 		goto error;
1353 	}
1354 
1355 	return (0);
1356 
1357 error:
1358 	di_devfs_path_free(devfs);
1359 	return (-1);
1360 }
1361 
1362 static int
topo_usb_enum_port_children(topo_mod_t * mod,tnode_t * pn,topo_usb_lport_t * plport)1363 topo_usb_enum_port_children(topo_mod_t *mod, tnode_t *pn,
1364     topo_usb_lport_t *plport)
1365 {
1366 	int ret;
1367 	topo_usb_port_t *port;
1368 	topo_instance_t min = 0, i;
1369 
1370 	if ((ret = port_range_create(mod, pn, min, plport->tul_nports)) != 0) {
1371 		topo_mod_dprintf(mod, "failed to create port range [%u, %u) "
1372 		    "for child hub", 0, plport->tul_nports);
1373 		return (ret);
1374 	}
1375 
1376 	for (i = 0, port = topo_list_next(&plport->tul_ports); port != NULL;
1377 	    port = topo_list_next(port)) {
1378 		tnode_t *tn;
1379 		if ((ret = port_create_usb(mod, pn, i, &tn)) != 0)
1380 			return (ret);
1381 
1382 		if ((ret = topo_usb_port_properties(mod, tn, port)) != 0) {
1383 			return (ret);
1384 		}
1385 
1386 		if ((ret = topo_usb_enum_device(mod, tn, port)) != 0)
1387 			return (ret);
1388 
1389 		i++;
1390 	}
1391 
1392 	return (0);
1393 }
1394 
1395 /*
1396  * Enumerate the requested device. Depending on the driver associated with it
1397  * (if any), we may have to create child nodes.
1398  */
1399 static int
topo_usb_enum_lport(topo_mod_t * mod,tnode_t * pn,topo_usb_port_t * port,topo_usb_lport_t * lport,topo_instance_t topo_inst)1400 topo_usb_enum_lport(topo_mod_t *mod, tnode_t *pn, topo_usb_port_t *port,
1401     topo_usb_lport_t *lport, topo_instance_t topo_inst)
1402 {
1403 	int ret, inst;
1404 	int *vendid = NULL, *prodid = NULL, *revid = NULL, *release = NULL;
1405 	char *vend = NULL, *prod = NULL, *serial = NULL, *speed = NULL;
1406 	char *min_speed = NULL, *sup_speeds = NULL;
1407 	int nsup_speeds = 0;
1408 	char *driver, *devfs;
1409 	char revbuf[32], relbuf[32];
1410 	tnode_t *tn = NULL;
1411 	di_prop_t prop = DI_PROP_NIL;
1412 	nvlist_t *auth = NULL, *fmri = NULL, *modnvl = NULL;
1413 
1414 	/*
1415 	 * Look up the information we'll need to create the usb-properties. We
1416 	 * do this first because this information is often part of the FMRI.
1417 	 */
1418 	for (prop = di_prop_next(lport->tul_device, DI_PROP_NIL);
1419 	    prop != DI_PROP_NIL; prop = di_prop_next(lport->tul_device, prop)) {
1420 		const char *pname = di_prop_name(prop);
1421 
1422 		if (strcmp(pname, "usb-vendor-id") == 0) {
1423 			if (di_prop_ints(prop, &vendid) != 1)
1424 				vendid = NULL;
1425 		} else if (strcmp(pname, "usb-product-id") == 0) {
1426 			if (di_prop_ints(prop, &prodid) != 1)
1427 				prodid = NULL;
1428 		} else if (strcmp(pname, "usb-revision-id") == 0) {
1429 			if (di_prop_ints(prop, &revid) != 1) {
1430 				revid = NULL;
1431 			} else {
1432 				(void) snprintf(revbuf, sizeof (revbuf), "%x",
1433 				    *revid);
1434 			}
1435 		} else if (strcmp(pname, "usb-release") == 0) {
1436 			if (di_prop_ints(prop, &release) != 1) {
1437 				release = NULL;
1438 			} else {
1439 				(void) snprintf(relbuf, sizeof (relbuf),
1440 				    "%x.%x", *release >> 8,
1441 				    (*release >> 4) & 0xf);
1442 			}
1443 		} else if (strcmp(pname, "usb-vendor-name") == 0) {
1444 			if (di_prop_strings(prop, &vend) != 1)
1445 				vend = NULL;
1446 		} else if (strcmp(pname, "usb-product-name") == 0) {
1447 			if (di_prop_strings(prop, &prod) != 1)
1448 				prod = NULL;
1449 		} else if (strcmp(pname, "usb-serialno") == 0) {
1450 			if (di_prop_strings(prop, &serial) != 1)
1451 				serial = NULL;
1452 		} else if (strcmp(pname, "full-speed") == 0) {
1453 			speed = "full-speed";
1454 		} else if (strcmp(pname, "low-speed") == 0) {
1455 			speed = "low-speed";
1456 		} else if (strcmp(pname, "high-speed") == 0) {
1457 			speed = "high-speed";
1458 		} else if (strcmp(pname, "super-speed") == 0) {
1459 			speed = "super-speed";
1460 		} else if (strcmp(pname, "usb-minimum-speed") == 0) {
1461 			if (di_prop_strings(prop, &min_speed) != 1)
1462 				min_speed = NULL;
1463 		} else if (strcmp(pname, "usb-supported-speeds") == 0) {
1464 			nsup_speeds = di_prop_strings(prop, &sup_speeds);
1465 			if (nsup_speeds <= 0) {
1466 				sup_speeds = NULL;
1467 			}
1468 		}
1469 	}
1470 
1471 	driver = di_driver_name(lport->tul_device);
1472 	inst = di_instance(lport->tul_device);
1473 	devfs = di_devfs_path(lport->tul_device);
1474 
1475 	if ((auth = topo_mod_auth(mod, pn)) == NULL) {
1476 		topo_mod_dprintf(mod, "failed to get authority for USB device: "
1477 		    "%s", topo_mod_errmsg(mod));
1478 		goto error;
1479 	}
1480 
1481 	if ((fmri = topo_mod_hcfmri(mod, pn, FM_HC_SCHEME_VERSION, USB_DEVICE,
1482 	    topo_inst, NULL, auth, prod, revbuf, serial)) == NULL) {
1483 		topo_mod_dprintf(mod, "failed to generate fmri for USB "
1484 		    "device %s: %s", di_devfs_path(lport->tul_device),
1485 		    topo_mod_errmsg(mod));
1486 		goto error;
1487 	}
1488 
1489 	if ((tn = topo_node_bind(mod, pn, USB_DEVICE, topo_inst, fmri)) ==
1490 	    NULL) {
1491 		topo_mod_dprintf(mod, "failed to bind USB device node: %s",
1492 		    topo_mod_errmsg(mod));
1493 		goto error;
1494 	}
1495 
1496 	/*
1497 	 * In general, we expect a USB device to be its own FRU. There are some
1498 	 * exceptions to this, for example, a built-in hub. However, it's hard
1499 	 * for us to generally know. It may be nice to allow the platform to
1500 	 * override this in the future.
1501 	 */
1502 	if (topo_node_fru_set(tn, fmri, 0, &ret) != 0) {
1503 		topo_mod_dprintf(mod, "failed to set FRU: %s",
1504 		    topo_strerror(ret));
1505 		(void) topo_mod_seterrno(mod, ret);
1506 		goto error;
1507 	}
1508 
1509 	/*
1510 	 * Inherit the label from the port on the device. This is intended to
1511 	 * only go a single way.
1512 	 */
1513 	if (port->tup_meta != NULL && port->tup_meta->tmp_label != NULL &&
1514 	    topo_node_label_set(tn, port->tup_meta->tmp_label, &ret) != 0) {
1515 		topo_mod_dprintf(mod, "failed to set label on device: %s",
1516 		    topo_strerror(ret));
1517 		goto error;
1518 	}
1519 
1520 	/*
1521 	 * USB-properties
1522 	 */
1523 	if (topo_pgroup_create(tn, &topo_usb_props_pgroup, &ret) != 0) {
1524 		topo_mod_dprintf(mod, "failed to create \"usb-properties\" "
1525 		    "property group: %s", topo_strerror(ret));
1526 		goto error;
1527 	}
1528 
1529 	if (topo_prop_set_uint32(tn, TOPO_PGROUP_USB_PROPS,
1530 	    TOPO_PGROUP_USB_PROPS_PORT, TOPO_PROP_IMMUTABLE, lport->tul_portno,
1531 	    &ret) != 0) {
1532 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1533 		    TOPO_PGROUP_USB_PROPS_PORT, topo_strerror(ret));
1534 		goto error;
1535 	}
1536 
1537 	if (vendid != NULL && topo_prop_set_int32(tn, TOPO_PGROUP_USB_PROPS,
1538 	    TOPO_PGROUP_USB_PROPS_VID, TOPO_PROP_IMMUTABLE, *vendid, &ret) !=
1539 	    0) {
1540 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1541 		    TOPO_PGROUP_USB_PROPS_VID, topo_strerror(ret));
1542 		goto error;
1543 	}
1544 
1545 	if (prodid != NULL && topo_prop_set_int32(tn, TOPO_PGROUP_USB_PROPS,
1546 	    TOPO_PGROUP_USB_PROPS_PID, TOPO_PROP_IMMUTABLE, *prodid, &ret) !=
1547 	    0) {
1548 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1549 		    TOPO_PGROUP_USB_PROPS_PID, topo_strerror(ret));
1550 		goto error;
1551 	}
1552 
1553 	if (revid != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1554 	    TOPO_PGROUP_USB_PROPS_REV, TOPO_PROP_IMMUTABLE, revbuf, &ret) !=
1555 	    0) {
1556 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1557 		    TOPO_PGROUP_USB_PROPS_REV, topo_strerror(ret));
1558 		goto error;
1559 	}
1560 
1561 	if (release != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1562 	    TOPO_PGROUP_USB_PROPS_VERSION, TOPO_PROP_IMMUTABLE, relbuf, &ret) !=
1563 	    0) {
1564 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1565 		    TOPO_PGROUP_USB_PROPS_VERSION, topo_strerror(ret));
1566 		goto error;
1567 	}
1568 
1569 	if (vend != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1570 	    TOPO_PGROUP_USB_PROPS_VNAME, TOPO_PROP_IMMUTABLE, vend, &ret) !=
1571 	    0) {
1572 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1573 		    TOPO_PGROUP_USB_PROPS_VNAME, topo_strerror(ret));
1574 		goto error;
1575 	}
1576 
1577 	if (prod != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1578 	    TOPO_PGROUP_USB_PROPS_PNAME, TOPO_PROP_IMMUTABLE, prod, &ret) !=
1579 	    0) {
1580 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1581 		    TOPO_PGROUP_USB_PROPS_PNAME, topo_strerror(ret));
1582 		goto error;
1583 	}
1584 
1585 	if (serial != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1586 	    TOPO_PGROUP_USB_PROPS_SN, TOPO_PROP_IMMUTABLE, serial, &ret) !=
1587 	    0) {
1588 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1589 		    TOPO_PGROUP_USB_PROPS_SN, topo_strerror(ret));
1590 		goto error;
1591 	}
1592 
1593 	if (speed != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1594 	    TOPO_PGROUP_USB_PROPS_SPEED, TOPO_PROP_IMMUTABLE, speed, &ret) !=
1595 	    0) {
1596 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1597 		    TOPO_PGROUP_USB_PROPS_SPEED, topo_strerror(ret));
1598 		goto error;
1599 	}
1600 
1601 	if (min_speed != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1602 	    TOPO_PGROUP_USB_PROPS_MIN_SPEED, TOPO_PROP_IMMUTABLE, min_speed,
1603 	    &ret) != 0) {
1604 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1605 		    TOPO_PGROUP_USB_PROPS_MIN_SPEED, topo_strerror(ret));
1606 		goto error;
1607 	}
1608 
1609 	if (sup_speeds != NULL) {
1610 		const char **strings, *c;
1611 		int i, rval;
1612 
1613 		if ((strings = topo_mod_zalloc(mod, sizeof (char *) *
1614 		    nsup_speeds)) == NULL) {
1615 			topo_mod_dprintf(mod, "failed to allocate character "
1616 			    "array for property %s",
1617 			    TOPO_PGROUP_USB_PROPS_SUPPORTED_SPEEDS);
1618 			goto error;
1619 		}
1620 
1621 		/*
1622 		 * devinfo string properties are concatenated NUL-terminated
1623 		 * strings. We need to translate that to a string array.
1624 		 */
1625 		for (c = sup_speeds, i = 0; i < nsup_speeds; i++) {
1626 			size_t len;
1627 
1628 			strings[i] = c;
1629 			if (i + 1 < nsup_speeds) {
1630 				len = strlen(c);
1631 				c += len + 1;
1632 			}
1633 		}
1634 
1635 		rval = topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PROPS,
1636 		    TOPO_PGROUP_USB_PROPS_SUPPORTED_SPEEDS, TOPO_PROP_IMMUTABLE,
1637 		    strings, nsup_speeds, &ret);
1638 		topo_mod_free(mod, strings, sizeof (char *) * nsup_speeds);
1639 		if (rval != 0) {
1640 			topo_mod_dprintf(mod, "failed to create property %s: "
1641 			    "%s", TOPO_PGROUP_USB_PROPS_SUPPORTED_SPEEDS,
1642 			    topo_strerror(ret));
1643 		}
1644 	}
1645 
1646 	/*
1647 	 * I/O pgroup
1648 	 */
1649 	if (topo_pgroup_create(tn, &topo_io_pgroup, &ret) != 0) {
1650 		topo_mod_dprintf(mod, "failed to create \"io\" "
1651 		    "property group: %s", topo_strerror(ret));
1652 		goto error;
1653 	}
1654 
1655 	if (driver != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO,
1656 	    TOPO_IO_DRIVER, TOPO_PROP_IMMUTABLE, driver, &ret) !=
1657 	    0) {
1658 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1659 		    TOPO_IO_DRIVER, topo_strerror(ret));
1660 		goto error;
1661 	}
1662 
1663 	if (inst != -1 && topo_prop_set_uint32(tn, TOPO_PGROUP_IO,
1664 	    TOPO_IO_INSTANCE, TOPO_PROP_IMMUTABLE, inst, &ret) !=
1665 	    0) {
1666 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1667 		    TOPO_IO_INSTANCE, topo_strerror(ret));
1668 		goto error;
1669 	}
1670 
1671 	if (devfs != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO,
1672 	    TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, devfs, &ret) !=
1673 	    0) {
1674 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1675 		    TOPO_IO_DEV_PATH, topo_strerror(ret));
1676 		goto error;
1677 	}
1678 
1679 	if (driver != NULL && (modnvl = topo_mod_modfmri(mod,
1680 	    FM_MOD_SCHEME_VERSION, driver)) != NULL &&
1681 	    topo_prop_set_fmri(tn, TOPO_PGROUP_IO, TOPO_IO_MODULE,
1682 	    TOPO_PROP_IMMUTABLE, modnvl, &ret) != 0) {
1683 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1684 		    TOPO_IO_MODULE, topo_strerror(ret));
1685 		goto error;
1686 	}
1687 
1688 	/*
1689 	 * Check the drivers to determine special behavior that we should do.
1690 	 * The following are cases that we want to handle:
1691 	 *
1692 	 *   o Creating disk nodes for scsa2usb devices
1693 	 *   o Creating children ports and searching them for hubd
1694 	 */
1695 	if (driver != NULL && strcmp(driver, "scsa2usb") == 0) {
1696 		if ((ret = topo_usb_enum_scsa2usb(mod, tn, lport)) != 0)
1697 			goto error;
1698 	}
1699 
1700 	if (lport->tul_nports > 0 && driver != NULL &&
1701 	    strcmp(driver, "hubd") == 0) {
1702 		if ((ret = topo_usb_enum_port_children(mod, tn, lport)) != 0)
1703 			goto error;
1704 	}
1705 
1706 	di_devfs_path_free(devfs);
1707 	nvlist_free(fmri);
1708 	nvlist_free(auth);
1709 	nvlist_free(modnvl);
1710 	return (0);
1711 
1712 error:
1713 	topo_node_unbind(tn);
1714 	di_devfs_path_free(devfs);
1715 	nvlist_free(fmri);
1716 	nvlist_free(auth);
1717 	nvlist_free(modnvl);
1718 	return (-1);
1719 }
1720 
1721 static int
topo_usb_enum_device(topo_mod_t * mod,tnode_t * pn,topo_usb_port_t * port)1722 topo_usb_enum_device(topo_mod_t *mod, tnode_t *pn, topo_usb_port_t *port)
1723 {
1724 	int ret;
1725 	topo_instance_t i, max;
1726 	topo_usb_lport_t *l;
1727 
1728 	max = 0;
1729 	for (l = topo_list_next(&port->tup_lports); l != NULL;
1730 	    l = topo_list_next(l)) {
1731 		if (l->tul_device != DI_NODE_NIL)
1732 			max++;
1733 	}
1734 
1735 	if (max == 0) {
1736 		return (0);
1737 	}
1738 
1739 	if ((ret = topo_node_range_create(mod, pn, USB_DEVICE, 0, max - 1)) !=
1740 	    0) {
1741 		return (-1);
1742 	}
1743 
1744 	for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL;
1745 	    l = topo_list_next(l)) {
1746 		if (l->tul_device != DI_NODE_NIL) {
1747 			topo_mod_dprintf(mod, "enumerating device on lport "
1748 			    "%u, log inst %d", l->tul_portno, i);
1749 			if ((ret = topo_usb_enum_lport(mod, pn, port, l,
1750 			    i)) != 0) {
1751 				return (ret);
1752 			}
1753 			i++;
1754 		}
1755 	}
1756 
1757 	return (0);
1758 }
1759 
1760 static int
topo_usb_enum_controller(topo_mod_t * mod,tnode_t * pnode,topo_usb_controller_t * c,topo_instance_t base)1761 topo_usb_enum_controller(topo_mod_t *mod, tnode_t *pnode,
1762     topo_usb_controller_t *c, topo_instance_t base)
1763 {
1764 	int ret;
1765 	topo_usb_port_t *port;
1766 
1767 	if (c->tuc_enumed)
1768 		return (0);
1769 
1770 	c->tuc_enumed = B_TRUE;
1771 	if (c->tuc_nports == 0)
1772 		return (0);
1773 
1774 	for (port = topo_list_next(&c->tuc_ports); port != NULL;
1775 	    port = topo_list_next(port)) {
1776 		tnode_t *tn;
1777 		if ((ret = port_create_usb(mod, pnode, base, &tn)) != 0)
1778 			return (ret);
1779 
1780 		if ((ret = topo_usb_port_properties(mod, tn, port)) != 0) {
1781 			return (ret);
1782 		}
1783 
1784 		if ((ret = topo_usb_enum_device(mod, tn, port)) != 0)
1785 			return (ret);
1786 
1787 		base++;
1788 	}
1789 
1790 	return (0);
1791 }
1792 
1793 static int
topo_usb_enum_mobo(topo_mod_t * mod,tnode_t * pnode,topo_usb_t * usb)1794 topo_usb_enum_mobo(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb)
1795 {
1796 	int ret;
1797 	topo_usb_controller_t *c;
1798 	topo_instance_t inst = 0;
1799 
1800 	/*
1801 	 * First count the number of ports, so we can create the right range.
1802 	 * Then go back and actually create things. Some of the ports here may
1803 	 * be actually on the chassis, that's OK, we don't mind over counting
1804 	 * here.
1805 	 */
1806 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1807 	    c = topo_list_next(c)) {
1808 		inst += c->tuc_nports;
1809 	}
1810 
1811 	if ((ret = port_range_create(mod, pnode, 0, inst)) != 0) {
1812 		topo_mod_dprintf(mod, "failed to create port range [%u, %u) "
1813 		    "for mobo", 0, inst);
1814 		return (ret);
1815 	}
1816 
1817 	inst = 0;
1818 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1819 	    c = topo_list_next(c)) {
1820 		if (c->tuc_enumed)
1821 			continue;
1822 		if ((ret = topo_usb_enum_controller(mod, pnode, c, inst)) !=
1823 		    0) {
1824 			return (ret);
1825 		}
1826 		inst += c->tuc_nports;
1827 	}
1828 
1829 	return (0);
1830 }
1831 
1832 static int
topo_usb_enum_pci(topo_mod_t * mod,tnode_t * pnode,topo_usb_t * usb,di_node_t din)1833 topo_usb_enum_pci(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb,
1834     di_node_t din)
1835 {
1836 	int ret;
1837 	topo_usb_controller_t *c;
1838 	topo_instance_t min = 0;
1839 
1840 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1841 	    c = topo_list_next(c)) {
1842 		if (din == c->tuc_devinfo) {
1843 			break;
1844 		}
1845 	}
1846 
1847 	if (c == NULL) {
1848 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1849 	}
1850 
1851 	if ((ret = port_range_create(mod, pnode, min, c->tuc_nports)) != 0) {
1852 		topo_mod_dprintf(mod, "failed to create port range [%u, %u) "
1853 		    "for controller %s", min, c->tuc_nports, c->tuc_path);
1854 		return (ret);
1855 	}
1856 
1857 	return (topo_usb_enum_controller(mod, pnode, c, min));
1858 }
1859 
1860 static int
topo_usb_enum_chassis(topo_mod_t * mod,tnode_t * pnode,topo_usb_t * usb)1861 topo_usb_enum_chassis(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb)
1862 {
1863 	int ret;
1864 	topo_usb_port_t *p;
1865 	topo_instance_t base = 0;
1866 
1867 	if (usb->tu_nchassis_ports == 0)
1868 		return (0);
1869 
1870 	if ((ret = port_range_create(mod, pnode, 0, usb->tu_nchassis_ports)) !=
1871 	    0) {
1872 		topo_mod_dprintf(mod, "failed to create port range [%u, %u) "
1873 		    "for mobo", 0, usb);
1874 		return (ret);
1875 	}
1876 
1877 	for (p = topo_list_next(&usb->tu_chassis_ports); p != NULL;
1878 	    p = topo_list_next(p)) {
1879 		tnode_t *tn;
1880 		if ((ret = port_create_usb(mod, pnode, base, &tn)) != 0)
1881 			return (ret);
1882 
1883 		if ((ret = topo_usb_port_properties(mod, tn, p)) != 0) {
1884 			return (ret);
1885 		}
1886 
1887 		if ((ret = topo_usb_enum_device(mod, tn, p)) != 0)
1888 			return (ret);
1889 
1890 		base++;
1891 	}
1892 
1893 	return (0);
1894 }
1895 
1896 /* ARGSUSED */
1897 static int
topo_usb_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * modarg,void * data)1898 topo_usb_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
1899     topo_instance_t min, topo_instance_t max, void *modarg, void *data)
1900 {
1901 	topo_usb_t *usb;
1902 	topo_usb_type_t type;
1903 
1904 	if (strcmp(name, USB_PCI) == 0) {
1905 		type = TOPO_USB_PCI;
1906 	} else if (strcmp(name, USB_MOBO) == 0) {
1907 		type = TOPO_USB_MOBO;
1908 	} else if (strcmp(name, USB_CHASSIS) == 0) {
1909 		type = TOPO_USB_CHASSIS;
1910 	} else {
1911 		topo_mod_dprintf(mod, "usb_enum: asked to enumerate unknown "
1912 		    "component: %s\n", name);
1913 		return (-1);
1914 	}
1915 
1916 	if (type == TOPO_USB_PCI && data == NULL) {
1917 		topo_mod_dprintf(mod, "usb_enum: missing argument to "
1918 		    "PCI controller enum");
1919 		return (-1);
1920 	} else if (type != TOPO_USB_PCI && data != NULL) {
1921 		topo_mod_dprintf(mod, "extraneous argument to non-controller "
1922 		    "enum %s", name);
1923 		return (-1);
1924 	}
1925 
1926 	if ((usb = topo_mod_getspecific(mod)) == NULL) {
1927 		return (-1);
1928 	}
1929 
1930 	if (!usb->tu_enum_done) {
1931 		if (topo_usb_gather(mod, usb, pnode) != 0)
1932 			return (-1);
1933 		usb->tu_enum_done = B_TRUE;
1934 	}
1935 
1936 	/*
1937 	 * Now that we've built up the topo nodes, enumerate the specific nodes
1938 	 * based on the requested type.
1939 	 */
1940 	if (type == TOPO_USB_PCI) {
1941 		return (topo_usb_enum_pci(mod, pnode, usb, data));
1942 	} else if (type == TOPO_USB_MOBO) {
1943 		return (topo_usb_enum_mobo(mod, pnode, usb));
1944 	} else if (type == TOPO_USB_CHASSIS) {
1945 		return (topo_usb_enum_chassis(mod, pnode, usb));
1946 	}
1947 
1948 	return (0);
1949 }
1950 
1951 static const topo_modops_t usb_ops = {
1952 	topo_usb_enum, NULL
1953 };
1954 
1955 static topo_modinfo_t usb_mod = {
1956 	USB, FM_FMRI_SCHEME_HC, USB_VERSION, &usb_ops
1957 };
1958 
1959 static void
topo_usb_port_free(topo_mod_t * mod,topo_usb_port_t * p)1960 topo_usb_port_free(topo_mod_t *mod, topo_usb_port_t *p)
1961 {
1962 	topo_usb_lport_t *lport;
1963 
1964 	while ((lport = topo_list_next(&p->tup_lports)) != NULL) {
1965 		topo_usb_port_t *child;
1966 
1967 		topo_list_delete(&p->tup_lports, lport);
1968 		while ((child = topo_list_next(&lport->tul_ports)) != NULL) {
1969 			topo_list_delete(&lport->tul_ports, child);
1970 			topo_usb_port_free(mod, child);
1971 		}
1972 		topo_mod_free(mod, lport, sizeof (topo_usb_lport_t));
1973 	}
1974 
1975 	topo_mod_free(mod, p, sizeof (topo_usb_port_t));
1976 }
1977 
1978 static void
topo_usb_free(topo_mod_t * mod,topo_usb_t * usb)1979 topo_usb_free(topo_mod_t *mod, topo_usb_t *usb)
1980 {
1981 	topo_usb_controller_t *c;
1982 	topo_usb_port_t *p;
1983 
1984 	if (usb == NULL)
1985 		return;
1986 
1987 	while ((p = topo_list_next(&usb->tu_chassis_ports)) != NULL) {
1988 		topo_list_delete(&usb->tu_chassis_ports, p);
1989 		topo_usb_port_free(mod, p);
1990 	}
1991 
1992 	while ((c = topo_list_next(&usb->tu_controllers)) != NULL) {
1993 
1994 		topo_list_delete(&usb->tu_controllers, c);
1995 		di_devfs_path_free(c->tuc_path);
1996 
1997 		while ((p = topo_list_next(&c->tuc_ports)) != NULL) {
1998 			topo_list_delete(&c->tuc_ports, p);
1999 			topo_usb_port_free(mod, p);
2000 		}
2001 		topo_mod_free(mod, c, sizeof (topo_usb_controller_t));
2002 	}
2003 
2004 	topo_usb_free_metadata(mod, &usb->tu_metadata);
2005 
2006 	/*
2007 	 * The devinfo handle came from fm, don't do anything ourselevs.
2008 	 */
2009 	usb->tu_devinfo = DI_NODE_NIL;
2010 
2011 	topo_mod_free(mod, usb, sizeof (topo_usb_t));
2012 }
2013 
2014 static topo_usb_t *
topo_usb_alloc(topo_mod_t * mod)2015 topo_usb_alloc(topo_mod_t *mod)
2016 {
2017 	topo_usb_t *usb = NULL;
2018 
2019 	if ((usb = topo_mod_zalloc(mod, sizeof (topo_usb_t))) == NULL) {
2020 		goto free;
2021 	}
2022 
2023 	if ((usb->tu_devinfo = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
2024 		goto free;
2025 	}
2026 
2027 	return (usb);
2028 
2029 free:
2030 	topo_usb_free(mod, usb);
2031 	return (NULL);
2032 }
2033 
2034 int
_topo_init(topo_mod_t * mod,topo_version_t version)2035 _topo_init(topo_mod_t *mod, topo_version_t version)
2036 {
2037 	topo_usb_t *usb;
2038 
2039 	if (getenv("TOPOUSBDEBUG") != NULL)
2040 		topo_mod_setdebug(mod);
2041 
2042 	topo_mod_dprintf(mod, "_mod_init: initializing %s enumerator\n", USB);
2043 
2044 	if (version != USB_VERSION) {
2045 		return (-1);
2046 	}
2047 
2048 	if ((usb = topo_usb_alloc(mod)) == NULL) {
2049 		return (-1);
2050 	}
2051 
2052 	if (topo_mod_register(mod, &usb_mod, TOPO_VERSION) != 0) {
2053 		topo_usb_free(mod, usb);
2054 		return (-1);
2055 	}
2056 
2057 	topo_mod_setspecific(mod, usb);
2058 
2059 	return (0);
2060 }
2061 
2062 void
_topo_fini(topo_mod_t * mod)2063 _topo_fini(topo_mod_t *mod)
2064 {
2065 	topo_usb_free(mod, topo_mod_getspecific(mod));
2066 	topo_mod_setspecific(mod, NULL);
2067 }
2068