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 
1341 	if (topo_node_range_create(mod, tn, DISK, min, max) != 0) {
1342 		topo_mod_dprintf(mod, "failed to create disk node range %s: %s",
1343 		    devfs, topo_mod_errmsg(mod));
1344 		goto error;
1345 	}
1346 
1347 	if (topo_mod_enumerate(mod, tn, DISK, DISK, min, max, NULL) != 0) {
1348 		topo_mod_dprintf(mod, "failed to create disk node %s: %s",
1349 		    devfs, topo_mod_errmsg(mod));
1350 		goto error;
1351 	}
1352 	di_devfs_path_free(devfs);
1353 
1354 	return (0);
1355 
1356 error:
1357 	di_devfs_path_free(devfs);
1358 	return (-1);
1359 }
1360 
1361 static int
topo_usb_enum_port_children(topo_mod_t * mod,tnode_t * pn,topo_usb_lport_t * plport)1362 topo_usb_enum_port_children(topo_mod_t *mod, tnode_t *pn,
1363     topo_usb_lport_t *plport)
1364 {
1365 	int ret;
1366 	topo_usb_port_t *port;
1367 	topo_instance_t min = 0, i;
1368 
1369 	if ((ret = port_range_create(mod, pn, min, plport->tul_nports)) != 0) {
1370 		topo_mod_dprintf(mod, "failed to create port range [%u, %u) "
1371 		    "for child hub", 0, plport->tul_nports);
1372 		return (ret);
1373 	}
1374 
1375 	for (i = 0, port = topo_list_next(&plport->tul_ports); port != NULL;
1376 	    port = topo_list_next(port)) {
1377 		tnode_t *tn;
1378 		if ((ret = port_create_usb(mod, pn, i, &tn)) != 0)
1379 			return (ret);
1380 
1381 		if ((ret = topo_usb_port_properties(mod, tn, port)) != 0) {
1382 			return (ret);
1383 		}
1384 
1385 		if ((ret = topo_usb_enum_device(mod, tn, port)) != 0)
1386 			return (ret);
1387 
1388 		i++;
1389 	}
1390 
1391 	return (0);
1392 }
1393 
1394 /*
1395  * Enumerate the requested device. Depending on the driver associated with it
1396  * (if any), we may have to create child nodes.
1397  */
1398 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)1399 topo_usb_enum_lport(topo_mod_t *mod, tnode_t *pn, topo_usb_port_t *port,
1400     topo_usb_lport_t *lport, topo_instance_t topo_inst)
1401 {
1402 	int ret, inst;
1403 	int *vendid = NULL, *prodid = NULL, *revid = NULL, *release = NULL;
1404 	char *vend = NULL, *prod = NULL, *serial = NULL, *speed = NULL;
1405 	char *min_speed = NULL, *sup_speeds = NULL;
1406 	int nsup_speeds = 0;
1407 	char *driver, *devfs;
1408 	char revbuf[32], relbuf[32];
1409 	tnode_t *tn = NULL;
1410 	di_prop_t prop = DI_PROP_NIL;
1411 	nvlist_t *auth = NULL, *fmri = NULL, *modnvl = NULL;
1412 
1413 	/*
1414 	 * Look up the information we'll need to create the usb-properties. We
1415 	 * do this first because this information is often part of the FMRI.
1416 	 */
1417 	for (prop = di_prop_next(lport->tul_device, DI_PROP_NIL);
1418 	    prop != DI_PROP_NIL; prop = di_prop_next(lport->tul_device, prop)) {
1419 		const char *pname = di_prop_name(prop);
1420 
1421 		if (strcmp(pname, "usb-vendor-id") == 0) {
1422 			if (di_prop_ints(prop, &vendid) != 1)
1423 				vendid = NULL;
1424 		} else if (strcmp(pname, "usb-product-id") == 0) {
1425 			if (di_prop_ints(prop, &prodid) != 1)
1426 				prodid = NULL;
1427 		} else if (strcmp(pname, "usb-revision-id") == 0) {
1428 			if (di_prop_ints(prop, &revid) != 1) {
1429 				revid = NULL;
1430 			} else {
1431 				(void) snprintf(revbuf, sizeof (revbuf), "%x",
1432 				    *revid);
1433 			}
1434 		} else if (strcmp(pname, "usb-release") == 0) {
1435 			if (di_prop_ints(prop, &release) != 1) {
1436 				release = NULL;
1437 			} else {
1438 				(void) snprintf(relbuf, sizeof (relbuf),
1439 				    "%x.%x", *release >> 8,
1440 				    (*release >> 4) & 0xf);
1441 			}
1442 		} else if (strcmp(pname, "usb-vendor-name") == 0) {
1443 			if (di_prop_strings(prop, &vend) != 1)
1444 				vend = NULL;
1445 		} else if (strcmp(pname, "usb-product-name") == 0) {
1446 			if (di_prop_strings(prop, &prod) != 1)
1447 				prod = NULL;
1448 		} else if (strcmp(pname, "usb-serialno") == 0) {
1449 			if (di_prop_strings(prop, &serial) != 1)
1450 				serial = NULL;
1451 		} else if (strcmp(pname, "full-speed") == 0) {
1452 			speed = "full-speed";
1453 		} else if (strcmp(pname, "low-speed") == 0) {
1454 			speed = "low-speed";
1455 		} else if (strcmp(pname, "high-speed") == 0) {
1456 			speed = "high-speed";
1457 		} else if (strcmp(pname, "super-speed") == 0) {
1458 			speed = "super-speed";
1459 		} else if (strcmp(pname, "usb-minimum-speed") == 0) {
1460 			if (di_prop_strings(prop, &min_speed) != 1)
1461 				min_speed = NULL;
1462 		} else if (strcmp(pname, "usb-supported-speeds") == 0) {
1463 			nsup_speeds = di_prop_strings(prop, &sup_speeds);
1464 			if (nsup_speeds <= 0) {
1465 				sup_speeds = NULL;
1466 			}
1467 		}
1468 	}
1469 
1470 	driver = di_driver_name(lport->tul_device);
1471 	inst = di_instance(lport->tul_device);
1472 	devfs = di_devfs_path(lport->tul_device);
1473 
1474 	if ((auth = topo_mod_auth(mod, pn)) == NULL) {
1475 		topo_mod_dprintf(mod, "failed to get authority for USB device: "
1476 		    "%s", topo_mod_errmsg(mod));
1477 		goto error;
1478 	}
1479 
1480 	if ((fmri = topo_mod_hcfmri(mod, pn, FM_HC_SCHEME_VERSION, USB_DEVICE,
1481 	    topo_inst, NULL, auth, prod, revbuf, serial)) == NULL) {
1482 		topo_mod_dprintf(mod, "failed to generate fmri for USB "
1483 		    "device %s: %s", di_devfs_path(lport->tul_device),
1484 		    topo_mod_errmsg(mod));
1485 		goto error;
1486 	}
1487 
1488 	if ((tn = topo_node_bind(mod, pn, USB_DEVICE, topo_inst, fmri)) ==
1489 	    NULL) {
1490 		topo_mod_dprintf(mod, "failed to bind USB device node: %s",
1491 		    topo_mod_errmsg(mod));
1492 		goto error;
1493 	}
1494 
1495 	/*
1496 	 * In general, we expect a USB device to be its own FRU. There are some
1497 	 * exceptions to this, for example, a built-in hub. However, it's hard
1498 	 * for us to generally know. It may be nice to allow the platform to
1499 	 * override this in the future.
1500 	 */
1501 	if (topo_node_fru_set(tn, fmri, 0, &ret) != 0) {
1502 		topo_mod_dprintf(mod, "failed to set FRU: %s",
1503 		    topo_strerror(ret));
1504 		(void) topo_mod_seterrno(mod, ret);
1505 		goto error;
1506 	}
1507 
1508 	/*
1509 	 * Inherit the label from the port on the device. This is intended to
1510 	 * only go a single way.
1511 	 */
1512 	if (port->tup_meta != NULL && port->tup_meta->tmp_label != NULL &&
1513 	    topo_node_label_set(tn, port->tup_meta->tmp_label, &ret) != 0) {
1514 		topo_mod_dprintf(mod, "failed to set label on device: %s",
1515 		    topo_strerror(ret));
1516 		goto error;
1517 	}
1518 
1519 	/*
1520 	 * USB-properties
1521 	 */
1522 	if (topo_pgroup_create(tn, &topo_usb_props_pgroup, &ret) != 0) {
1523 		topo_mod_dprintf(mod, "failed to create \"usb-properties\" "
1524 		    "property group: %s", topo_strerror(ret));
1525 		goto error;
1526 	}
1527 
1528 	if (topo_prop_set_uint32(tn, TOPO_PGROUP_USB_PROPS,
1529 	    TOPO_PGROUP_USB_PROPS_PORT, TOPO_PROP_IMMUTABLE, lport->tul_portno,
1530 	    &ret) != 0) {
1531 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1532 		    TOPO_PGROUP_USB_PROPS_PORT, topo_strerror(ret));
1533 		goto error;
1534 	}
1535 
1536 	if (vendid != NULL && topo_prop_set_int32(tn, TOPO_PGROUP_USB_PROPS,
1537 	    TOPO_PGROUP_USB_PROPS_VID, TOPO_PROP_IMMUTABLE, *vendid, &ret) !=
1538 	    0) {
1539 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1540 		    TOPO_PGROUP_USB_PROPS_VID, topo_strerror(ret));
1541 		goto error;
1542 	}
1543 
1544 	if (prodid != NULL && topo_prop_set_int32(tn, TOPO_PGROUP_USB_PROPS,
1545 	    TOPO_PGROUP_USB_PROPS_PID, TOPO_PROP_IMMUTABLE, *prodid, &ret) !=
1546 	    0) {
1547 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1548 		    TOPO_PGROUP_USB_PROPS_PID, topo_strerror(ret));
1549 		goto error;
1550 	}
1551 
1552 	if (revid != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1553 	    TOPO_PGROUP_USB_PROPS_REV, TOPO_PROP_IMMUTABLE, revbuf, &ret) !=
1554 	    0) {
1555 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1556 		    TOPO_PGROUP_USB_PROPS_REV, topo_strerror(ret));
1557 		goto error;
1558 	}
1559 
1560 	if (release != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1561 	    TOPO_PGROUP_USB_PROPS_VERSION, TOPO_PROP_IMMUTABLE, relbuf, &ret) !=
1562 	    0) {
1563 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1564 		    TOPO_PGROUP_USB_PROPS_VERSION, topo_strerror(ret));
1565 		goto error;
1566 	}
1567 
1568 	if (vend != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1569 	    TOPO_PGROUP_USB_PROPS_VNAME, TOPO_PROP_IMMUTABLE, vend, &ret) !=
1570 	    0) {
1571 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1572 		    TOPO_PGROUP_USB_PROPS_VNAME, topo_strerror(ret));
1573 		goto error;
1574 	}
1575 
1576 	if (prod != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1577 	    TOPO_PGROUP_USB_PROPS_PNAME, TOPO_PROP_IMMUTABLE, prod, &ret) !=
1578 	    0) {
1579 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1580 		    TOPO_PGROUP_USB_PROPS_PNAME, topo_strerror(ret));
1581 		goto error;
1582 	}
1583 
1584 	if (serial != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1585 	    TOPO_PGROUP_USB_PROPS_SN, TOPO_PROP_IMMUTABLE, serial, &ret) !=
1586 	    0) {
1587 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1588 		    TOPO_PGROUP_USB_PROPS_SN, topo_strerror(ret));
1589 		goto error;
1590 	}
1591 
1592 	if (speed != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1593 	    TOPO_PGROUP_USB_PROPS_SPEED, TOPO_PROP_IMMUTABLE, speed, &ret) !=
1594 	    0) {
1595 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1596 		    TOPO_PGROUP_USB_PROPS_SPEED, topo_strerror(ret));
1597 		goto error;
1598 	}
1599 
1600 	if (min_speed != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1601 	    TOPO_PGROUP_USB_PROPS_MIN_SPEED, TOPO_PROP_IMMUTABLE, min_speed,
1602 	    &ret) != 0) {
1603 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1604 		    TOPO_PGROUP_USB_PROPS_MIN_SPEED, topo_strerror(ret));
1605 		goto error;
1606 	}
1607 
1608 	if (sup_speeds != NULL) {
1609 		const char **strings, *c;
1610 		int i, rval;
1611 
1612 		if ((strings = topo_mod_zalloc(mod, sizeof (char *) *
1613 		    nsup_speeds)) == NULL) {
1614 			topo_mod_dprintf(mod, "failed to allocate character "
1615 			    "array for property %s",
1616 			    TOPO_PGROUP_USB_PROPS_SUPPORTED_SPEEDS);
1617 			goto error;
1618 		}
1619 
1620 		/*
1621 		 * devinfo string properties are concatenated NUL-terminated
1622 		 * strings. We need to translate that to a string array.
1623 		 */
1624 		for (c = sup_speeds, i = 0; i < nsup_speeds; i++) {
1625 			size_t len;
1626 
1627 			strings[i] = c;
1628 			if (i + 1 < nsup_speeds) {
1629 				len = strlen(c);
1630 				c += len + 1;
1631 			}
1632 		}
1633 
1634 		rval = topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PROPS,
1635 		    TOPO_PGROUP_USB_PROPS_SUPPORTED_SPEEDS, TOPO_PROP_IMMUTABLE,
1636 		    strings, nsup_speeds, &ret);
1637 		topo_mod_free(mod, strings, sizeof (char *) * nsup_speeds);
1638 		if (rval != 0) {
1639 			topo_mod_dprintf(mod, "failed to create property %s: "
1640 			    "%s", TOPO_PGROUP_USB_PROPS_SUPPORTED_SPEEDS,
1641 			    topo_strerror(ret));
1642 		}
1643 	}
1644 
1645 	/*
1646 	 * I/O pgroup
1647 	 */
1648 	if (topo_pgroup_create(tn, &topo_io_pgroup, &ret) != 0) {
1649 		topo_mod_dprintf(mod, "failed to create \"io\" "
1650 		    "property group: %s", topo_strerror(ret));
1651 		goto error;
1652 	}
1653 
1654 	if (driver != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO,
1655 	    TOPO_IO_DRIVER, TOPO_PROP_IMMUTABLE, driver, &ret) !=
1656 	    0) {
1657 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1658 		    TOPO_IO_DRIVER, topo_strerror(ret));
1659 		goto error;
1660 	}
1661 
1662 	if (inst != -1 && topo_prop_set_uint32(tn, TOPO_PGROUP_IO,
1663 	    TOPO_IO_INSTANCE, TOPO_PROP_IMMUTABLE, inst, &ret) !=
1664 	    0) {
1665 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1666 		    TOPO_IO_INSTANCE, topo_strerror(ret));
1667 		goto error;
1668 	}
1669 
1670 	if (devfs != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO,
1671 	    TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, devfs, &ret) !=
1672 	    0) {
1673 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1674 		    TOPO_IO_DEV_PATH, topo_strerror(ret));
1675 		goto error;
1676 	}
1677 
1678 	if (driver != NULL && (modnvl = topo_mod_modfmri(mod,
1679 	    FM_MOD_SCHEME_VERSION, driver)) != NULL &&
1680 	    topo_prop_set_fmri(tn, TOPO_PGROUP_IO, TOPO_IO_MODULE,
1681 	    TOPO_PROP_IMMUTABLE, modnvl, &ret) != 0) {
1682 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1683 		    TOPO_IO_MODULE, topo_strerror(ret));
1684 		goto error;
1685 	}
1686 
1687 	/*
1688 	 * Check the drivers to determine special behavior that we should do.
1689 	 * The following are cases that we want to handle:
1690 	 *
1691 	 *   o Creating disk nodes for scsa2usb devices
1692 	 *   o Creating children ports and searching them for hubd
1693 	 */
1694 	if (driver != NULL && strcmp(driver, "scsa2usb") == 0) {
1695 		if ((ret = topo_usb_enum_scsa2usb(mod, tn, lport)) != 0)
1696 			goto error;
1697 	}
1698 
1699 	if (lport->tul_nports > 0 && driver != NULL &&
1700 	    strcmp(driver, "hubd") == 0) {
1701 		if ((ret = topo_usb_enum_port_children(mod, tn, lport)) != 0)
1702 			goto error;
1703 	}
1704 
1705 	di_devfs_path_free(devfs);
1706 	nvlist_free(fmri);
1707 	nvlist_free(auth);
1708 	nvlist_free(modnvl);
1709 	return (0);
1710 
1711 error:
1712 	topo_node_unbind(tn);
1713 	di_devfs_path_free(devfs);
1714 	nvlist_free(fmri);
1715 	nvlist_free(auth);
1716 	nvlist_free(modnvl);
1717 	return (-1);
1718 }
1719 
1720 static int
topo_usb_enum_device(topo_mod_t * mod,tnode_t * pn,topo_usb_port_t * port)1721 topo_usb_enum_device(topo_mod_t *mod, tnode_t *pn, topo_usb_port_t *port)
1722 {
1723 	int ret;
1724 	topo_instance_t i, max;
1725 	topo_usb_lport_t *l;
1726 
1727 	max = 0;
1728 	for (l = topo_list_next(&port->tup_lports); l != NULL;
1729 	    l = topo_list_next(l)) {
1730 		if (l->tul_device != DI_NODE_NIL)
1731 			max++;
1732 	}
1733 
1734 	if (max == 0) {
1735 		return (0);
1736 	}
1737 
1738 	if ((ret = topo_node_range_create(mod, pn, USB_DEVICE, 0, max - 1)) !=
1739 	    0) {
1740 		return (-1);
1741 	}
1742 
1743 	for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL;
1744 	    l = topo_list_next(l)) {
1745 		if (l->tul_device != DI_NODE_NIL) {
1746 			topo_mod_dprintf(mod, "enumerating device on lport "
1747 			    "%u, log inst %" PRIu64 "", l->tul_portno, i);
1748 			if ((ret = topo_usb_enum_lport(mod, pn, port, l,
1749 			    i)) != 0) {
1750 				return (ret);
1751 			}
1752 			i++;
1753 		}
1754 	}
1755 
1756 	return (0);
1757 }
1758 
1759 static int
topo_usb_enum_controller(topo_mod_t * mod,tnode_t * pnode,topo_usb_controller_t * c,topo_instance_t base)1760 topo_usb_enum_controller(topo_mod_t *mod, tnode_t *pnode,
1761     topo_usb_controller_t *c, topo_instance_t base)
1762 {
1763 	int ret;
1764 	topo_usb_port_t *port;
1765 
1766 	if (c->tuc_enumed)
1767 		return (0);
1768 
1769 	c->tuc_enumed = B_TRUE;
1770 	if (c->tuc_nports == 0)
1771 		return (0);
1772 
1773 	for (port = topo_list_next(&c->tuc_ports); port != NULL;
1774 	    port = topo_list_next(port)) {
1775 		tnode_t *tn;
1776 		if ((ret = port_create_usb(mod, pnode, base, &tn)) != 0)
1777 			return (ret);
1778 
1779 		if ((ret = topo_usb_port_properties(mod, tn, port)) != 0) {
1780 			return (ret);
1781 		}
1782 
1783 		if ((ret = topo_usb_enum_device(mod, tn, port)) != 0)
1784 			return (ret);
1785 
1786 		base++;
1787 	}
1788 
1789 	return (0);
1790 }
1791 
1792 static int
topo_usb_enum_mobo(topo_mod_t * mod,tnode_t * pnode,topo_usb_t * usb)1793 topo_usb_enum_mobo(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb)
1794 {
1795 	int ret;
1796 	topo_usb_controller_t *c;
1797 	topo_instance_t inst = 0;
1798 
1799 	/*
1800 	 * First count the number of ports, so we can create the right range.
1801 	 * Then go back and actually create things. Some of the ports here may
1802 	 * be actually on the chassis, that's OK, we don't mind over counting
1803 	 * here.
1804 	 */
1805 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1806 	    c = topo_list_next(c)) {
1807 		inst += c->tuc_nports;
1808 	}
1809 
1810 	if ((ret = port_range_create(mod, pnode, 0, inst)) != 0) {
1811 		topo_mod_dprintf(mod, "failed to create port range [0, %"
1812 		    PRIu64 ") for mobo", inst);
1813 		return (ret);
1814 	}
1815 
1816 	inst = 0;
1817 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1818 	    c = topo_list_next(c)) {
1819 		if (c->tuc_enumed)
1820 			continue;
1821 		if ((ret = topo_usb_enum_controller(mod, pnode, c, inst)) !=
1822 		    0) {
1823 			return (ret);
1824 		}
1825 		inst += c->tuc_nports;
1826 	}
1827 
1828 	return (0);
1829 }
1830 
1831 static int
topo_usb_enum_pci(topo_mod_t * mod,tnode_t * pnode,topo_usb_t * usb,di_node_t din)1832 topo_usb_enum_pci(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb,
1833     di_node_t din)
1834 {
1835 	int ret;
1836 	topo_usb_controller_t *c;
1837 
1838 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1839 	    c = topo_list_next(c)) {
1840 		if (din == c->tuc_devinfo) {
1841 			break;
1842 		}
1843 	}
1844 
1845 	if (c == NULL) {
1846 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1847 	}
1848 
1849 	if ((ret = port_range_create(mod, pnode, 0, c->tuc_nports)) != 0) {
1850 		topo_mod_dprintf(mod, "failed to create port range [0, %u) "
1851 		    "for controller %s", c->tuc_nports, c->tuc_path);
1852 		return (ret);
1853 	}
1854 
1855 	return (topo_usb_enum_controller(mod, pnode, c, 0));
1856 }
1857 
1858 static int
topo_usb_enum_chassis(topo_mod_t * mod,tnode_t * pnode,topo_usb_t * usb)1859 topo_usb_enum_chassis(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb)
1860 {
1861 	int ret;
1862 	topo_usb_port_t *p;
1863 	topo_instance_t base = 0;
1864 
1865 	if (usb->tu_nchassis_ports == 0)
1866 		return (0);
1867 
1868 	if ((ret = port_range_create(mod, pnode, 0, usb->tu_nchassis_ports)) !=
1869 	    0) {
1870 		topo_mod_dprintf(mod, "failed to create port range [0, %u) "
1871 		    "for chassis", usb->tu_nchassis_ports);
1872 		return (ret);
1873 	}
1874 
1875 	for (p = topo_list_next(&usb->tu_chassis_ports); p != NULL;
1876 	    p = topo_list_next(p)) {
1877 		tnode_t *tn;
1878 		if ((ret = port_create_usb(mod, pnode, base, &tn)) != 0)
1879 			return (ret);
1880 
1881 		if ((ret = topo_usb_port_properties(mod, tn, p)) != 0) {
1882 			return (ret);
1883 		}
1884 
1885 		if ((ret = topo_usb_enum_device(mod, tn, p)) != 0)
1886 			return (ret);
1887 
1888 		base++;
1889 	}
1890 
1891 	return (0);
1892 }
1893 
1894 /* ARGSUSED */
1895 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)1896 topo_usb_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
1897     topo_instance_t min, topo_instance_t max, void *modarg, void *data)
1898 {
1899 	topo_usb_t *usb;
1900 	topo_usb_type_t type;
1901 
1902 	if (strcmp(name, USB_PCI) == 0) {
1903 		type = TOPO_USB_PCI;
1904 	} else if (strcmp(name, USB_MOBO) == 0) {
1905 		type = TOPO_USB_MOBO;
1906 	} else if (strcmp(name, USB_CHASSIS) == 0) {
1907 		type = TOPO_USB_CHASSIS;
1908 	} else {
1909 		topo_mod_dprintf(mod, "usb_enum: asked to enumerate unknown "
1910 		    "component: %s\n", name);
1911 		return (-1);
1912 	}
1913 
1914 	if (type == TOPO_USB_PCI && data == NULL) {
1915 		topo_mod_dprintf(mod, "usb_enum: missing argument to "
1916 		    "PCI controller enum");
1917 		return (-1);
1918 	} else if (type != TOPO_USB_PCI && data != NULL) {
1919 		topo_mod_dprintf(mod, "extraneous argument to non-controller "
1920 		    "enum %s", name);
1921 		return (-1);
1922 	}
1923 
1924 	if ((usb = topo_mod_getspecific(mod)) == NULL) {
1925 		return (-1);
1926 	}
1927 
1928 	if (!usb->tu_enum_done) {
1929 		if (topo_usb_gather(mod, usb, pnode) != 0)
1930 			return (-1);
1931 		usb->tu_enum_done = B_TRUE;
1932 	}
1933 
1934 	/*
1935 	 * Now that we've built up the topo nodes, enumerate the specific nodes
1936 	 * based on the requested type.
1937 	 */
1938 	if (type == TOPO_USB_PCI) {
1939 		return (topo_usb_enum_pci(mod, pnode, usb, data));
1940 	} else if (type == TOPO_USB_MOBO) {
1941 		return (topo_usb_enum_mobo(mod, pnode, usb));
1942 	} else if (type == TOPO_USB_CHASSIS) {
1943 		return (topo_usb_enum_chassis(mod, pnode, usb));
1944 	}
1945 
1946 	return (0);
1947 }
1948 
1949 static const topo_modops_t usb_ops = {
1950 	topo_usb_enum, NULL
1951 };
1952 
1953 static topo_modinfo_t usb_mod = {
1954 	USB, FM_FMRI_SCHEME_HC, USB_VERSION, &usb_ops
1955 };
1956 
1957 static void
topo_usb_port_free(topo_mod_t * mod,topo_usb_port_t * p)1958 topo_usb_port_free(topo_mod_t *mod, topo_usb_port_t *p)
1959 {
1960 	topo_usb_lport_t *lport;
1961 
1962 	while ((lport = topo_list_next(&p->tup_lports)) != NULL) {
1963 		topo_usb_port_t *child;
1964 
1965 		topo_list_delete(&p->tup_lports, lport);
1966 		while ((child = topo_list_next(&lport->tul_ports)) != NULL) {
1967 			topo_list_delete(&lport->tul_ports, child);
1968 			topo_usb_port_free(mod, child);
1969 		}
1970 		topo_mod_free(mod, lport, sizeof (topo_usb_lport_t));
1971 	}
1972 
1973 	topo_mod_free(mod, p, sizeof (topo_usb_port_t));
1974 }
1975 
1976 static void
topo_usb_free(topo_mod_t * mod,topo_usb_t * usb)1977 topo_usb_free(topo_mod_t *mod, topo_usb_t *usb)
1978 {
1979 	topo_usb_controller_t *c;
1980 	topo_usb_port_t *p;
1981 
1982 	if (usb == NULL)
1983 		return;
1984 
1985 	while ((p = topo_list_next(&usb->tu_chassis_ports)) != NULL) {
1986 		topo_list_delete(&usb->tu_chassis_ports, p);
1987 		topo_usb_port_free(mod, p);
1988 	}
1989 
1990 	while ((c = topo_list_next(&usb->tu_controllers)) != NULL) {
1991 
1992 		topo_list_delete(&usb->tu_controllers, c);
1993 		di_devfs_path_free(c->tuc_path);
1994 
1995 		while ((p = topo_list_next(&c->tuc_ports)) != NULL) {
1996 			topo_list_delete(&c->tuc_ports, p);
1997 			topo_usb_port_free(mod, p);
1998 		}
1999 		topo_mod_free(mod, c, sizeof (topo_usb_controller_t));
2000 	}
2001 
2002 	topo_usb_free_metadata(mod, &usb->tu_metadata);
2003 
2004 	/*
2005 	 * The devinfo handle came from fm, don't do anything ourselevs.
2006 	 */
2007 	usb->tu_devinfo = DI_NODE_NIL;
2008 
2009 	topo_mod_free(mod, usb, sizeof (topo_usb_t));
2010 }
2011 
2012 static topo_usb_t *
topo_usb_alloc(topo_mod_t * mod)2013 topo_usb_alloc(topo_mod_t *mod)
2014 {
2015 	topo_usb_t *usb = NULL;
2016 
2017 	if ((usb = topo_mod_zalloc(mod, sizeof (topo_usb_t))) == NULL) {
2018 		goto free;
2019 	}
2020 
2021 	if ((usb->tu_devinfo = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
2022 		goto free;
2023 	}
2024 
2025 	return (usb);
2026 
2027 free:
2028 	topo_usb_free(mod, usb);
2029 	return (NULL);
2030 }
2031 
2032 int
_topo_init(topo_mod_t * mod,topo_version_t version)2033 _topo_init(topo_mod_t *mod, topo_version_t version)
2034 {
2035 	topo_usb_t *usb;
2036 
2037 	if (getenv("TOPOUSBDEBUG") != NULL)
2038 		topo_mod_setdebug(mod);
2039 
2040 	topo_mod_dprintf(mod, "_mod_init: initializing %s enumerator\n", USB);
2041 
2042 	if (version != USB_VERSION) {
2043 		return (-1);
2044 	}
2045 
2046 	if ((usb = topo_usb_alloc(mod)) == NULL) {
2047 		return (-1);
2048 	}
2049 
2050 	if (topo_mod_register(mod, &usb_mod, TOPO_VERSION) != 0) {
2051 		topo_usb_free(mod, usb);
2052 		return (-1);
2053 	}
2054 
2055 	topo_mod_setspecific(mod, usb);
2056 
2057 	return (0);
2058 }
2059 
2060 void
_topo_fini(topo_mod_t * mod)2061 _topo_fini(topo_mod_t *mod)
2062 {
2063 	topo_usb_free(mod, topo_mod_getspecific(mod));
2064 	topo_mod_setspecific(mod, NULL);
2065 }
2066