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