1672fc84aSRobert Mustacchi /*
2672fc84aSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3672fc84aSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4672fc84aSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5672fc84aSRobert Mustacchi  * 1.0 of the CDDL.
6672fc84aSRobert Mustacchi  *
7672fc84aSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8672fc84aSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9672fc84aSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10672fc84aSRobert Mustacchi  */
11672fc84aSRobert Mustacchi 
12672fc84aSRobert Mustacchi /*
130d2006e4SRobert Mustacchi  * Copyright (c) 2019, Joyent, Inc.
14672fc84aSRobert Mustacchi  */
15672fc84aSRobert Mustacchi 
16672fc84aSRobert Mustacchi /*
17672fc84aSRobert Mustacchi  * The purpose of this module is to build topology information for USB devices.
18672fc84aSRobert Mustacchi  * USB devices are more complicated to build topology information for, as there
19672fc84aSRobert Mustacchi  * are multiple sources of information needed to correctly understand the
20672fc84aSRobert Mustacchi  * topology, and the way they present themselves is not always straightforward.
21672fc84aSRobert Mustacchi  *
22672fc84aSRobert Mustacchi  * We enumerate two different types of devices:
23672fc84aSRobert Mustacchi  *
24672fc84aSRobert Mustacchi  *   o USB ports
25672fc84aSRobert Mustacchi  *   o USB devices
26672fc84aSRobert Mustacchi  *
27672fc84aSRobert Mustacchi  * A USB port represents a logical port, while a USB device represents an actual
28672fc84aSRobert Mustacchi  * device that's been plugged in. If a device is a hub, then we'll enumerate
29672fc84aSRobert Mustacchi  * that device as well.
30672fc84aSRobert Mustacchi  *
31672fc84aSRobert Mustacchi  * Now, some basics. There are several different USB controllers that exist in
32672fc84aSRobert Mustacchi  * the system. Some are part of the chipset, while others may be present via
33672fc84aSRobert Mustacchi  * add-on cards. The system interfaces initially with USB devices through a host
34672fc84aSRobert Mustacchi  * controller. Prior to USB 3.0/xhci, a single controller only supported a
35672fc84aSRobert Mustacchi  * single protocol. With USB 3.0, it is possible for a port to share wiring with
36672fc84aSRobert Mustacchi  * both USB 2.0 devices and USB 3.0 devices. However, to the host controller
37672fc84aSRobert Mustacchi  * this appears as two different logical ports.
38672fc84aSRobert Mustacchi  *
39672fc84aSRobert Mustacchi  * To make matters worse, during the transition to USB 3, the ports that were
40672fc84aSRobert Mustacchi  * controlled could be routed to and from a USB 2 controller to a USB 3
41672fc84aSRobert Mustacchi  * controller. This means that there are a lot of ways for ports to overlap.
42672fc84aSRobert Mustacchi  *
43672fc84aSRobert Mustacchi  * In the first case, controllers define a way to perform this mapping by
44672fc84aSRobert Mustacchi  * leveraging ACPI information. Of course, this only helps us if the platform
45672fc84aSRobert Mustacchi  * provides ACPI information, which it may not. When we do know that two ports
46672fc84aSRobert Mustacchi  * are actually the same port, either because of ACPI or because of a
47672fc84aSRobert Mustacchi  * product-specific mapping file, then we'll use that to say two ports are the
48672fc84aSRobert Mustacchi  * same. Otherwise, we'll enumerate them as two separate logical ports.
49672fc84aSRobert Mustacchi  *
50672fc84aSRobert Mustacchi  * To perform the actual enumeration, the first time we're asked to enumerate a
51672fc84aSRobert Mustacchi  * node, we go through and put together an entire picture of all of the USB
52672fc84aSRobert Mustacchi  * devices in the system. This is done so we can make sure to enumerate devices
53672fc84aSRobert Mustacchi  * under specific devices.  The actual topology is determined in a few different
54672fc84aSRobert Mustacchi  * passes.
55672fc84aSRobert Mustacchi  *
56672fc84aSRobert Mustacchi  * Before we walk any trees, we look to see if we have a topo USB metadata file
57672fc84aSRobert Mustacchi  * and if present, load it. However, we do not apply any information from it.
58672fc84aSRobert Mustacchi  *
59672fc84aSRobert Mustacchi  * The first pass uses the devinfo tree to determine all of the USB controllers
60672fc84aSRobert Mustacchi  * and devices that are in the system. We use properties in the devices tree to
61672fc84aSRobert Mustacchi  * identify whether items are a root hub. When a root hub is found, we walk all
62672fc84aSRobert Mustacchi  * of its children and make a note of all of the logical ports under it.
63672fc84aSRobert Mustacchi  *
64672fc84aSRobert Mustacchi  * Next, we walk the information provided by ACPI to try and reduplicate
65672fc84aSRobert Mustacchi  * information about the ports on the system. If the USB topology metadata tells
66672fc84aSRobert Mustacchi  * us that we should not skip ACPI, then we use it. This is done by walking the
67672fc84aSRobert Mustacchi  * /devices/fw tree, looking for USB nodes and then linking them to their
68672fc84aSRobert Mustacchi  * corresponding entries found from the first devinfo walk.
69672fc84aSRobert Mustacchi  *
70672fc84aSRobert Mustacchi  * Finally, we go back and apply metadata to ports that match.
71672fc84aSRobert Mustacchi  *
72672fc84aSRobert Mustacchi  *
73672fc84aSRobert Mustacchi  * To logically keep track of all of this, we have several different structures:
74672fc84aSRobert Mustacchi  *
75672fc84aSRobert Mustacchi  *  topo_usb_controller_t  - Represents a physical controller.
76672fc84aSRobert Mustacchi  *  topo_usb_port_t	   - Represents a physical port. This is a synthetic
77672fc84aSRobert Mustacchi  *			     construct that we put together based on ACPI
78672fc84aSRobert Mustacchi  *			     information.
79672fc84aSRobert Mustacchi  *  topo_usb_lport_t	   - Represents a logical port. This is what the OS
80672fc84aSRobert Mustacchi  *			     actually detects and sees. Each logical port
81672fc84aSRobert Mustacchi  *			     belongs to a corresponding topo_usb_port_t.
82672fc84aSRobert Mustacchi  *  topo_usb_t		   - Represents the overall topology enumeration state.
83672fc84aSRobert Mustacchi  *
84672fc84aSRobert Mustacchi  *
85672fc84aSRobert Mustacchi  * This topo module is invoked at three different points by the surrounding code
86672fc84aSRobert Mustacchi  * and logic. Specifically:
87672fc84aSRobert Mustacchi  *
88672fc84aSRobert Mustacchi  *   * Dynamically by the pcibus enumerator when we encounter PCI add on cards
89672fc84aSRobert Mustacchi  *     which are present in a physical slot. Traditional chipset devices are not
90672fc84aSRobert Mustacchi  *     considered a part of this.
91672fc84aSRobert Mustacchi  *
92672fc84aSRobert Mustacchi  *   * Statically under the motherboard. All ports that don't belong to a PCI
93672fc84aSRobert Mustacchi  *     device are assumed to belong under the motherboard, unless a
94672fc84aSRobert Mustacchi  *     platform-specific topology map maps them under the chassis.
95672fc84aSRobert Mustacchi  *
96672fc84aSRobert Mustacchi  *   * Statically under the chassis. Ports are only placed under the chassis if
97672fc84aSRobert Mustacchi  *     a platform-specific topology file indicates that the port is a part of
98672fc84aSRobert Mustacchi  *     the chassis.
99672fc84aSRobert Mustacchi  */
100672fc84aSRobert Mustacchi 
101672fc84aSRobert Mustacchi #include <libdevinfo.h>
102672fc84aSRobert Mustacchi #include <strings.h>
103672fc84aSRobert Mustacchi #include <sys/types.h>
104672fc84aSRobert Mustacchi #include <sys/stat.h>
105672fc84aSRobert Mustacchi #include <fcntl.h>
106672fc84aSRobert Mustacchi #include <dirent.h>
107672fc84aSRobert Mustacchi #include <sys/debug.h>
108672fc84aSRobert Mustacchi #include <unistd.h>
109672fc84aSRobert Mustacchi 
110672fc84aSRobert Mustacchi #include <sys/fm/protocol.h>
111672fc84aSRobert Mustacchi #include <fm/topo_mod.h>
112672fc84aSRobert Mustacchi #include <fm/topo_list.h>
113672fc84aSRobert Mustacchi #include <fm/topo_method.h>
114672fc84aSRobert Mustacchi 
115672fc84aSRobert Mustacchi #include <topo_port.h>
116672fc84aSRobert Mustacchi 
117672fc84aSRobert Mustacchi #include "topo_usb.h"
118672fc84aSRobert Mustacchi #include "topo_usb_int.h"
119672fc84aSRobert Mustacchi 
120672fc84aSRobert Mustacchi typedef enum topo_usb_type {
121672fc84aSRobert Mustacchi 	TOPO_USB_PCI,
122672fc84aSRobert Mustacchi 	TOPO_USB_MOBO,
123672fc84aSRobert Mustacchi 	TOPO_USB_CHASSIS
124672fc84aSRobert Mustacchi } topo_usb_type_t;
125672fc84aSRobert Mustacchi 
126672fc84aSRobert Mustacchi typedef enum topo_usb_cdrv {
127672fc84aSRobert Mustacchi 	TOPO_USB_D_UNKNOWN,
128672fc84aSRobert Mustacchi 	TOPO_USB_D_UHCI,
129672fc84aSRobert Mustacchi 	TOPO_USB_D_OHCI,
130672fc84aSRobert Mustacchi 	TOPO_USB_D_EHCI,
131672fc84aSRobert Mustacchi 	TOPO_USB_D_XHCI
132672fc84aSRobert Mustacchi } topo_usb_cdrv_t;
133672fc84aSRobert Mustacchi 
134672fc84aSRobert Mustacchi typedef enum topo_usb_protocol {
135672fc84aSRobert Mustacchi 	TOPO_USB_P_UNKNOWN,
136672fc84aSRobert Mustacchi 	TOPO_USB_P_1x,
137672fc84aSRobert Mustacchi 	TOPO_USB_P_20,
138672fc84aSRobert Mustacchi 	TOPO_USB_P_30,
139672fc84aSRobert Mustacchi 	TOPO_USB_P_31
140672fc84aSRobert Mustacchi } topo_usb_protocol_t;
141672fc84aSRobert Mustacchi 
142672fc84aSRobert Mustacchi typedef enum topo_usb_port_connected {
143672fc84aSRobert Mustacchi 	TOPO_USB_C_UNKNOWN,
144672fc84aSRobert Mustacchi 	TOPO_USB_C_DISCONNECTED,
145672fc84aSRobert Mustacchi 	TOPO_USB_C_CONNECTED
146672fc84aSRobert Mustacchi } topo_usb_port_connected_t;
147672fc84aSRobert Mustacchi 
148672fc84aSRobert Mustacchi typedef struct topo_usb_port {
149672fc84aSRobert Mustacchi 	topo_list_t	tup_link;
150672fc84aSRobert Mustacchi 	uint_t		tup_nlports;
151672fc84aSRobert Mustacchi 	topo_list_t	tup_lports;
152672fc84aSRobert Mustacchi 	boolean_t	tup_pld_valid;
153672fc84aSRobert Mustacchi 	acpi_pld_info_t	tup_pld;
154672fc84aSRobert Mustacchi 	uint_t		tup_port_type;
155672fc84aSRobert Mustacchi 	topo_usb_port_connected_t	tup_port_connected;
156672fc84aSRobert Mustacchi 	topo_usb_meta_port_t	*tup_meta;
157672fc84aSRobert Mustacchi } topo_usb_port_t;
158672fc84aSRobert Mustacchi 
159672fc84aSRobert Mustacchi typedef struct topo_usb_lport {
160672fc84aSRobert Mustacchi 	topo_list_t		tul_link;
161672fc84aSRobert Mustacchi 	uint_t			tul_portno;
162672fc84aSRobert Mustacchi 	topo_usb_protocol_t	tul_protocol;
163672fc84aSRobert Mustacchi 	di_node_t		tul_device;
164672fc84aSRobert Mustacchi 	di_node_t		tul_acpi_device;
165672fc84aSRobert Mustacchi 	topo_usb_port_t		*tul_port;
166672fc84aSRobert Mustacchi 	uint_t			tul_nhubd_ports;
167672fc84aSRobert Mustacchi 	uint_t			tul_nports;
168672fc84aSRobert Mustacchi 	topo_list_t		tul_ports;
169672fc84aSRobert Mustacchi 	char			tul_name[PATH_MAX];
170672fc84aSRobert Mustacchi 	const char		*tul_acpi_name;
171672fc84aSRobert Mustacchi } topo_usb_lport_t;
172672fc84aSRobert Mustacchi 
173672fc84aSRobert Mustacchi typedef struct topo_usb_controller {
174672fc84aSRobert Mustacchi 	topo_list_t	tuc_link;
175672fc84aSRobert Mustacchi 	di_node_t	tuc_devinfo;
176672fc84aSRobert Mustacchi 	char		*tuc_path;
177672fc84aSRobert Mustacchi 	char		*tuc_acpi_path;
178672fc84aSRobert Mustacchi 	char		tuc_name[PATH_MAX];
179672fc84aSRobert Mustacchi 	topo_usb_cdrv_t	tuc_driver;
180672fc84aSRobert Mustacchi 	/*
181672fc84aSRobert Mustacchi 	 * Number of actual ports we've created (some of the logical ports are
182672fc84aSRobert Mustacchi 	 * deduped).
183672fc84aSRobert Mustacchi 	 */
184672fc84aSRobert Mustacchi 	uint_t		tuc_nports;
185672fc84aSRobert Mustacchi 	topo_list_t	tuc_ports;
186672fc84aSRobert Mustacchi 	/*
187672fc84aSRobert Mustacchi 	 * Total number of logical ports we expect to exist on this controller.
188672fc84aSRobert Mustacchi 	 * This may be greater than the number of actual ports we've created
189672fc84aSRobert Mustacchi 	 * under it because some physical ports represent more than one logical
190672fc84aSRobert Mustacchi 	 * port (xhci with USB2/3).
191672fc84aSRobert Mustacchi 	 */
192672fc84aSRobert Mustacchi 	uint_t		tuc_nhubd_ports;
193672fc84aSRobert Mustacchi 	/*
194672fc84aSRobert Mustacchi 	 * Keep track of port number and offset information. This is only done
195672fc84aSRobert Mustacchi 	 * for xhci.
196672fc84aSRobert Mustacchi 	 */
197672fc84aSRobert Mustacchi 	uint_t		tuc_nusb20;
198672fc84aSRobert Mustacchi 	uint_t		tuc_fusb20;
199672fc84aSRobert Mustacchi 	uint_t		tuc_nusb30;
200672fc84aSRobert Mustacchi 	uint_t		tuc_fusb30;
201672fc84aSRobert Mustacchi 	uint_t		tuc_nusb31;
202672fc84aSRobert Mustacchi 	uint_t		tuc_fusb31;
203672fc84aSRobert Mustacchi 	boolean_t	tuc_enumed;
204672fc84aSRobert Mustacchi } topo_usb_controller_t;
205672fc84aSRobert Mustacchi 
206672fc84aSRobert Mustacchi typedef struct topo_usb {
207672fc84aSRobert Mustacchi 	topo_list_t	tu_controllers;
208672fc84aSRobert Mustacchi 	boolean_t	tu_enum_done;
209672fc84aSRobert Mustacchi 	di_node_t	tu_devinfo;
210672fc84aSRobert Mustacchi 	topo_list_t	tu_metadata;
211672fc84aSRobert Mustacchi 	topo_usb_meta_flags_t	tu_meta_flags;
212672fc84aSRobert Mustacchi 	topo_list_t	tu_chassis_ports;
213672fc84aSRobert Mustacchi 	uint_t		tu_nchassis_ports;
214672fc84aSRobert Mustacchi } topo_usb_t;
215672fc84aSRobert Mustacchi 
216672fc84aSRobert Mustacchi typedef struct topo_usb_devcfg_arg {
217672fc84aSRobert Mustacchi 	topo_usb_t	*tda_usb;
218672fc84aSRobert Mustacchi 	topo_mod_t	*tda_mod;
219672fc84aSRobert Mustacchi 	boolean_t	tda_fatal;
220672fc84aSRobert Mustacchi } topo_usb_devcfg_arg_t;
221672fc84aSRobert Mustacchi 
222672fc84aSRobert Mustacchi static const topo_pgroup_info_t topo_usb_port_pgroup = {
223672fc84aSRobert Mustacchi 	TOPO_PGROUP_USB_PORT,
224672fc84aSRobert Mustacchi 	TOPO_STABILITY_PRIVATE,
225672fc84aSRobert Mustacchi 	TOPO_STABILITY_PRIVATE,
226672fc84aSRobert Mustacchi 	1
227672fc84aSRobert Mustacchi };
228672fc84aSRobert Mustacchi 
229672fc84aSRobert Mustacchi static const topo_pgroup_info_t topo_io_pgroup = {
230672fc84aSRobert Mustacchi 	TOPO_PGROUP_IO,
231672fc84aSRobert Mustacchi 	TOPO_STABILITY_PRIVATE,
232672fc84aSRobert Mustacchi 	TOPO_STABILITY_PRIVATE,
233672fc84aSRobert Mustacchi 	1
234672fc84aSRobert Mustacchi };
235672fc84aSRobert Mustacchi 
236672fc84aSRobert Mustacchi static const topo_pgroup_info_t topo_binding_pgroup = {
237672fc84aSRobert Mustacchi 	TOPO_PGROUP_BINDING,
238672fc84aSRobert Mustacchi 	TOPO_STABILITY_PRIVATE,
239672fc84aSRobert Mustacchi 	TOPO_STABILITY_PRIVATE,
240672fc84aSRobert Mustacchi 	1
241672fc84aSRobert Mustacchi };
242672fc84aSRobert Mustacchi 
243672fc84aSRobert Mustacchi static const topo_pgroup_info_t topo_usb_props_pgroup = {
244672fc84aSRobert Mustacchi 	TOPO_PGROUP_USB_PROPS,
245672fc84aSRobert Mustacchi 	TOPO_STABILITY_PRIVATE,
246672fc84aSRobert Mustacchi 	TOPO_STABILITY_PRIVATE,
247672fc84aSRobert Mustacchi 	1
248672fc84aSRobert Mustacchi };
249672fc84aSRobert Mustacchi 
250672fc84aSRobert Mustacchi /* Required forwards */
251672fc84aSRobert Mustacchi static int topo_usb_enum_device(topo_mod_t *, tnode_t *, topo_usb_port_t *);
252672fc84aSRobert Mustacchi 
253672fc84aSRobert Mustacchi /*
254672fc84aSRobert Mustacchi  * Defines the maximum number of USB ports that can exist. Ports are basically
255672fc84aSRobert Mustacchi  * defined by a uint8_t, meaning that we can go up to UINT8_MAX inclusively.
256672fc84aSRobert Mustacchi  */
257672fc84aSRobert Mustacchi #define	USB_TOPO_PORT_MAX	256
258672fc84aSRobert Mustacchi 
259672fc84aSRobert Mustacchi /*
260672fc84aSRobert Mustacchi  * Default value to indicate that a USB port has no valid type.
261672fc84aSRobert Mustacchi  */
262672fc84aSRobert Mustacchi #define	USB_TOPO_PORT_TYPE_DEFAULT	0xff
263672fc84aSRobert Mustacchi 
264672fc84aSRobert Mustacchi /*
265672fc84aSRobert Mustacchi  * These come from the ACPI 6.2 / Table 9-290 UPC Return Package Values.
266672fc84aSRobert Mustacchi  */
267672fc84aSRobert Mustacchi static const char *
topo_usb_port_type_to_string(int type)268672fc84aSRobert Mustacchi topo_usb_port_type_to_string(int type)
269672fc84aSRobert Mustacchi {
270672fc84aSRobert Mustacchi 	switch (type) {
271672fc84aSRobert Mustacchi 	case 0x00:
272672fc84aSRobert Mustacchi 		return ("Type A connector");
273672fc84aSRobert Mustacchi 	case 0x01:
274672fc84aSRobert Mustacchi 		return ("Mini-AB connector");
275672fc84aSRobert Mustacchi 	case 0x02:
276672fc84aSRobert Mustacchi 		return ("ExpressCard");
277672fc84aSRobert Mustacchi 	case 0x03:
278672fc84aSRobert Mustacchi 		return ("USB 3 Standard-A connector");
279672fc84aSRobert Mustacchi 	case 0x04:
280672fc84aSRobert Mustacchi 		return ("USB 3 Standard-B connector");
281672fc84aSRobert Mustacchi 	case 0x05:
282672fc84aSRobert Mustacchi 		return ("USB 3 Micro-B connector");
283672fc84aSRobert Mustacchi 	case 0x06:
284672fc84aSRobert Mustacchi 		return ("USB 3 Micro-AB connector");
285672fc84aSRobert Mustacchi 	case 0x07:
286672fc84aSRobert Mustacchi 		return ("USB 3 Power-B connector");
287672fc84aSRobert Mustacchi 	case 0x08:
288672fc84aSRobert Mustacchi 		return ("Type C connector - USB2-only");
289672fc84aSRobert Mustacchi 	case 0x09:
290672fc84aSRobert Mustacchi 		return ("Type C connector - USB2 and SS with Switch");
291672fc84aSRobert Mustacchi 	case 0x0A:
292672fc84aSRobert Mustacchi 		return ("Type C connector - USB2 and SS without Switch");
293672fc84aSRobert Mustacchi 	/* 0x0B->0xFE are reserved. Treat them like 0xFF */
294672fc84aSRobert Mustacchi 	case 0xFF:
295672fc84aSRobert Mustacchi 	default:
296672fc84aSRobert Mustacchi 		return ("Unknown");
297672fc84aSRobert Mustacchi 	}
298672fc84aSRobert Mustacchi }
299672fc84aSRobert Mustacchi 
300672fc84aSRobert Mustacchi /*
301672fc84aSRobert Mustacchi  * Searches the list of ports at a given layer (not recursively) for the
302672fc84aSRobert Mustacchi  * specific port id.
303672fc84aSRobert Mustacchi  */
304672fc84aSRobert Mustacchi static topo_usb_lport_t *
topo_usb_lport_find(topo_list_t * plist,uint_t logid)305672fc84aSRobert Mustacchi topo_usb_lport_find(topo_list_t *plist, uint_t logid)
306672fc84aSRobert Mustacchi {
307672fc84aSRobert Mustacchi 	topo_usb_port_t *p;
308672fc84aSRobert Mustacchi 
309672fc84aSRobert Mustacchi 	for (p = topo_list_next(plist); p != NULL; p = topo_list_next(p)) {
310672fc84aSRobert Mustacchi 		topo_usb_lport_t *l;
311672fc84aSRobert Mustacchi 
312672fc84aSRobert Mustacchi 		for (l = topo_list_next(&p->tup_lports); l != NULL;
313672fc84aSRobert Mustacchi 		    l = topo_list_next(l)) {
314672fc84aSRobert Mustacchi 			if (l->tul_portno == logid)
315672fc84aSRobert Mustacchi 				return (l);
316672fc84aSRobert Mustacchi 		}
317672fc84aSRobert Mustacchi 	}
318672fc84aSRobert Mustacchi 	return (NULL);
319672fc84aSRobert Mustacchi }
320672fc84aSRobert Mustacchi 
321672fc84aSRobert Mustacchi /*
322672fc84aSRobert Mustacchi  * Create an instance of a controller and seed the basic information.
323672fc84aSRobert Mustacchi  */
324672fc84aSRobert Mustacchi static topo_usb_controller_t *
topo_usb_controller_create(topo_mod_t * mod,topo_usb_t * usb,di_node_t node)325672fc84aSRobert Mustacchi topo_usb_controller_create(topo_mod_t *mod, topo_usb_t *usb, di_node_t node)
326672fc84aSRobert Mustacchi {
327672fc84aSRobert Mustacchi 	int *pcount, inst;
328672fc84aSRobert Mustacchi 	char *drvname, *acpi;
329672fc84aSRobert Mustacchi 	topo_usb_controller_t *c;
330672fc84aSRobert Mustacchi 
331672fc84aSRobert Mustacchi 	/*
332672fc84aSRobert Mustacchi 	 * If we can't get the port count or the driver, then this node is
333672fc84aSRobert Mustacchi 	 * uninteresting.
334672fc84aSRobert Mustacchi 	 */
335672fc84aSRobert Mustacchi 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-port-count",
336672fc84aSRobert Mustacchi 	    &pcount) != 1) {
337672fc84aSRobert Mustacchi 		return (NULL);
338672fc84aSRobert Mustacchi 	}
339672fc84aSRobert Mustacchi 
340672fc84aSRobert Mustacchi 	if ((drvname = di_driver_name(node)) == NULL ||
341672fc84aSRobert Mustacchi 	    (inst = di_instance(node) == -1))
342672fc84aSRobert Mustacchi 		return (NULL);
343672fc84aSRobert Mustacchi 
344672fc84aSRobert Mustacchi 	if ((c = topo_mod_zalloc(mod, sizeof (topo_usb_controller_t))) ==
345672fc84aSRobert Mustacchi 	    NULL || *pcount <= 0) {
346672fc84aSRobert Mustacchi 		return (NULL);
347672fc84aSRobert Mustacchi 	}
348672fc84aSRobert Mustacchi 
349672fc84aSRobert Mustacchi 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "acpi-namespace",
350672fc84aSRobert Mustacchi 	    &acpi) == 1) {
351672fc84aSRobert Mustacchi 		c->tuc_acpi_path = acpi;
352672fc84aSRobert Mustacchi 	}
353672fc84aSRobert Mustacchi 
354672fc84aSRobert Mustacchi 	c->tuc_nhubd_ports = (uint_t)*pcount;
355672fc84aSRobert Mustacchi 	c->tuc_devinfo = node;
356672fc84aSRobert Mustacchi 	c->tuc_path = di_devfs_path(node);
357672fc84aSRobert Mustacchi 	(void) snprintf(c->tuc_name, sizeof (c->tuc_name), "%s%d", drvname,
358672fc84aSRobert Mustacchi 	    inst);
359672fc84aSRobert Mustacchi 	if (strcmp(drvname, "xhci") == 0) {
360672fc84aSRobert Mustacchi 		int *p;
361672fc84aSRobert Mustacchi 
362672fc84aSRobert Mustacchi 		c->tuc_driver = TOPO_USB_D_XHCI;
363672fc84aSRobert Mustacchi 
364672fc84aSRobert Mustacchi 		/*
365672fc84aSRobert Mustacchi 		 * Grab the properties that we need so we can better do a port
366672fc84aSRobert Mustacchi 		 * speed mapping.
367672fc84aSRobert Mustacchi 		 */
368672fc84aSRobert Mustacchi 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
369672fc84aSRobert Mustacchi 		    "usb2.0-port-count", &p) == 1 && *p > 0) {
370672fc84aSRobert Mustacchi 			c->tuc_nusb20 = (uint_t)*p;
371672fc84aSRobert Mustacchi 		}
372672fc84aSRobert Mustacchi 
373672fc84aSRobert Mustacchi 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
374672fc84aSRobert Mustacchi 		    "usb2.0-first-port", &p) == 1 && *p > 0) {
375672fc84aSRobert Mustacchi 			c->tuc_fusb20 = (uint_t)*p;
376672fc84aSRobert Mustacchi 		}
377672fc84aSRobert Mustacchi 
378672fc84aSRobert Mustacchi 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
379672fc84aSRobert Mustacchi 		    "usb3.0-port-count", &p) == 1 && *p > 0) {
380672fc84aSRobert Mustacchi 			c->tuc_nusb30 = (uint_t)*p;
381672fc84aSRobert Mustacchi 		}
382672fc84aSRobert Mustacchi 
383672fc84aSRobert Mustacchi 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
384672fc84aSRobert Mustacchi 		    "usb3.0-first-port", &p) == 1 && *p > 0) {
385672fc84aSRobert Mustacchi 			c->tuc_fusb30 = (uint_t)*p;
386672fc84aSRobert Mustacchi 		}
387672fc84aSRobert Mustacchi 
388672fc84aSRobert Mustacchi 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
389672fc84aSRobert Mustacchi 		    "usb3.1-port-count", &p) == 1 && *p > 0) {
390672fc84aSRobert Mustacchi 			c->tuc_nusb31 = (uint_t)*p;
391672fc84aSRobert Mustacchi 		}
392672fc84aSRobert Mustacchi 
393672fc84aSRobert Mustacchi 		if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
394672fc84aSRobert Mustacchi 		    "usb3.1-first-port", &p) == 1 && *p > 0) {
395672fc84aSRobert Mustacchi 			c->tuc_fusb31 = (uint_t)*p;
396672fc84aSRobert Mustacchi 		}
397672fc84aSRobert Mustacchi 	} else if (strcmp(drvname, "ehci") == 0) {
398672fc84aSRobert Mustacchi 		c->tuc_driver = TOPO_USB_D_EHCI;
399672fc84aSRobert Mustacchi 	} else if (strcmp(drvname, "uhci") == 0) {
400672fc84aSRobert Mustacchi 		c->tuc_driver = TOPO_USB_D_UHCI;
401672fc84aSRobert Mustacchi 	} else if (strcmp(drvname, "ohci") == 0) {
402672fc84aSRobert Mustacchi 		c->tuc_driver = TOPO_USB_D_OHCI;
403672fc84aSRobert Mustacchi 	} else {
404672fc84aSRobert Mustacchi 		c->tuc_driver = TOPO_USB_D_UNKNOWN;
405672fc84aSRobert Mustacchi 	}
406672fc84aSRobert Mustacchi 	topo_list_append(&usb->tu_controllers, c);
407672fc84aSRobert Mustacchi 	topo_mod_dprintf(mod, "created new USB controller at %s", c->tuc_path);
408672fc84aSRobert Mustacchi 
409672fc84aSRobert Mustacchi 	return (c);
410672fc84aSRobert Mustacchi }
411672fc84aSRobert Mustacchi 
412672fc84aSRobert Mustacchi /*
413672fc84aSRobert Mustacchi  * Process this port and any others that might exist.
414672fc84aSRobert Mustacchi  */
415672fc84aSRobert Mustacchi static boolean_t
topo_usb_gather_acpi_port(topo_mod_t * mod,topo_usb_t * usb,topo_list_t * plist,uint_t * nports,topo_usb_controller_t * tuc,di_node_t portinfo)416672fc84aSRobert Mustacchi topo_usb_gather_acpi_port(topo_mod_t *mod, topo_usb_t *usb, topo_list_t *plist,
417672fc84aSRobert Mustacchi     uint_t *nports, topo_usb_controller_t *tuc, di_node_t portinfo)
418672fc84aSRobert Mustacchi {
419672fc84aSRobert Mustacchi 	int64_t *portno;
420672fc84aSRobert Mustacchi 	uchar_t *loc;
421672fc84aSRobert Mustacchi 	int loclen, *type;
422672fc84aSRobert Mustacchi 	char *acpi;
423672fc84aSRobert Mustacchi 	acpi_pld_info_t pld;
424672fc84aSRobert Mustacchi 	boolean_t pld_valid = B_FALSE;
425672fc84aSRobert Mustacchi 	topo_usb_port_t *port = NULL;
426672fc84aSRobert Mustacchi 	topo_usb_lport_t *lport;
427672fc84aSRobert Mustacchi 	di_node_t child;
428672fc84aSRobert Mustacchi 
429672fc84aSRobert Mustacchi 	/*
430672fc84aSRobert Mustacchi 	 * Get the port's address, it's a required value. Because this is coming
431672fc84aSRobert Mustacchi 	 * from firmware, we cannot trust the port's value to be correct.
432672fc84aSRobert Mustacchi 	 */
433672fc84aSRobert Mustacchi 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, portinfo, "acpi-address",
434672fc84aSRobert Mustacchi 	    &portno) != 1 || *portno < 1 || *portno >= USB_TOPO_PORT_MAX) {
435672fc84aSRobert Mustacchi 		return (B_FALSE);
436672fc84aSRobert Mustacchi 	}
437672fc84aSRobert Mustacchi 
438672fc84aSRobert Mustacchi 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, portinfo, "acpi-namespace",
439672fc84aSRobert Mustacchi 	    &acpi) != 1) {
440672fc84aSRobert Mustacchi 		return (B_FALSE);
441672fc84aSRobert Mustacchi 	}
442672fc84aSRobert Mustacchi 
443672fc84aSRobert Mustacchi 	/*
444672fc84aSRobert Mustacchi 	 * Check to see if we have any ACPI location information. If we do, we
445672fc84aSRobert Mustacchi 	 * can decode it.
446672fc84aSRobert Mustacchi 	 */
447672fc84aSRobert Mustacchi 	if ((loclen = di_prop_lookup_bytes(DDI_DEV_T_ANY, portinfo,
448672fc84aSRobert Mustacchi 	    "acpi-physical-location", &loc)) >= ACPI_PLD_REV1_BUFFER_SIZE &&
449672fc84aSRobert Mustacchi 	    usbtopo_decode_pld(loc, loclen, &pld)) {
450672fc84aSRobert Mustacchi 		pld_valid = B_TRUE;
451672fc84aSRobert Mustacchi 	}
452672fc84aSRobert Mustacchi 
453672fc84aSRobert Mustacchi 	/*
454672fc84aSRobert Mustacchi 	 * Find the corresponding lport. If this node doesn't happen to match
455672fc84aSRobert Mustacchi 	 * something we've enumerated from the hub. Warn about that fact and
456672fc84aSRobert Mustacchi 	 * consider this bad data.
457672fc84aSRobert Mustacchi 	 */
458672fc84aSRobert Mustacchi 	lport = topo_usb_lport_find(plist, (uint_t)*portno);
459672fc84aSRobert Mustacchi 	if (lport == NULL) {
460672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to find physical usb port for "
461672fc84aSRobert Mustacchi 		    "%s/%u", acpi, (uint_t)*portno);
462672fc84aSRobert Mustacchi 		return (B_TRUE);
463672fc84aSRobert Mustacchi 	}
464672fc84aSRobert Mustacchi 
465672fc84aSRobert Mustacchi 	if (lport->tul_acpi_device != DI_NODE_NIL) {
466672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "logical port already bound to %s, not "
467672fc84aSRobert Mustacchi 		    "binding to %s", lport->tul_acpi_name, acpi);
468672fc84aSRobert Mustacchi 		return (B_FALSE);
469672fc84aSRobert Mustacchi 	}
470672fc84aSRobert Mustacchi 
471672fc84aSRobert Mustacchi 	lport->tul_acpi_device = portinfo;
472672fc84aSRobert Mustacchi 	lport->tul_acpi_name = acpi;
473672fc84aSRobert Mustacchi 	port = lport->tul_port;
474672fc84aSRobert Mustacchi 
475672fc84aSRobert Mustacchi 	if (pld_valid) {
476672fc84aSRobert Mustacchi 		port->tup_pld_valid = B_TRUE;
477672fc84aSRobert Mustacchi 		port->tup_pld = pld;
478672fc84aSRobert Mustacchi 	}
479672fc84aSRobert Mustacchi 
480672fc84aSRobert Mustacchi 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, portinfo, "usb-port-type",
481672fc84aSRobert Mustacchi 	    &type) == 1 && *type >= 0) {
482672fc84aSRobert Mustacchi 		port->tup_port_type = *type;
483672fc84aSRobert Mustacchi 	} else {
484672fc84aSRobert Mustacchi 		port->tup_port_type = USB_TOPO_PORT_TYPE_DEFAULT;
485672fc84aSRobert Mustacchi 	}
486672fc84aSRobert Mustacchi 
487672fc84aSRobert Mustacchi 	if (di_prop_find(DDI_DEV_T_ANY, portinfo,
488672fc84aSRobert Mustacchi 	    "usb-port-connectable") != DI_PROP_NIL) {
489672fc84aSRobert Mustacchi 		port->tup_port_connected = TOPO_USB_C_CONNECTED;
490672fc84aSRobert Mustacchi 	} else {
491672fc84aSRobert Mustacchi 		port->tup_port_connected = TOPO_USB_C_DISCONNECTED;
492672fc84aSRobert Mustacchi 	}
493672fc84aSRobert Mustacchi 
494672fc84aSRobert Mustacchi 	for (child = di_child_node(portinfo); child != NULL;
495672fc84aSRobert Mustacchi 	    child = di_sibling_node(child)) {
496672fc84aSRobert Mustacchi 		const char *pname;
497672fc84aSRobert Mustacchi 
498672fc84aSRobert Mustacchi 		pname = di_node_name(child);
499672fc84aSRobert Mustacchi 		if (pname == NULL || strcmp(pname, "port") != 0) {
500672fc84aSRobert Mustacchi 			continue;
501672fc84aSRobert Mustacchi 		}
502672fc84aSRobert Mustacchi 
503672fc84aSRobert Mustacchi 		if (!topo_usb_gather_acpi_port(mod, usb, &lport->tul_ports,
504672fc84aSRobert Mustacchi 		    &lport->tul_nports, tuc, child)) {
505672fc84aSRobert Mustacchi 			return (B_FALSE);
506672fc84aSRobert Mustacchi 		}
507672fc84aSRobert Mustacchi 	}
508672fc84aSRobert Mustacchi 
509672fc84aSRobert Mustacchi 	topo_mod_dprintf(mod, "discovered %u ACPI usb child ports",
510672fc84aSRobert Mustacchi 	    lport->tul_nports);
511672fc84aSRobert Mustacchi 
512672fc84aSRobert Mustacchi 	return (B_TRUE);
513672fc84aSRobert Mustacchi }
514672fc84aSRobert Mustacchi 
515672fc84aSRobert Mustacchi /*
516672fc84aSRobert Mustacchi  * First, bootstrap all of our information by reading the ACPI information
517672fc84aSRobert Mustacchi  * exposed in the devinfo tree. All of the nodes we care about will be under
518672fc84aSRobert Mustacchi  * /fw/sb@XX/usbrootub@YYY/port@ZZZ
519672fc84aSRobert Mustacchi  */
520672fc84aSRobert Mustacchi static boolean_t
topo_usb_gather_acpi(topo_mod_t * mod,topo_usb_t * usb)521672fc84aSRobert Mustacchi topo_usb_gather_acpi(topo_mod_t *mod, topo_usb_t *usb)
522672fc84aSRobert Mustacchi {
523672fc84aSRobert Mustacchi 	di_node_t fwroot, sbnode;
524672fc84aSRobert Mustacchi 
525672fc84aSRobert Mustacchi 	/*
526672fc84aSRobert Mustacchi 	 * If we can't find the /fw node, that's fine. We may not have any ACPI
527672fc84aSRobert Mustacchi 	 * information on the system.
528672fc84aSRobert Mustacchi 	 */
529672fc84aSRobert Mustacchi 	fwroot = di_lookup_node(usb->tu_devinfo, "/fw");
530672fc84aSRobert Mustacchi 	if (fwroot == DI_NODE_NIL)
531672fc84aSRobert Mustacchi 		return (B_TRUE);
532672fc84aSRobert Mustacchi 
533672fc84aSRobert Mustacchi 	for (sbnode = di_child_node(fwroot); sbnode != DI_NODE_NIL;
534672fc84aSRobert Mustacchi 	    sbnode = di_sibling_node(sbnode)) {
535672fc84aSRobert Mustacchi 		const char *sbname;
536672fc84aSRobert Mustacchi 		di_node_t hub;
537672fc84aSRobert Mustacchi 
538672fc84aSRobert Mustacchi 		sbname = di_node_name(sbnode);
539672fc84aSRobert Mustacchi 		if (sbname == NULL || strcmp(sbname, "sb") != 0) {
540672fc84aSRobert Mustacchi 			continue;
541672fc84aSRobert Mustacchi 		}
542672fc84aSRobert Mustacchi 
543672fc84aSRobert Mustacchi 		for (hub = di_child_node(sbnode); hub != DI_NODE_NIL;
544672fc84aSRobert Mustacchi 		    hub = di_sibling_node(hub)) {
545672fc84aSRobert Mustacchi 			const char *hubname;
546672fc84aSRobert Mustacchi 			char *acpi;
547672fc84aSRobert Mustacchi 			topo_usb_controller_t *tuc;
548672fc84aSRobert Mustacchi 			di_node_t port;
549672fc84aSRobert Mustacchi 
550672fc84aSRobert Mustacchi 			hubname = di_node_name(hub);
551672fc84aSRobert Mustacchi 			if (hubname == NULL ||
552672fc84aSRobert Mustacchi 			    strcmp(hubname, "usbroothub") != 0) {
553672fc84aSRobert Mustacchi 				continue;
554672fc84aSRobert Mustacchi 			}
555672fc84aSRobert Mustacchi 
556672fc84aSRobert Mustacchi 			if (di_prop_lookup_strings(DDI_DEV_T_ANY, hub,
557672fc84aSRobert Mustacchi 			    "acpi-controller-name", &acpi) != 1) {
558672fc84aSRobert Mustacchi 				continue;
559672fc84aSRobert Mustacchi 			}
560672fc84aSRobert Mustacchi 
561672fc84aSRobert Mustacchi 			for (tuc = topo_list_next(&usb->tu_controllers);
562672fc84aSRobert Mustacchi 			    tuc != NULL;
563672fc84aSRobert Mustacchi 			    tuc = topo_list_next(tuc)) {
564672fc84aSRobert Mustacchi 				if (tuc->tuc_acpi_path != NULL &&
565672fc84aSRobert Mustacchi 				    strcmp(acpi, tuc->tuc_acpi_path) == 0)
566672fc84aSRobert Mustacchi 					break;
567672fc84aSRobert Mustacchi 			}
568672fc84aSRobert Mustacchi 
569672fc84aSRobert Mustacchi 			if (tuc == NULL) {
570672fc84aSRobert Mustacchi 				topo_mod_dprintf(mod, "failed to find USB "
571672fc84aSRobert Mustacchi 				    "controller for ACPI path %s", acpi);
572672fc84aSRobert Mustacchi 				continue;
573672fc84aSRobert Mustacchi 			}
574672fc84aSRobert Mustacchi 
575672fc84aSRobert Mustacchi 			for (port = di_child_node(hub); port != NULL;
576672fc84aSRobert Mustacchi 			    port = di_sibling_node(port)) {
577672fc84aSRobert Mustacchi 				const char *pname;
578672fc84aSRobert Mustacchi 
579672fc84aSRobert Mustacchi 				pname = di_node_name(port);
580672fc84aSRobert Mustacchi 				if (pname == NULL ||
581672fc84aSRobert Mustacchi 				    strcmp(pname, "port") != 0) {
582672fc84aSRobert Mustacchi 					continue;
583672fc84aSRobert Mustacchi 				}
584672fc84aSRobert Mustacchi 
585672fc84aSRobert Mustacchi 				if (!topo_usb_gather_acpi_port(mod, usb,
586672fc84aSRobert Mustacchi 				    &tuc->tuc_ports, &tuc->tuc_nports, tuc,
587672fc84aSRobert Mustacchi 				    port)) {
588672fc84aSRobert Mustacchi 					return (B_FALSE);
589672fc84aSRobert Mustacchi 				}
590672fc84aSRobert Mustacchi 			}
591672fc84aSRobert Mustacchi 
592672fc84aSRobert Mustacchi 			topo_mod_dprintf(mod, "found ACPI usb controller %s "
593672fc84aSRobert Mustacchi 			    "with %d top-level ports", tuc->tuc_path,
594672fc84aSRobert Mustacchi 			    tuc->tuc_nports);
595672fc84aSRobert Mustacchi 		}
596672fc84aSRobert Mustacchi 	}
597672fc84aSRobert Mustacchi 
598672fc84aSRobert Mustacchi 	return (B_TRUE);
599672fc84aSRobert Mustacchi }
600672fc84aSRobert Mustacchi 
601672fc84aSRobert Mustacchi static topo_usb_port_t *
topo_usb_port_create(topo_mod_t * mod,uint_t portno,const char * parent,char sep)602672fc84aSRobert Mustacchi topo_usb_port_create(topo_mod_t *mod, uint_t portno, const char *parent,
603672fc84aSRobert Mustacchi     char sep)
604672fc84aSRobert Mustacchi {
605672fc84aSRobert Mustacchi 	topo_usb_lport_t *l;
606672fc84aSRobert Mustacchi 	topo_usb_port_t *p;
607672fc84aSRobert Mustacchi 
608672fc84aSRobert Mustacchi 	if ((l = topo_mod_zalloc(mod, sizeof (topo_usb_lport_t))) == NULL) {
609672fc84aSRobert Mustacchi 		return (NULL);
610672fc84aSRobert Mustacchi 	}
611672fc84aSRobert Mustacchi 	l->tul_portno = portno;
612672fc84aSRobert Mustacchi 	if (snprintf(l->tul_name, sizeof (l->tul_name), "%s%c%u", parent, sep,
613672fc84aSRobert Mustacchi 	    portno) >= sizeof (l->tul_name)) {
614672fc84aSRobert Mustacchi 		topo_mod_free(mod, l, sizeof (topo_usb_lport_t));
615672fc84aSRobert Mustacchi 		return (NULL);
616672fc84aSRobert Mustacchi 	}
617672fc84aSRobert Mustacchi 
618672fc84aSRobert Mustacchi 	if ((p = topo_mod_zalloc(mod, sizeof (topo_usb_port_t))) == NULL) {
619672fc84aSRobert Mustacchi 		topo_mod_free(mod, l, sizeof (topo_usb_lport_t));
620672fc84aSRobert Mustacchi 		return (NULL);
621672fc84aSRobert Mustacchi 	}
622672fc84aSRobert Mustacchi 	l->tul_port = p;
623672fc84aSRobert Mustacchi 	p->tup_port_type = USB_TOPO_PORT_TYPE_DEFAULT;
624672fc84aSRobert Mustacchi 	topo_list_append(&p->tup_lports, l);
625672fc84aSRobert Mustacchi 	p->tup_nlports++;
626672fc84aSRobert Mustacchi 
627672fc84aSRobert Mustacchi 	return (p);
628672fc84aSRobert Mustacchi }
629672fc84aSRobert Mustacchi 
630672fc84aSRobert Mustacchi /*
631672fc84aSRobert Mustacchi  * Set the protocol of a port that belongs to a root hub.
632672fc84aSRobert Mustacchi  */
633672fc84aSRobert Mustacchi static void
topo_usb_set_rhub_port_protocol(topo_mod_t * mod,topo_usb_controller_t * tuc,topo_usb_lport_t * lport)634672fc84aSRobert Mustacchi topo_usb_set_rhub_port_protocol(topo_mod_t *mod, topo_usb_controller_t *tuc,
635672fc84aSRobert Mustacchi     topo_usb_lport_t *lport)
636672fc84aSRobert Mustacchi {
637672fc84aSRobert Mustacchi 	switch (tuc->tuc_driver) {
638672fc84aSRobert Mustacchi 	case TOPO_USB_D_XHCI:
639672fc84aSRobert Mustacchi 		break;
640672fc84aSRobert Mustacchi 	case TOPO_USB_D_UHCI:
641672fc84aSRobert Mustacchi 	case TOPO_USB_D_OHCI:
642672fc84aSRobert Mustacchi 		lport->tul_protocol = TOPO_USB_P_1x;
643672fc84aSRobert Mustacchi 		return;
644672fc84aSRobert Mustacchi 	case TOPO_USB_D_EHCI:
645672fc84aSRobert Mustacchi 		lport->tul_protocol = TOPO_USB_P_20;
646672fc84aSRobert Mustacchi 		return;
647672fc84aSRobert Mustacchi 	case TOPO_USB_D_UNKNOWN:
648672fc84aSRobert Mustacchi 	default:
649672fc84aSRobert Mustacchi 		lport->tul_protocol = TOPO_USB_P_UNKNOWN;
650672fc84aSRobert Mustacchi 		return;
651672fc84aSRobert Mustacchi 	}
652672fc84aSRobert Mustacchi 
653672fc84aSRobert Mustacchi 	/*
654672fc84aSRobert Mustacchi 	 * The xHCI controller can support multiple different, protocols. It
655672fc84aSRobert Mustacchi 	 * communicates this information to us via devinfo properties. It's
656672fc84aSRobert Mustacchi 	 * possible that a port that is within max ports is not within the range
657672fc84aSRobert Mustacchi 	 * here. If that's the case, we'll set it to unknown.
658672fc84aSRobert Mustacchi 	 */
659672fc84aSRobert Mustacchi 	if (lport->tul_portno >= tuc->tuc_fusb20 &&
660672fc84aSRobert Mustacchi 	    lport->tul_portno < tuc->tuc_fusb20 + tuc->tuc_nusb20) {
661672fc84aSRobert Mustacchi 		lport->tul_protocol = TOPO_USB_P_20;
662672fc84aSRobert Mustacchi 	} else if (lport->tul_portno >= tuc->tuc_fusb30 &&
663672fc84aSRobert Mustacchi 	    lport->tul_portno < tuc->tuc_fusb30 + tuc->tuc_nusb30) {
664672fc84aSRobert Mustacchi 		lport->tul_protocol = TOPO_USB_P_30;
665672fc84aSRobert Mustacchi 	} else if (lport->tul_portno >= tuc->tuc_fusb31 &&
666672fc84aSRobert Mustacchi 	    lport->tul_portno < tuc->tuc_fusb31 + tuc->tuc_nusb31) {
667672fc84aSRobert Mustacchi 		lport->tul_protocol = TOPO_USB_P_31;
668672fc84aSRobert Mustacchi 	} else {
669672fc84aSRobert Mustacchi 		lport->tul_protocol = TOPO_USB_P_UNKNOWN;
670672fc84aSRobert Mustacchi 	}
671672fc84aSRobert Mustacchi }
672672fc84aSRobert Mustacchi 
673672fc84aSRobert Mustacchi /*
674672fc84aSRobert Mustacchi  * We've found a node on the list. Attempt to find its corresponding port. If we
675672fc84aSRobert Mustacchi  * find a hub, then we will descend further down this part of the tree.
676672fc84aSRobert Mustacchi  */
677672fc84aSRobert Mustacchi static int
topo_usb_gather_devcfg_port(topo_mod_t * mod,topo_usb_controller_t * c,topo_list_t * plist,di_node_t node)678672fc84aSRobert Mustacchi topo_usb_gather_devcfg_port(topo_mod_t *mod, topo_usb_controller_t *c,
679672fc84aSRobert Mustacchi     topo_list_t *plist, di_node_t node)
680672fc84aSRobert Mustacchi {
681672fc84aSRobert Mustacchi 	int *vend, *reg, *nports;
682672fc84aSRobert Mustacchi 	topo_usb_lport_t *l;
683672fc84aSRobert Mustacchi 	char *drvname;
684672fc84aSRobert Mustacchi 
685672fc84aSRobert Mustacchi 	/*
686672fc84aSRobert Mustacchi 	 * Look for the presence of the usb-vendor-id property to determine
687672fc84aSRobert Mustacchi 	 * whether or not this is a usb device node. usba always adds this
688672fc84aSRobert Mustacchi 	 * to the devices that it enumerates.
689672fc84aSRobert Mustacchi 	 */
690672fc84aSRobert Mustacchi 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-vendor-id",
691672fc84aSRobert Mustacchi 	    &vend) != 1) {
692672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to find usb-vendor-id property "
693672fc84aSRobert Mustacchi 		    "for child");
694672fc84aSRobert Mustacchi 		return (0);
695672fc84aSRobert Mustacchi 	}
696672fc84aSRobert Mustacchi 
697672fc84aSRobert Mustacchi 	/*
698672fc84aSRobert Mustacchi 	 * For usb-devices, the reg property is one entry long and it has the
699672fc84aSRobert Mustacchi 	 * logical port that the controller sees.
700672fc84aSRobert Mustacchi 	 */
701672fc84aSRobert Mustacchi 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &reg) != 1 ||
702672fc84aSRobert Mustacchi 	    *reg <= 0) {
703672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "got bad \"reg\" property");
704672fc84aSRobert Mustacchi 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
705672fc84aSRobert Mustacchi 	}
706672fc84aSRobert Mustacchi 
707672fc84aSRobert Mustacchi 	if ((l = topo_usb_lport_find(plist, (uint_t)*reg)) == NULL) {
708672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to find topo_usb_lport_t for "
709672fc84aSRobert Mustacchi 		    "port %d", *reg);
710672fc84aSRobert Mustacchi 		return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
711672fc84aSRobert Mustacchi 	}
712672fc84aSRobert Mustacchi 
713672fc84aSRobert Mustacchi 	l->tul_device = node;
714672fc84aSRobert Mustacchi 
715672fc84aSRobert Mustacchi 	/*
716672fc84aSRobert Mustacchi 	 * Check to see if we have a hub and if so, process it.
717672fc84aSRobert Mustacchi 	 */
718672fc84aSRobert Mustacchi 	if ((drvname = di_driver_name(node)) != NULL &&
719672fc84aSRobert Mustacchi 	    strcmp(drvname, "hubd") == 0 &&
720672fc84aSRobert Mustacchi 	    di_prop_lookup_ints(DDI_DEV_T_ANY, node, "usb-port-count",
721672fc84aSRobert Mustacchi 	    &nports) == 1 && *nports >= 1) {
722672fc84aSRobert Mustacchi 		di_node_t child;
723672fc84aSRobert Mustacchi 
724672fc84aSRobert Mustacchi 		/*
725672fc84aSRobert Mustacchi 		 * First go through and try and discover and create all the
726672fc84aSRobert Mustacchi 		 * logical ports that exist. It is possible that these ports
727672fc84aSRobert Mustacchi 		 * already exist and that we have ACPI information about them.
728672fc84aSRobert Mustacchi 		 * This would happen when a root port is connected into a set of
729672fc84aSRobert Mustacchi 		 * hubs that are built-in.
730672fc84aSRobert Mustacchi 		 */
731672fc84aSRobert Mustacchi 		l->tul_nhubd_ports = (uint_t)*nports;
732672fc84aSRobert Mustacchi 		for (uint_t i = 1; i <= l->tul_nhubd_ports; i++) {
733672fc84aSRobert Mustacchi 			topo_usb_lport_t *clport;
734672fc84aSRobert Mustacchi 			topo_usb_port_t *cport;
735672fc84aSRobert Mustacchi 
736672fc84aSRobert Mustacchi 			if ((cport = topo_usb_port_create(mod, i, l->tul_name,
737672fc84aSRobert Mustacchi 			    '.')) == NULL) {
738672fc84aSRobert Mustacchi 				return (topo_mod_seterrno(mod, EMOD_NOMEM));
739672fc84aSRobert Mustacchi 			}
740672fc84aSRobert Mustacchi 
741672fc84aSRobert Mustacchi 			clport = topo_list_next(&cport->tup_lports);
742672fc84aSRobert Mustacchi 			topo_list_append(&l->tul_ports, cport);
743672fc84aSRobert Mustacchi 			l->tul_nports++;
744672fc84aSRobert Mustacchi 
745672fc84aSRobert Mustacchi 			clport->tul_protocol = l->tul_protocol;
746672fc84aSRobert Mustacchi 		}
747672fc84aSRobert Mustacchi 
748672fc84aSRobert Mustacchi 		/*
749672fc84aSRobert Mustacchi 		 * Now go through and discover its children.
750672fc84aSRobert Mustacchi 		 */
751672fc84aSRobert Mustacchi 		for (child = di_child_node(node); child != NULL;
752672fc84aSRobert Mustacchi 		    child = di_sibling_node(child)) {
753672fc84aSRobert Mustacchi 			int ret;
754672fc84aSRobert Mustacchi 
755672fc84aSRobert Mustacchi 			if ((ret = topo_usb_gather_devcfg_port(mod, c,
756672fc84aSRobert Mustacchi 			    &l->tul_ports, child)) != 0) {
757672fc84aSRobert Mustacchi 				return (-1);
758672fc84aSRobert Mustacchi 			}
759672fc84aSRobert Mustacchi 		}
760672fc84aSRobert Mustacchi 	}
761672fc84aSRobert Mustacchi 
762672fc84aSRobert Mustacchi 	return (0);
763672fc84aSRobert Mustacchi }
764672fc84aSRobert Mustacchi 
765672fc84aSRobert Mustacchi static int
topo_usb_gather_devcfg_cb(di_node_t node,void * arg)766672fc84aSRobert Mustacchi topo_usb_gather_devcfg_cb(di_node_t node, void *arg)
767672fc84aSRobert Mustacchi {
768672fc84aSRobert Mustacchi 	uint_t i;
769672fc84aSRobert Mustacchi 	topo_usb_controller_t *tuc;
770672fc84aSRobert Mustacchi 	di_prop_t prop = DI_PROP_NIL;
771672fc84aSRobert Mustacchi 	boolean_t rh = B_FALSE, pc = B_FALSE;
772672fc84aSRobert Mustacchi 	topo_usb_devcfg_arg_t *tda = arg;
773672fc84aSRobert Mustacchi 	topo_usb_t *usb = tda->tda_usb;
774672fc84aSRobert Mustacchi 	topo_mod_t *mod = tda->tda_mod;
775672fc84aSRobert Mustacchi 	di_node_t child;
776672fc84aSRobert Mustacchi 
777672fc84aSRobert Mustacchi 	while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
778672fc84aSRobert Mustacchi 		const char *name = di_prop_name(prop);
779672fc84aSRobert Mustacchi 		int *ports;
780672fc84aSRobert Mustacchi 
781672fc84aSRobert Mustacchi 		if (strcmp(name, "root-hub") == 0 &&
782672fc84aSRobert Mustacchi 		    di_prop_type(prop) == DI_PROP_TYPE_BOOLEAN) {
783672fc84aSRobert Mustacchi 			rh = B_TRUE;
784672fc84aSRobert Mustacchi 		} else if (strcmp(name, "usb-port-count") == 0 &&
785672fc84aSRobert Mustacchi 		    di_prop_ints(prop, &ports) == 1 && *ports > 0 &&
786672fc84aSRobert Mustacchi 		    *ports < USB_TOPO_PORT_MAX) {
787672fc84aSRobert Mustacchi 			pc = B_TRUE;
788672fc84aSRobert Mustacchi 		}
789672fc84aSRobert Mustacchi 	}
790672fc84aSRobert Mustacchi 
791672fc84aSRobert Mustacchi 	if (!rh || !pc)
792672fc84aSRobert Mustacchi 		return (DI_WALK_CONTINUE);
793672fc84aSRobert Mustacchi 
794672fc84aSRobert Mustacchi 	if ((tuc = topo_usb_controller_create(mod, usb, node)) == NULL) {
795672fc84aSRobert Mustacchi 		tda->tda_fatal = B_TRUE;
796672fc84aSRobert Mustacchi 		return (DI_WALK_TERMINATE);
797672fc84aSRobert Mustacchi 	}
798672fc84aSRobert Mustacchi 
799672fc84aSRobert Mustacchi 	/*
800672fc84aSRobert Mustacchi 	 * Check to make sure that every logical port exists at this level and
801672fc84aSRobert Mustacchi 	 * that we have its speed information filled in. If it does not exist,
802672fc84aSRobert Mustacchi 	 * create it.
803672fc84aSRobert Mustacchi 	 */
804672fc84aSRobert Mustacchi 	for (i = 1; i <= tuc->tuc_nhubd_ports; i++) {
805672fc84aSRobert Mustacchi 		topo_usb_lport_t *l;
806672fc84aSRobert Mustacchi 		topo_usb_port_t *p;
807672fc84aSRobert Mustacchi 
808672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "attempting to discover lport %u on "
809672fc84aSRobert Mustacchi 		    "controller %s", i, tuc->tuc_path);
810672fc84aSRobert Mustacchi 
811672fc84aSRobert Mustacchi 		if ((p = topo_usb_port_create(mod, i, tuc->tuc_name, '@')) ==
812672fc84aSRobert Mustacchi 		    NULL) {
813672fc84aSRobert Mustacchi 			topo_mod_dprintf(mod, "failed to create "
814672fc84aSRobert Mustacchi 			    "port %u", i);
815672fc84aSRobert Mustacchi 			tda->tda_fatal = B_TRUE;
816672fc84aSRobert Mustacchi 			return (DI_WALK_TERMINATE);
817672fc84aSRobert Mustacchi 		}
818672fc84aSRobert Mustacchi 
819672fc84aSRobert Mustacchi 		topo_list_append(&tuc->tuc_ports, p);
820672fc84aSRobert Mustacchi 		tuc->tuc_nports++;
821672fc84aSRobert Mustacchi 		l = topo_list_next(&p->tup_lports);
822672fc84aSRobert Mustacchi 
823672fc84aSRobert Mustacchi 		topo_usb_set_rhub_port_protocol(mod, tuc, l);
824672fc84aSRobert Mustacchi 	}
825672fc84aSRobert Mustacchi 
826672fc84aSRobert Mustacchi 	for (child = di_child_node(tuc->tuc_devinfo); child != NULL;
827672fc84aSRobert Mustacchi 	    child = di_sibling_node(child)) {
828672fc84aSRobert Mustacchi 		int ret;
829672fc84aSRobert Mustacchi 
830672fc84aSRobert Mustacchi 		if ((ret = topo_usb_gather_devcfg_port(mod, tuc,
831672fc84aSRobert Mustacchi 		    &tuc->tuc_ports, child)) != 0) {
832672fc84aSRobert Mustacchi 			tda->tda_fatal = B_TRUE;
833672fc84aSRobert Mustacchi 			return (DI_WALK_TERMINATE);
834672fc84aSRobert Mustacchi 		}
835672fc84aSRobert Mustacchi 	}
836672fc84aSRobert Mustacchi 
837672fc84aSRobert Mustacchi 	return (DI_WALK_PRUNECHILD);
838672fc84aSRobert Mustacchi }
839672fc84aSRobert Mustacchi 
840672fc84aSRobert Mustacchi /*
841672fc84aSRobert Mustacchi  * To find all the controllers in the system, look for device nodes that have
842672fc84aSRobert Mustacchi  * the 'root-hub' property and also a valid usb-port-count property.
843672fc84aSRobert Mustacchi  */
844672fc84aSRobert Mustacchi static boolean_t
topo_usb_gather_devcfg(topo_mod_t * mod,topo_usb_t * usb)845672fc84aSRobert Mustacchi topo_usb_gather_devcfg(topo_mod_t *mod, topo_usb_t *usb)
846672fc84aSRobert Mustacchi {
847672fc84aSRobert Mustacchi 	topo_usb_devcfg_arg_t tda;
848672fc84aSRobert Mustacchi 
849672fc84aSRobert Mustacchi 	tda.tda_usb = usb;
850672fc84aSRobert Mustacchi 	tda.tda_mod = mod;
851672fc84aSRobert Mustacchi 	tda.tda_fatal = B_FALSE;
852672fc84aSRobert Mustacchi 
853672fc84aSRobert Mustacchi 	(void) di_walk_node(usb->tu_devinfo, DI_WALK_CLDFIRST,
854672fc84aSRobert Mustacchi 	    &tda, topo_usb_gather_devcfg_cb);
855672fc84aSRobert Mustacchi 
856672fc84aSRobert Mustacchi 	return (!tda.tda_fatal);
857672fc84aSRobert Mustacchi }
858672fc84aSRobert Mustacchi 
859672fc84aSRobert Mustacchi /*
860672fc84aSRobert Mustacchi  * For more information on the matching logic here, see xHCI r1.1 / Appendix D -
861672fc84aSRobert Mustacchi  * Port to Connector Mapping.
862672fc84aSRobert Mustacchi  */
863672fc84aSRobert Mustacchi static boolean_t
topo_usb_acpi_pld_match(const acpi_pld_info_t * l,const acpi_pld_info_t * r)864672fc84aSRobert Mustacchi topo_usb_acpi_pld_match(const acpi_pld_info_t *l, const acpi_pld_info_t *r)
865672fc84aSRobert Mustacchi {
866672fc84aSRobert Mustacchi 	if (l->Panel == r->Panel &&
867672fc84aSRobert Mustacchi 	    l->VerticalPosition == r->VerticalPosition &&
868672fc84aSRobert Mustacchi 	    l->HorizontalPosition == r->HorizontalPosition &&
869672fc84aSRobert Mustacchi 	    l->Shape == r->Shape &&
870672fc84aSRobert Mustacchi 	    l->GroupOrientation == r->GroupOrientation &&
871672fc84aSRobert Mustacchi 	    l->GroupPosition == r->GroupPosition &&
872672fc84aSRobert Mustacchi 	    l->GroupToken == r->GroupToken) {
873672fc84aSRobert Mustacchi 		return (B_TRUE);
874672fc84aSRobert Mustacchi 	}
875672fc84aSRobert Mustacchi 
876672fc84aSRobert Mustacchi 	return (B_FALSE);
877672fc84aSRobert Mustacchi }
878672fc84aSRobert Mustacchi 
879672fc84aSRobert Mustacchi typedef boolean_t (*topo_usb_port_match_f)(topo_usb_port_t *, void *);
880672fc84aSRobert Mustacchi 
881672fc84aSRobert Mustacchi static topo_usb_port_t *
topo_usb_port_match_lport(topo_usb_lport_t * lport,boolean_t remove,topo_usb_port_match_f func,void * arg)882672fc84aSRobert Mustacchi topo_usb_port_match_lport(topo_usb_lport_t *lport, boolean_t remove,
883672fc84aSRobert Mustacchi     topo_usb_port_match_f func, void *arg)
884672fc84aSRobert Mustacchi {
885672fc84aSRobert Mustacchi 	topo_usb_port_t *p;
886672fc84aSRobert Mustacchi 
887672fc84aSRobert Mustacchi 	for (p = topo_list_next(&lport->tul_ports); p != NULL;
888672fc84aSRobert Mustacchi 	    p = topo_list_next(p)) {
889672fc84aSRobert Mustacchi 		topo_usb_lport_t *l;
890672fc84aSRobert Mustacchi 		topo_usb_port_t *ret;
891672fc84aSRobert Mustacchi 
892672fc84aSRobert Mustacchi 		if (func(p, arg)) {
893672fc84aSRobert Mustacchi 			if (remove) {
894672fc84aSRobert Mustacchi 				topo_list_delete(&lport->tul_ports, p);
895672fc84aSRobert Mustacchi 				lport->tul_nports--;
896672fc84aSRobert Mustacchi 			}
897672fc84aSRobert Mustacchi 
898672fc84aSRobert Mustacchi 			return (p);
899672fc84aSRobert Mustacchi 		}
900672fc84aSRobert Mustacchi 
901672fc84aSRobert Mustacchi 		for (l = topo_list_next(&p->tup_lports); l != NULL;
902672fc84aSRobert Mustacchi 		    l = topo_list_next(l)) {
903672fc84aSRobert Mustacchi 			if ((ret = topo_usb_port_match_lport(l,
904672fc84aSRobert Mustacchi 			    remove, func, arg)) != NULL) {
905672fc84aSRobert Mustacchi 				return (ret);
906672fc84aSRobert Mustacchi 			}
907672fc84aSRobert Mustacchi 		}
908672fc84aSRobert Mustacchi 	}
909672fc84aSRobert Mustacchi 
910672fc84aSRobert Mustacchi 	return (NULL);
911672fc84aSRobert Mustacchi }
912672fc84aSRobert Mustacchi 
913672fc84aSRobert Mustacchi static topo_usb_port_t *
topo_usb_port_match_controller(topo_usb_controller_t * c,boolean_t remove,topo_usb_port_match_f func,void * arg)914672fc84aSRobert Mustacchi topo_usb_port_match_controller(topo_usb_controller_t *c, boolean_t remove,
915672fc84aSRobert Mustacchi     topo_usb_port_match_f func, void *arg)
916672fc84aSRobert Mustacchi {
917672fc84aSRobert Mustacchi 	topo_usb_port_t *p;
918672fc84aSRobert Mustacchi 
919672fc84aSRobert Mustacchi 	for (p = topo_list_next(&c->tuc_ports); p != NULL;
920672fc84aSRobert Mustacchi 	    p = topo_list_next(p)) {
921672fc84aSRobert Mustacchi 		topo_usb_lport_t *l;
922672fc84aSRobert Mustacchi 		topo_usb_port_t *ret;
923672fc84aSRobert Mustacchi 
924672fc84aSRobert Mustacchi 		if (func(p, arg)) {
925672fc84aSRobert Mustacchi 			if (remove) {
926672fc84aSRobert Mustacchi 				topo_list_delete(&c->tuc_ports, p);
927672fc84aSRobert Mustacchi 				c->tuc_nports--;
928672fc84aSRobert Mustacchi 			}
929672fc84aSRobert Mustacchi 
930672fc84aSRobert Mustacchi 			return (p);
931672fc84aSRobert Mustacchi 		}
932672fc84aSRobert Mustacchi 
933672fc84aSRobert Mustacchi 		for (l = topo_list_next(&p->tup_lports); l != NULL;
934672fc84aSRobert Mustacchi 		    l = topo_list_next(l)) {
935672fc84aSRobert Mustacchi 			if ((ret = topo_usb_port_match_lport(l,
936672fc84aSRobert Mustacchi 			    remove, func, arg)) != NULL) {
937672fc84aSRobert Mustacchi 				return (ret);
938672fc84aSRobert Mustacchi 			}
939672fc84aSRobert Mustacchi 		}
940672fc84aSRobert Mustacchi 	}
941672fc84aSRobert Mustacchi 
942672fc84aSRobert Mustacchi 	return (NULL);
943672fc84aSRobert Mustacchi }
944672fc84aSRobert Mustacchi 
945672fc84aSRobert Mustacchi static topo_usb_port_t *
topo_usb_port_match(topo_usb_t * usb,boolean_t remove,topo_usb_port_match_f func,void * arg)946672fc84aSRobert Mustacchi topo_usb_port_match(topo_usb_t *usb, boolean_t remove,
947672fc84aSRobert Mustacchi     topo_usb_port_match_f func, void *arg)
948672fc84aSRobert Mustacchi {
949672fc84aSRobert Mustacchi 	topo_usb_controller_t *c;
950672fc84aSRobert Mustacchi 
951672fc84aSRobert Mustacchi 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
952672fc84aSRobert Mustacchi 	    c = topo_list_next(c)) {
953672fc84aSRobert Mustacchi 		topo_usb_port_t *p;
954672fc84aSRobert Mustacchi 
955672fc84aSRobert Mustacchi 		if ((p = topo_usb_port_match_controller(c, remove, func,
956672fc84aSRobert Mustacchi 		    arg)) != NULL)
957672fc84aSRobert Mustacchi 			return (p);
958672fc84aSRobert Mustacchi 	}
959672fc84aSRobert Mustacchi 	return (NULL);
960672fc84aSRobert Mustacchi }
961672fc84aSRobert Mustacchi 
962672fc84aSRobert Mustacchi /*
963672fc84aSRobert Mustacchi  * Merge all of the local ports and information in source, to sink.
964672fc84aSRobert Mustacchi  */
965672fc84aSRobert Mustacchi static void
topo_usb_port_merge(topo_usb_port_t * sink,topo_usb_port_t * source)966672fc84aSRobert Mustacchi topo_usb_port_merge(topo_usb_port_t *sink, topo_usb_port_t *source)
967672fc84aSRobert Mustacchi {
968672fc84aSRobert Mustacchi 	topo_usb_lport_t *l;
969672fc84aSRobert Mustacchi 
970672fc84aSRobert Mustacchi 	while ((l = topo_list_next(&source->tup_lports)) != NULL) {
971672fc84aSRobert Mustacchi 		topo_list_delete(&source->tup_lports, l);
972672fc84aSRobert Mustacchi 		source->tup_nlports--;
973672fc84aSRobert Mustacchi 		topo_list_append(&sink->tup_lports, l);
974672fc84aSRobert Mustacchi 		sink->tup_nlports++;
975672fc84aSRobert Mustacchi 	}
976672fc84aSRobert Mustacchi 
977672fc84aSRobert Mustacchi 	if (sink->tup_port_type == USB_TOPO_PORT_TYPE_DEFAULT) {
978672fc84aSRobert Mustacchi 		sink->tup_port_type = source->tup_port_type;
979672fc84aSRobert Mustacchi 	}
980672fc84aSRobert Mustacchi 
981672fc84aSRobert Mustacchi 	if (sink->tup_port_connected == TOPO_USB_C_UNKNOWN) {
982672fc84aSRobert Mustacchi 		sink->tup_port_connected = source->tup_port_connected;
983672fc84aSRobert Mustacchi 	}
984672fc84aSRobert Mustacchi }
985672fc84aSRobert Mustacchi 
986672fc84aSRobert Mustacchi static boolean_t
topo_usb_acpi_port_match(topo_usb_port_t * port,void * arg)987672fc84aSRobert Mustacchi topo_usb_acpi_port_match(topo_usb_port_t *port, void *arg)
988672fc84aSRobert Mustacchi {
989672fc84aSRobert Mustacchi 	topo_usb_port_t *target = arg;
990672fc84aSRobert Mustacchi 
991672fc84aSRobert Mustacchi 	return (port != target && port->tup_pld_valid &&
992672fc84aSRobert Mustacchi 	    topo_usb_acpi_pld_match(&port->tup_pld, &target->tup_pld));
993672fc84aSRobert Mustacchi }
994672fc84aSRobert Mustacchi 
995672fc84aSRobert Mustacchi /*
996672fc84aSRobert Mustacchi  * Ports on an xhci controller can match up. If we've been told that we should
997672fc84aSRobert Mustacchi  * do so, attempt to perform that match. We only try to find matches in the top
998672fc84aSRobert Mustacchi  * level ports of an xhci controller as that's what's most common on systems,
999672fc84aSRobert Mustacchi  * though we'll search all the descendants.
1000672fc84aSRobert Mustacchi  */
1001672fc84aSRobert Mustacchi static void
topo_usb_acpi_match(topo_mod_t * mod,topo_usb_controller_t * tuc)1002672fc84aSRobert Mustacchi topo_usb_acpi_match(topo_mod_t *mod, topo_usb_controller_t *tuc)
1003672fc84aSRobert Mustacchi {
1004672fc84aSRobert Mustacchi 	topo_usb_port_t *p;
1005672fc84aSRobert Mustacchi 
1006672fc84aSRobert Mustacchi 	for (p = topo_list_next(&tuc->tuc_ports); p != NULL;
1007672fc84aSRobert Mustacchi 	    p = topo_list_next(p)) {
1008672fc84aSRobert Mustacchi 		topo_usb_port_t *match;
1009672fc84aSRobert Mustacchi 
1010672fc84aSRobert Mustacchi 		if ((match = topo_usb_port_match_controller(tuc, B_TRUE,
1011672fc84aSRobert Mustacchi 		    topo_usb_acpi_port_match, p)) != NULL) {
1012672fc84aSRobert Mustacchi 			VERIFY3P(p, !=, match);
1013672fc84aSRobert Mustacchi 			topo_usb_port_merge(p, match);
1014672fc84aSRobert Mustacchi 			topo_mod_free(mod, match, sizeof (topo_usb_port_t));
1015672fc84aSRobert Mustacchi 		}
1016672fc84aSRobert Mustacchi 	}
1017672fc84aSRobert Mustacchi }
1018672fc84aSRobert Mustacchi 
1019672fc84aSRobert Mustacchi static boolean_t
topo_usb_metadata_match(topo_usb_port_t * port,void * arg)1020672fc84aSRobert Mustacchi topo_usb_metadata_match(topo_usb_port_t *port, void *arg)
1021672fc84aSRobert Mustacchi {
1022672fc84aSRobert Mustacchi 	topo_usb_meta_port_path_t *path = arg;
1023672fc84aSRobert Mustacchi 	topo_usb_lport_t *l;
1024672fc84aSRobert Mustacchi 
1025672fc84aSRobert Mustacchi 	if (path->tmpp_type != TOPO_USB_T_ACPI)
1026672fc84aSRobert Mustacchi 		return (B_FALSE);
1027672fc84aSRobert Mustacchi 
1028672fc84aSRobert Mustacchi 	for (l = topo_list_next(&port->tup_lports); l != NULL;
1029672fc84aSRobert Mustacchi 	    l = topo_list_next(l)) {
1030672fc84aSRobert Mustacchi 		if (l->tul_acpi_name != NULL && strcmp(path->tmpp_path,
1031672fc84aSRobert Mustacchi 		    l->tul_acpi_name) == 0) {
1032672fc84aSRobert Mustacchi 			return (B_TRUE);
1033672fc84aSRobert Mustacchi 		}
1034672fc84aSRobert Mustacchi 	}
1035672fc84aSRobert Mustacchi 
1036672fc84aSRobert Mustacchi 	return (B_FALSE);
1037672fc84aSRobert Mustacchi }
1038672fc84aSRobert Mustacchi 
1039672fc84aSRobert Mustacchi /*
1040672fc84aSRobert Mustacchi  * We've found metadata describing the USB ports. We need to now go through and
1041672fc84aSRobert Mustacchi  * try to match that data up to actual nodes.
1042672fc84aSRobert Mustacchi  */
1043672fc84aSRobert Mustacchi static void
topo_usb_apply_metadata(topo_mod_t * mod,topo_usb_t * usb)1044672fc84aSRobert Mustacchi topo_usb_apply_metadata(topo_mod_t *mod, topo_usb_t *usb)
1045672fc84aSRobert Mustacchi {
1046672fc84aSRobert Mustacchi 	topo_usb_meta_port_t *m;
1047672fc84aSRobert Mustacchi 
1048672fc84aSRobert Mustacchi 	for (m = topo_list_next(&usb->tu_metadata); m != NULL;
1049672fc84aSRobert Mustacchi 	    m = topo_list_next(m)) {
1050672fc84aSRobert Mustacchi 		topo_usb_port_t *p, *sink = NULL;
1051672fc84aSRobert Mustacchi 		topo_usb_meta_port_path_t *path;
1052672fc84aSRobert Mustacchi 		boolean_t remove = B_FALSE;
1053672fc84aSRobert Mustacchi 
1054672fc84aSRobert Mustacchi 		/*
1055672fc84aSRobert Mustacchi 		 * If this is a chassis node, we'll remove the port and move it
1056672fc84aSRobert Mustacchi 		 * to the chassis.
1057672fc84aSRobert Mustacchi 		 */
1058672fc84aSRobert Mustacchi 		if (m->tmp_flags & TOPO_USB_F_CHASSIS) {
1059672fc84aSRobert Mustacchi 			remove = B_TRUE;
1060672fc84aSRobert Mustacchi 		}
1061672fc84aSRobert Mustacchi 
1062672fc84aSRobert Mustacchi 		for (path = topo_list_next(&m->tmp_paths); path != NULL;
1063672fc84aSRobert Mustacchi 		    path = topo_list_next(path)) {
1064672fc84aSRobert Mustacchi 			topo_mod_dprintf(mod, "considering metadata path %s",
1065672fc84aSRobert Mustacchi 			    path->tmpp_path);
1066672fc84aSRobert Mustacchi 			if ((p = topo_usb_port_match(usb, remove,
1067672fc84aSRobert Mustacchi 			    topo_usb_metadata_match, path)) == NULL)
1068672fc84aSRobert Mustacchi 				continue;
1069672fc84aSRobert Mustacchi 			topo_mod_dprintf(mod, "matched path to a logical port");
1070672fc84aSRobert Mustacchi 			p->tup_meta = m;
1071672fc84aSRobert Mustacchi 
1072672fc84aSRobert Mustacchi 			/*
1073672fc84aSRobert Mustacchi 			 * Check if we can move this to the Chassis. We should
1074672fc84aSRobert Mustacchi 			 * always do this on the first port in a group. However,
1075672fc84aSRobert Mustacchi 			 * if it's a match candidate, then it will have already
1076672fc84aSRobert Mustacchi 			 * been appended.
1077672fc84aSRobert Mustacchi 			 */
1078672fc84aSRobert Mustacchi 			if ((m->tmp_flags & TOPO_USB_F_CHASSIS) != 0 &&
1079672fc84aSRobert Mustacchi 			    sink == NULL) {
1080672fc84aSRobert Mustacchi 				topo_list_append(&usb->tu_chassis_ports, p);
1081672fc84aSRobert Mustacchi 				usb->tu_nchassis_ports++;
1082672fc84aSRobert Mustacchi 			}
1083672fc84aSRobert Mustacchi 
1084672fc84aSRobert Mustacchi 			if ((usb->tu_meta_flags & TOPO_USB_M_METADATA_MATCH) !=
1085672fc84aSRobert Mustacchi 			    0) {
1086672fc84aSRobert Mustacchi 				if (sink == NULL) {
1087672fc84aSRobert Mustacchi 					sink = p;
1088672fc84aSRobert Mustacchi 					remove = B_TRUE;
1089672fc84aSRobert Mustacchi 				} else {
1090672fc84aSRobert Mustacchi 					VERIFY3P(p, !=, sink);
1091672fc84aSRobert Mustacchi 					topo_usb_port_merge(sink, p);
1092672fc84aSRobert Mustacchi 					topo_mod_free(mod, p,
1093672fc84aSRobert Mustacchi 					    sizeof (topo_usb_port_t));
1094672fc84aSRobert Mustacchi 				}
1095672fc84aSRobert Mustacchi 				continue;
1096672fc84aSRobert Mustacchi 			}
1097672fc84aSRobert Mustacchi 
1098672fc84aSRobert Mustacchi 			break;
1099672fc84aSRobert Mustacchi 		}
1100672fc84aSRobert Mustacchi 
1101672fc84aSRobert Mustacchi 	}
1102672fc84aSRobert Mustacchi }
1103672fc84aSRobert Mustacchi 
1104672fc84aSRobert Mustacchi static int
topo_usb_gather(topo_mod_t * mod,topo_usb_t * usb,tnode_t * pnode)1105672fc84aSRobert Mustacchi topo_usb_gather(topo_mod_t *mod, topo_usb_t *usb, tnode_t *pnode)
1106672fc84aSRobert Mustacchi {
1107672fc84aSRobert Mustacchi 	int ret;
1108672fc84aSRobert Mustacchi 
1109672fc84aSRobert Mustacchi 	if ((ret = topo_usb_load_metadata(mod, pnode, &usb->tu_metadata,
1110672fc84aSRobert Mustacchi 	    &usb->tu_meta_flags)) != 0) {
1111672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to read usb metadata");
1112672fc84aSRobert Mustacchi 		return (-1);
1113672fc84aSRobert Mustacchi 	}
1114672fc84aSRobert Mustacchi 	topo_mod_dprintf(mod, "loaded metadata flags: %d", usb->tu_meta_flags);
1115672fc84aSRobert Mustacchi 
1116672fc84aSRobert Mustacchi 	if (!topo_usb_gather_devcfg(mod, usb)) {
1117672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "encountered fatal error while "
1118672fc84aSRobert Mustacchi 		    "gathering physical data");
1119672fc84aSRobert Mustacchi 		return (-1);
1120672fc84aSRobert Mustacchi 	}
1121672fc84aSRobert Mustacchi 
1122672fc84aSRobert Mustacchi 	if ((usb->tu_meta_flags & TOPO_USB_M_NO_ACPI) == 0 &&
1123672fc84aSRobert Mustacchi 	    !topo_usb_gather_acpi(mod, usb)) {
1124672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "encountered fatal error while "
1125672fc84aSRobert Mustacchi 		    "gathering ACPI data");
1126672fc84aSRobert Mustacchi 		return (-1);
1127672fc84aSRobert Mustacchi 	}
1128672fc84aSRobert Mustacchi 
1129672fc84aSRobert Mustacchi 	if ((usb->tu_meta_flags & TOPO_USB_M_ACPI_MATCH) != 0) {
1130672fc84aSRobert Mustacchi 		topo_usb_controller_t *c;
1131672fc84aSRobert Mustacchi 
1132672fc84aSRobert Mustacchi 		for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1133672fc84aSRobert Mustacchi 		    c = topo_list_next(c)) {
1134672fc84aSRobert Mustacchi 			if (c->tuc_driver == TOPO_USB_D_XHCI) {
1135672fc84aSRobert Mustacchi 				topo_usb_acpi_match(mod, c);
1136672fc84aSRobert Mustacchi 			}
1137672fc84aSRobert Mustacchi 		}
1138672fc84aSRobert Mustacchi 	}
1139672fc84aSRobert Mustacchi 
1140672fc84aSRobert Mustacchi 	topo_usb_apply_metadata(mod, usb);
1141672fc84aSRobert Mustacchi 
1142672fc84aSRobert Mustacchi 	return (0);
1143672fc84aSRobert Mustacchi }
1144672fc84aSRobert Mustacchi 
1145672fc84aSRobert Mustacchi static int
topo_usb_port_properties(topo_mod_t * mod,tnode_t * tn,topo_usb_port_t * port)1146672fc84aSRobert Mustacchi topo_usb_port_properties(topo_mod_t *mod, tnode_t *tn, topo_usb_port_t *port)
1147672fc84aSRobert Mustacchi {
1148672fc84aSRobert Mustacchi 	int err;
1149672fc84aSRobert Mustacchi 	char **strs = NULL;
1150672fc84aSRobert Mustacchi 	uint_t i;
1151672fc84aSRobert Mustacchi 	topo_usb_lport_t *l;
1152672fc84aSRobert Mustacchi 	char *label;
1153672fc84aSRobert Mustacchi 	const char *ptype;
1154672fc84aSRobert Mustacchi 	size_t strlen;
1155672fc84aSRobert Mustacchi 
1156672fc84aSRobert Mustacchi 	strlen = sizeof (char *) * MAX(port->tup_nlports,
1157672fc84aSRobert Mustacchi 	    TOPO_PROP_USB_PORT_NATTRS);
1158672fc84aSRobert Mustacchi 	if ((strs = topo_mod_zalloc(mod, strlen)) == NULL) {
1159672fc84aSRobert Mustacchi 		return (-1);
1160672fc84aSRobert Mustacchi 	}
1161672fc84aSRobert Mustacchi 
1162672fc84aSRobert Mustacchi 	label = NULL;
1163672fc84aSRobert Mustacchi 	if (port->tup_meta != NULL) {
1164672fc84aSRobert Mustacchi 		label = port->tup_meta->tmp_label;
1165672fc84aSRobert Mustacchi 	}
1166672fc84aSRobert Mustacchi 
1167672fc84aSRobert Mustacchi 	if (port->tup_meta != NULL && port->tup_meta->tmp_port_type !=
1168672fc84aSRobert Mustacchi 	    USB_TOPO_PORT_TYPE_DEFAULT) {
1169672fc84aSRobert Mustacchi 		ptype =
1170672fc84aSRobert Mustacchi 		    topo_usb_port_type_to_string(port->tup_meta->tmp_port_type);
1171672fc84aSRobert Mustacchi 	} else {
1172672fc84aSRobert Mustacchi 		ptype = topo_usb_port_type_to_string(port->tup_port_type);
1173672fc84aSRobert Mustacchi 	}
1174672fc84aSRobert Mustacchi 
1175672fc84aSRobert Mustacchi 	if (topo_pgroup_create(tn, &topo_usb_port_pgroup, &err) != 0) {
1176672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property group %s: "
1177672fc84aSRobert Mustacchi 		    "%s\n", TOPO_PGROUP_USB_PORT, topo_strerror(err));
1178672fc84aSRobert Mustacchi 		goto error;
1179672fc84aSRobert Mustacchi 
1180672fc84aSRobert Mustacchi 	}
1181672fc84aSRobert Mustacchi 
1182672fc84aSRobert Mustacchi 	if (label != NULL && topo_node_label_set(tn, label, &err) != 0) {
1183672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to set label on port: %s",
1184672fc84aSRobert Mustacchi 		    topo_strerror(err));
1185672fc84aSRobert Mustacchi 		goto error;
1186672fc84aSRobert Mustacchi 	}
1187672fc84aSRobert Mustacchi 
1188672fc84aSRobert Mustacchi 	if (ptype != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PORT,
1189672fc84aSRobert Mustacchi 	    TOPO_PROP_USB_PORT_TYPE, TOPO_PROP_IMMUTABLE, ptype, &err) != 0) {
1190672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to set %s property: %s",
1191672fc84aSRobert Mustacchi 		    TOPO_PROP_USB_PORT_TYPE, topo_strerror(err));
1192672fc84aSRobert Mustacchi 		goto error;
1193672fc84aSRobert Mustacchi 	}
1194672fc84aSRobert Mustacchi 
1195672fc84aSRobert Mustacchi 	for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL;
1196672fc84aSRobert Mustacchi 	    l = topo_list_next(l)) {
1197672fc84aSRobert Mustacchi 		char *vers;
1198672fc84aSRobert Mustacchi 		int j;
1199672fc84aSRobert Mustacchi 
1200672fc84aSRobert Mustacchi 		switch (l->tul_protocol) {
1201672fc84aSRobert Mustacchi 		case TOPO_USB_P_1x:
1202672fc84aSRobert Mustacchi 			vers = "1.x";
1203672fc84aSRobert Mustacchi 			break;
1204672fc84aSRobert Mustacchi 		case TOPO_USB_P_20:
1205672fc84aSRobert Mustacchi 			vers = "2.0";
1206672fc84aSRobert Mustacchi 			break;
1207672fc84aSRobert Mustacchi 		case TOPO_USB_P_30:
1208672fc84aSRobert Mustacchi 			vers = "3.0";
1209672fc84aSRobert Mustacchi 			break;
1210672fc84aSRobert Mustacchi 		case TOPO_USB_P_31:
1211672fc84aSRobert Mustacchi 			vers = "3.1";
1212672fc84aSRobert Mustacchi 			break;
1213672fc84aSRobert Mustacchi 		default:
1214672fc84aSRobert Mustacchi 			continue;
1215672fc84aSRobert Mustacchi 		}
1216672fc84aSRobert Mustacchi 
1217672fc84aSRobert Mustacchi 		/*
1218672fc84aSRobert Mustacchi 		 * Make sure we don't already have this string. This can happen
1219672fc84aSRobert Mustacchi 		 * when we have an ehci port and xhci support that both provide
1220672fc84aSRobert Mustacchi 		 * USB 2.0 service.
1221672fc84aSRobert Mustacchi 		 */
1222672fc84aSRobert Mustacchi 		for (j = 0; j < i; j++) {
1223672fc84aSRobert Mustacchi 			if (strcmp(strs[j], vers) == 0)
1224672fc84aSRobert Mustacchi 				break;
1225672fc84aSRobert Mustacchi 		}
1226672fc84aSRobert Mustacchi 
1227672fc84aSRobert Mustacchi 		if (j < i)
1228672fc84aSRobert Mustacchi 			continue;
1229672fc84aSRobert Mustacchi 		strs[i++] = vers;
1230672fc84aSRobert Mustacchi 	}
1231672fc84aSRobert Mustacchi 
1232672fc84aSRobert Mustacchi 	if (i > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PORT,
1233672fc84aSRobert Mustacchi 	    TOPO_PROP_USB_PORT_VERSIONS, TOPO_PROP_IMMUTABLE,
1234672fc84aSRobert Mustacchi 	    (const char **)strs, i, &err) != 0) {
1235672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to set %s property: %s",
1236672fc84aSRobert Mustacchi 		    TOPO_PROP_USB_PORT_VERSIONS, topo_strerror(err));
1237672fc84aSRobert Mustacchi 		goto error;
1238672fc84aSRobert Mustacchi 	}
1239672fc84aSRobert Mustacchi 
1240672fc84aSRobert Mustacchi 	i = 0;
1241672fc84aSRobert Mustacchi 	if (port->tup_pld_valid && port->tup_pld.UserVisible != 0 &&
1242672fc84aSRobert Mustacchi 	    port->tup_port_connected == TOPO_USB_C_CONNECTED) {
1243672fc84aSRobert Mustacchi 		strs[i++] = TOPO_PROP_USB_PORT_A_VISIBLE;
1244672fc84aSRobert Mustacchi 	} else if (port->tup_port_connected == TOPO_USB_C_CONNECTED) {
1245672fc84aSRobert Mustacchi 		strs[i++] = TOPO_PROP_USB_PORT_A_CONNECTED;
1246672fc84aSRobert Mustacchi 	} else if (port->tup_port_connected == TOPO_USB_C_DISCONNECTED) {
1247672fc84aSRobert Mustacchi 		strs[i++] = TOPO_PROP_USB_PORT_A_DISCONNECTED;
1248672fc84aSRobert Mustacchi 	}
1249672fc84aSRobert Mustacchi 
1250672fc84aSRobert Mustacchi 	if (port->tup_meta != NULL) {
1251672fc84aSRobert Mustacchi 		if (port->tup_meta->tmp_flags & TOPO_USB_F_INTERNAL) {
1252672fc84aSRobert Mustacchi 			strs[i++] = TOPO_PROP_USB_PORT_A_INTERNAL;
1253672fc84aSRobert Mustacchi 		}
1254672fc84aSRobert Mustacchi 
1255672fc84aSRobert Mustacchi 		if (port->tup_meta->tmp_flags & TOPO_USB_F_EXTERNAL) {
1256672fc84aSRobert Mustacchi 			strs[i++] = TOPO_PROP_USB_PORT_A_EXTERNAL;
1257672fc84aSRobert Mustacchi 		}
1258672fc84aSRobert Mustacchi 	}
1259672fc84aSRobert Mustacchi 
1260672fc84aSRobert Mustacchi 	if (i > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PORT,
1261672fc84aSRobert Mustacchi 	    TOPO_PROP_USB_PORT_ATTRIBUTES, TOPO_PROP_IMMUTABLE,
1262672fc84aSRobert Mustacchi 	    (const char **)strs, i, &err) != 0) {
1263672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to set %s property: %s",
1264672fc84aSRobert Mustacchi 		    TOPO_PROP_USB_PORT_VERSIONS, topo_strerror(err));
1265672fc84aSRobert Mustacchi 		goto error;
1266672fc84aSRobert Mustacchi 	}
1267672fc84aSRobert Mustacchi 
1268672fc84aSRobert Mustacchi 	for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL;
1269672fc84aSRobert Mustacchi 	    l = topo_list_next(l)) {
1270672fc84aSRobert Mustacchi 		strs[i++] = l->tul_name;
1271672fc84aSRobert Mustacchi 	}
1272672fc84aSRobert Mustacchi 
1273672fc84aSRobert Mustacchi 	if (i > 0 && topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PORT,
1274672fc84aSRobert Mustacchi 	    TOPO_PROP_USB_PORT_LPORTS, TOPO_PROP_IMMUTABLE,
1275672fc84aSRobert Mustacchi 	    (const char **)strs, i, &err) != 0) {
1276672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to set %s propert: %s",
1277672fc84aSRobert Mustacchi 		    TOPO_PROP_USB_PORT_LPORTS, topo_strerror(err));
1278672fc84aSRobert Mustacchi 		goto error;
1279672fc84aSRobert Mustacchi 	}
1280672fc84aSRobert Mustacchi 
1281672fc84aSRobert Mustacchi 	err = 0;
1282672fc84aSRobert Mustacchi error:
1283672fc84aSRobert Mustacchi 	if (strs != NULL) {
1284672fc84aSRobert Mustacchi 		topo_mod_free(mod, strs, strlen);
1285672fc84aSRobert Mustacchi 	}
1286672fc84aSRobert Mustacchi 
1287672fc84aSRobert Mustacchi 	if (err != 0) {
1288672fc84aSRobert Mustacchi 		return (topo_mod_seterrno(mod, err));
1289672fc84aSRobert Mustacchi 	}
1290672fc84aSRobert Mustacchi 
1291672fc84aSRobert Mustacchi 	return (err);
1292672fc84aSRobert Mustacchi }
1293672fc84aSRobert Mustacchi 
1294672fc84aSRobert Mustacchi /*
1295672fc84aSRobert Mustacchi  * Create a disk node under the scsa2usb node. When we have an scsa2usb node,
1296672fc84aSRobert Mustacchi  * we'll have a child devinfo which is a disk. To successfully enumerate this,
1297672fc84aSRobert Mustacchi  * we need to find the child node (which should be our only direct descendent)
1298672fc84aSRobert Mustacchi  * and get its devfs path. From there we can construct a 'binding' property
1299672fc84aSRobert Mustacchi  * group with the 'occupantpath' property that points to the module. At that
1300672fc84aSRobert Mustacchi  * point we can invoke the disk enumerator.
1301672fc84aSRobert Mustacchi  */
1302672fc84aSRobert Mustacchi static int
topo_usb_enum_scsa2usb(topo_mod_t * mod,tnode_t * tn,topo_usb_lport_t * lport)1303672fc84aSRobert Mustacchi topo_usb_enum_scsa2usb(topo_mod_t *mod, tnode_t *tn, topo_usb_lport_t *lport)
1304672fc84aSRobert Mustacchi {
1305672fc84aSRobert Mustacchi 	int ret;
1306672fc84aSRobert Mustacchi 	di_node_t child;
1307672fc84aSRobert Mustacchi 	char *devfs = NULL;
1308672fc84aSRobert Mustacchi 	topo_instance_t min = 0, max = 0;
1309672fc84aSRobert Mustacchi 
1310672fc84aSRobert Mustacchi 	if ((child = di_child_node(lport->tul_device)) == DI_NODE_NIL ||
1311672fc84aSRobert Mustacchi 	    strcmp("disk", di_node_name(child)) != 0) {
1312672fc84aSRobert Mustacchi 		return (0);
1313672fc84aSRobert Mustacchi 	}
1314672fc84aSRobert Mustacchi 
1315672fc84aSRobert Mustacchi 	if ((devfs = di_devfs_path(child)) == NULL) {
1316672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to get USB disk child device "
1317672fc84aSRobert Mustacchi 		    "devfs path");
1318672fc84aSRobert Mustacchi 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1319672fc84aSRobert Mustacchi 	}
1320672fc84aSRobert Mustacchi 
1321672fc84aSRobert Mustacchi 	if (topo_mod_load(mod, DISK, TOPO_VERSION) == NULL) {
1322672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to load disk module: %s",
1323672fc84aSRobert Mustacchi 		    topo_mod_errmsg(mod));
1324672fc84aSRobert Mustacchi 		goto error;
1325672fc84aSRobert Mustacchi 	}
1326672fc84aSRobert Mustacchi 
1327672fc84aSRobert Mustacchi 	if (topo_pgroup_create(tn, &topo_binding_pgroup, &ret) != 0) {
1328672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create \"binding\" "
1329672fc84aSRobert Mustacchi 		    "property group: %s", topo_strerror(ret));
1330672fc84aSRobert Mustacchi 		goto error;
1331672fc84aSRobert Mustacchi 	}
1332672fc84aSRobert Mustacchi 
1333672fc84aSRobert Mustacchi 	if (topo_prop_set_string(tn, TOPO_PGROUP_BINDING,
1334672fc84aSRobert Mustacchi 	    TOPO_BINDING_OCCUPANT, TOPO_PROP_IMMUTABLE, devfs, &ret) !=
1335672fc84aSRobert Mustacchi 	    0) {
1336672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1337672fc84aSRobert Mustacchi 		    TOPO_IO_MODULE, topo_strerror(ret));
1338672fc84aSRobert Mustacchi 		goto error;
1339672fc84aSRobert Mustacchi 	}
1340672fc84aSRobert Mustacchi 
1341672fc84aSRobert Mustacchi 	if (topo_node_range_create(mod, tn, DISK, min, max) != 0) {
1342672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create disk node range %s: %s",
1343672fc84aSRobert Mustacchi 		    devfs, topo_mod_errmsg(mod));
1344672fc84aSRobert Mustacchi 		goto error;
1345672fc84aSRobert Mustacchi 	}
1346672fc84aSRobert Mustacchi 
1347672fc84aSRobert Mustacchi 	if (topo_mod_enumerate(mod, tn, DISK, DISK, min, max, NULL) != 0) {
1348672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create disk node %s: %s",
1349672fc84aSRobert Mustacchi 		    devfs, topo_mod_errmsg(mod));
1350672fc84aSRobert Mustacchi 		goto error;
1351672fc84aSRobert Mustacchi 	}
1352*6597d6fcSRobert Mustacchi 	di_devfs_path_free(devfs);
1353672fc84aSRobert Mustacchi 
1354672fc84aSRobert Mustacchi 	return (0);
1355672fc84aSRobert Mustacchi 
1356672fc84aSRobert Mustacchi error:
1357672fc84aSRobert Mustacchi 	di_devfs_path_free(devfs);
1358672fc84aSRobert Mustacchi 	return (-1);
1359672fc84aSRobert Mustacchi }
1360672fc84aSRobert Mustacchi 
1361672fc84aSRobert Mustacchi static int
topo_usb_enum_port_children(topo_mod_t * mod,tnode_t * pn,topo_usb_lport_t * plport)1362672fc84aSRobert Mustacchi topo_usb_enum_port_children(topo_mod_t *mod, tnode_t *pn,
1363672fc84aSRobert Mustacchi     topo_usb_lport_t *plport)
1364672fc84aSRobert Mustacchi {
1365672fc84aSRobert Mustacchi 	int ret;
1366672fc84aSRobert Mustacchi 	topo_usb_port_t *port;
1367672fc84aSRobert Mustacchi 	topo_instance_t min = 0, i;
1368672fc84aSRobert Mustacchi 
1369672fc84aSRobert Mustacchi 	if ((ret = port_range_create(mod, pn, min, plport->tul_nports)) != 0) {
1370672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create port range [%u, %u) "
1371672fc84aSRobert Mustacchi 		    "for child hub", 0, plport->tul_nports);
1372672fc84aSRobert Mustacchi 		return (ret);
1373672fc84aSRobert Mustacchi 	}
1374672fc84aSRobert Mustacchi 
1375672fc84aSRobert Mustacchi 	for (i = 0, port = topo_list_next(&plport->tul_ports); port != NULL;
1376672fc84aSRobert Mustacchi 	    port = topo_list_next(port)) {
1377672fc84aSRobert Mustacchi 		tnode_t *tn;
1378672fc84aSRobert Mustacchi 		if ((ret = port_create_usb(mod, pn, i, &tn)) != 0)
1379672fc84aSRobert Mustacchi 			return (ret);
1380672fc84aSRobert Mustacchi 
1381672fc84aSRobert Mustacchi 		if ((ret = topo_usb_port_properties(mod, tn, port)) != 0) {
1382672fc84aSRobert Mustacchi 			return (ret);
1383672fc84aSRobert Mustacchi 		}
1384672fc84aSRobert Mustacchi 
1385672fc84aSRobert Mustacchi 		if ((ret = topo_usb_enum_device(mod, tn, port)) != 0)
1386672fc84aSRobert Mustacchi 			return (ret);
1387672fc84aSRobert Mustacchi 
1388672fc84aSRobert Mustacchi 		i++;
1389672fc84aSRobert Mustacchi 	}
1390672fc84aSRobert Mustacchi 
1391672fc84aSRobert Mustacchi 	return (0);
1392672fc84aSRobert Mustacchi }
1393672fc84aSRobert Mustacchi 
1394672fc84aSRobert Mustacchi /*
1395672fc84aSRobert Mustacchi  * Enumerate the requested device. Depending on the driver associated with it
1396672fc84aSRobert Mustacchi  * (if any), we may have to create child nodes.
1397672fc84aSRobert Mustacchi  */
1398672fc84aSRobert Mustacchi static int
topo_usb_enum_lport(topo_mod_t * mod,tnode_t * pn,topo_usb_port_t * port,topo_usb_lport_t * lport,topo_instance_t topo_inst)1399672fc84aSRobert Mustacchi topo_usb_enum_lport(topo_mod_t *mod, tnode_t *pn, topo_usb_port_t *port,
1400672fc84aSRobert Mustacchi     topo_usb_lport_t *lport, topo_instance_t topo_inst)
1401672fc84aSRobert Mustacchi {
1402672fc84aSRobert Mustacchi 	int ret, inst;
1403672fc84aSRobert Mustacchi 	int *vendid = NULL, *prodid = NULL, *revid = NULL, *release = NULL;
1404672fc84aSRobert Mustacchi 	char *vend = NULL, *prod = NULL, *serial = NULL, *speed = NULL;
14050d2006e4SRobert Mustacchi 	char *min_speed = NULL, *sup_speeds = NULL;
14060d2006e4SRobert Mustacchi 	int nsup_speeds = 0;
1407672fc84aSRobert Mustacchi 	char *driver, *devfs;
1408672fc84aSRobert Mustacchi 	char revbuf[32], relbuf[32];
1409672fc84aSRobert Mustacchi 	tnode_t *tn = NULL;
1410672fc84aSRobert Mustacchi 	di_prop_t prop = DI_PROP_NIL;
1411672fc84aSRobert Mustacchi 	nvlist_t *auth = NULL, *fmri = NULL, *modnvl = NULL;
1412672fc84aSRobert Mustacchi 
1413672fc84aSRobert Mustacchi 	/*
1414672fc84aSRobert Mustacchi 	 * Look up the information we'll need to create the usb-properties. We
1415672fc84aSRobert Mustacchi 	 * do this first because this information is often part of the FMRI.
1416672fc84aSRobert Mustacchi 	 */
1417672fc84aSRobert Mustacchi 	for (prop = di_prop_next(lport->tul_device, DI_PROP_NIL);
1418672fc84aSRobert Mustacchi 	    prop != DI_PROP_NIL; prop = di_prop_next(lport->tul_device, prop)) {
1419672fc84aSRobert Mustacchi 		const char *pname = di_prop_name(prop);
1420672fc84aSRobert Mustacchi 
1421672fc84aSRobert Mustacchi 		if (strcmp(pname, "usb-vendor-id") == 0) {
1422672fc84aSRobert Mustacchi 			if (di_prop_ints(prop, &vendid) != 1)
1423672fc84aSRobert Mustacchi 				vendid = NULL;
1424672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "usb-product-id") == 0) {
1425672fc84aSRobert Mustacchi 			if (di_prop_ints(prop, &prodid) != 1)
1426672fc84aSRobert Mustacchi 				prodid = NULL;
1427672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "usb-revision-id") == 0) {
1428672fc84aSRobert Mustacchi 			if (di_prop_ints(prop, &revid) != 1) {
1429672fc84aSRobert Mustacchi 				revid = NULL;
1430672fc84aSRobert Mustacchi 			} else {
1431672fc84aSRobert Mustacchi 				(void) snprintf(revbuf, sizeof (revbuf), "%x",
1432672fc84aSRobert Mustacchi 				    *revid);
1433672fc84aSRobert Mustacchi 			}
1434672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "usb-release") == 0) {
1435672fc84aSRobert Mustacchi 			if (di_prop_ints(prop, &release) != 1) {
1436672fc84aSRobert Mustacchi 				release = NULL;
1437672fc84aSRobert Mustacchi 			} else {
1438672fc84aSRobert Mustacchi 				(void) snprintf(relbuf, sizeof (relbuf),
1439672fc84aSRobert Mustacchi 				    "%x.%x", *release >> 8,
1440672fc84aSRobert Mustacchi 				    (*release >> 4) & 0xf);
1441672fc84aSRobert Mustacchi 			}
1442672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "usb-vendor-name") == 0) {
1443672fc84aSRobert Mustacchi 			if (di_prop_strings(prop, &vend) != 1)
1444672fc84aSRobert Mustacchi 				vend = NULL;
1445672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "usb-product-name") == 0) {
1446672fc84aSRobert Mustacchi 			if (di_prop_strings(prop, &prod) != 1)
1447672fc84aSRobert Mustacchi 				prod = NULL;
1448672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "usb-serialno") == 0) {
1449672fc84aSRobert Mustacchi 			if (di_prop_strings(prop, &serial) != 1)
1450672fc84aSRobert Mustacchi 				serial = NULL;
1451672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "full-speed") == 0) {
1452672fc84aSRobert Mustacchi 			speed = "full-speed";
1453672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "low-speed") == 0) {
1454672fc84aSRobert Mustacchi 			speed = "low-speed";
1455672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "high-speed") == 0) {
1456672fc84aSRobert Mustacchi 			speed = "high-speed";
1457672fc84aSRobert Mustacchi 		} else if (strcmp(pname, "super-speed") == 0) {
1458672fc84aSRobert Mustacchi 			speed = "super-speed";
14590d2006e4SRobert Mustacchi 		} else if (strcmp(pname, "usb-minimum-speed") == 0) {
14600d2006e4SRobert Mustacchi 			if (di_prop_strings(prop, &min_speed) != 1)
14610d2006e4SRobert Mustacchi 				min_speed = NULL;
14620d2006e4SRobert Mustacchi 		} else if (strcmp(pname, "usb-supported-speeds") == 0) {
14630d2006e4SRobert Mustacchi 			nsup_speeds = di_prop_strings(prop, &sup_speeds);
14640d2006e4SRobert Mustacchi 			if (nsup_speeds <= 0) {
14650d2006e4SRobert Mustacchi 				sup_speeds = NULL;
14660d2006e4SRobert Mustacchi 			}
1467672fc84aSRobert Mustacchi 		}
1468672fc84aSRobert Mustacchi 	}
1469672fc84aSRobert Mustacchi 
1470672fc84aSRobert Mustacchi 	driver = di_driver_name(lport->tul_device);
1471672fc84aSRobert Mustacchi 	inst = di_instance(lport->tul_device);
1472672fc84aSRobert Mustacchi 	devfs = di_devfs_path(lport->tul_device);
1473672fc84aSRobert Mustacchi 
1474672fc84aSRobert Mustacchi 	if ((auth = topo_mod_auth(mod, pn)) == NULL) {
1475672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to get authority for USB device: "
1476672fc84aSRobert Mustacchi 		    "%s", topo_mod_errmsg(mod));
1477672fc84aSRobert Mustacchi 		goto error;
1478672fc84aSRobert Mustacchi 	}
1479672fc84aSRobert Mustacchi 
1480672fc84aSRobert Mustacchi 	if ((fmri = topo_mod_hcfmri(mod, pn, FM_HC_SCHEME_VERSION, USB_DEVICE,
1481672fc84aSRobert Mustacchi 	    topo_inst, NULL, auth, prod, revbuf, serial)) == NULL) {
1482672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to generate fmri for USB "
1483672fc84aSRobert Mustacchi 		    "device %s: %s", di_devfs_path(lport->tul_device),
1484672fc84aSRobert Mustacchi 		    topo_mod_errmsg(mod));
1485672fc84aSRobert Mustacchi 		goto error;
1486672fc84aSRobert Mustacchi 	}
1487672fc84aSRobert Mustacchi 
1488672fc84aSRobert Mustacchi 	if ((tn = topo_node_bind(mod, pn, USB_DEVICE, topo_inst, fmri)) ==
1489672fc84aSRobert Mustacchi 	    NULL) {
1490672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to bind USB device node: %s",
1491672fc84aSRobert Mustacchi 		    topo_mod_errmsg(mod));
1492672fc84aSRobert Mustacchi 		goto error;
1493672fc84aSRobert Mustacchi 	}
1494672fc84aSRobert Mustacchi 
1495672fc84aSRobert Mustacchi 	/*
1496672fc84aSRobert Mustacchi 	 * In general, we expect a USB device to be its own FRU. There are some
1497672fc84aSRobert Mustacchi 	 * exceptions to this, for example, a built-in hub. However, it's hard
1498672fc84aSRobert Mustacchi 	 * for us to generally know. It may be nice to allow the platform to
1499672fc84aSRobert Mustacchi 	 * override this in the future.
1500672fc84aSRobert Mustacchi 	 */
1501672fc84aSRobert Mustacchi 	if (topo_node_fru_set(tn, fmri, 0, &ret) != 0) {
1502672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to set FRU: %s",
1503672fc84aSRobert Mustacchi 		    topo_strerror(ret));
1504672fc84aSRobert Mustacchi 		(void) topo_mod_seterrno(mod, ret);
1505672fc84aSRobert Mustacchi 		goto error;
1506672fc84aSRobert Mustacchi 	}
1507672fc84aSRobert Mustacchi 
1508672fc84aSRobert Mustacchi 	/*
1509672fc84aSRobert Mustacchi 	 * Inherit the label from the port on the device. This is intended to
1510672fc84aSRobert Mustacchi 	 * only go a single way.
1511672fc84aSRobert Mustacchi 	 */
1512672fc84aSRobert Mustacchi 	if (port->tup_meta != NULL && port->tup_meta->tmp_label != NULL &&
1513672fc84aSRobert Mustacchi 	    topo_node_label_set(tn, port->tup_meta->tmp_label, &ret) != 0) {
1514672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to set label on device: %s",
1515672fc84aSRobert Mustacchi 		    topo_strerror(ret));
1516672fc84aSRobert Mustacchi 		goto error;
1517672fc84aSRobert Mustacchi 	}
1518672fc84aSRobert Mustacchi 
1519672fc84aSRobert Mustacchi 	/*
1520672fc84aSRobert Mustacchi 	 * USB-properties
1521672fc84aSRobert Mustacchi 	 */
1522672fc84aSRobert Mustacchi 	if (topo_pgroup_create(tn, &topo_usb_props_pgroup, &ret) != 0) {
1523672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create \"usb-properties\" "
1524672fc84aSRobert Mustacchi 		    "property group: %s", topo_strerror(ret));
1525672fc84aSRobert Mustacchi 		goto error;
1526672fc84aSRobert Mustacchi 	}
1527672fc84aSRobert Mustacchi 
1528672fc84aSRobert Mustacchi 	if (topo_prop_set_uint32(tn, TOPO_PGROUP_USB_PROPS,
1529672fc84aSRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_PORT, TOPO_PROP_IMMUTABLE, lport->tul_portno,
1530672fc84aSRobert Mustacchi 	    &ret) != 0) {
1531672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1532672fc84aSRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_PORT, topo_strerror(ret));
1533672fc84aSRobert Mustacchi 		goto error;
1534672fc84aSRobert Mustacchi 	}
1535672fc84aSRobert Mustacchi 
1536672fc84aSRobert Mustacchi 	if (vendid != NULL && topo_prop_set_int32(tn, TOPO_PGROUP_USB_PROPS,
1537672fc84aSRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_VID, TOPO_PROP_IMMUTABLE, *vendid, &ret) !=
1538672fc84aSRobert Mustacchi 	    0) {
1539672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1540672fc84aSRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_VID, topo_strerror(ret));
1541672fc84aSRobert Mustacchi 		goto error;
1542672fc84aSRobert Mustacchi 	}
1543672fc84aSRobert Mustacchi 
1544672fc84aSRobert Mustacchi 	if (prodid != NULL && topo_prop_set_int32(tn, TOPO_PGROUP_USB_PROPS,
1545672fc84aSRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_PID, TOPO_PROP_IMMUTABLE, *prodid, &ret) !=
1546672fc84aSRobert Mustacchi 	    0) {
1547672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1548672fc84aSRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_PID, topo_strerror(ret));
1549672fc84aSRobert Mustacchi 		goto error;
1550672fc84aSRobert Mustacchi 	}
1551672fc84aSRobert Mustacchi 
1552672fc84aSRobert Mustacchi 	if (revid != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1553672fc84aSRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_REV, TOPO_PROP_IMMUTABLE, revbuf, &ret) !=
1554672fc84aSRobert Mustacchi 	    0) {
1555672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1556672fc84aSRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_REV, topo_strerror(ret));
1557672fc84aSRobert Mustacchi 		goto error;
1558672fc84aSRobert Mustacchi 	}
1559672fc84aSRobert Mustacchi 
1560672fc84aSRobert Mustacchi 	if (release != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1561672fc84aSRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_VERSION, TOPO_PROP_IMMUTABLE, relbuf, &ret) !=
1562672fc84aSRobert Mustacchi 	    0) {
1563672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1564672fc84aSRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_VERSION, topo_strerror(ret));
1565672fc84aSRobert Mustacchi 		goto error;
1566672fc84aSRobert Mustacchi 	}
1567672fc84aSRobert Mustacchi 
1568672fc84aSRobert Mustacchi 	if (vend != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1569672fc84aSRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_VNAME, TOPO_PROP_IMMUTABLE, vend, &ret) !=
1570672fc84aSRobert Mustacchi 	    0) {
1571672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1572672fc84aSRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_VNAME, topo_strerror(ret));
1573672fc84aSRobert Mustacchi 		goto error;
1574672fc84aSRobert Mustacchi 	}
1575672fc84aSRobert Mustacchi 
1576672fc84aSRobert Mustacchi 	if (prod != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1577672fc84aSRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_PNAME, TOPO_PROP_IMMUTABLE, prod, &ret) !=
1578672fc84aSRobert Mustacchi 	    0) {
1579672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1580672fc84aSRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_PNAME, topo_strerror(ret));
1581672fc84aSRobert Mustacchi 		goto error;
1582672fc84aSRobert Mustacchi 	}
1583672fc84aSRobert Mustacchi 
1584672fc84aSRobert Mustacchi 	if (serial != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1585672fc84aSRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_SN, TOPO_PROP_IMMUTABLE, serial, &ret) !=
1586672fc84aSRobert Mustacchi 	    0) {
1587672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1588672fc84aSRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_SN, topo_strerror(ret));
1589672fc84aSRobert Mustacchi 		goto error;
1590672fc84aSRobert Mustacchi 	}
1591672fc84aSRobert Mustacchi 
1592672fc84aSRobert Mustacchi 	if (speed != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
1593672fc84aSRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_SPEED, TOPO_PROP_IMMUTABLE, speed, &ret) !=
1594672fc84aSRobert Mustacchi 	    0) {
1595672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1596672fc84aSRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_SPEED, topo_strerror(ret));
1597672fc84aSRobert Mustacchi 		goto error;
1598672fc84aSRobert Mustacchi 	}
1599672fc84aSRobert Mustacchi 
16000d2006e4SRobert Mustacchi 	if (min_speed != NULL && topo_prop_set_string(tn, TOPO_PGROUP_USB_PROPS,
16010d2006e4SRobert Mustacchi 	    TOPO_PGROUP_USB_PROPS_MIN_SPEED, TOPO_PROP_IMMUTABLE, min_speed,
16020d2006e4SRobert Mustacchi 	    &ret) != 0) {
16030d2006e4SRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
16040d2006e4SRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_MIN_SPEED, topo_strerror(ret));
16050d2006e4SRobert Mustacchi 		goto error;
16060d2006e4SRobert Mustacchi 	}
16070d2006e4SRobert Mustacchi 
16080d2006e4SRobert Mustacchi 	if (sup_speeds != NULL) {
16090d2006e4SRobert Mustacchi 		const char **strings, *c;
16100d2006e4SRobert Mustacchi 		int i, rval;
16110d2006e4SRobert Mustacchi 
16120d2006e4SRobert Mustacchi 		if ((strings = topo_mod_zalloc(mod, sizeof (char *) *
16130d2006e4SRobert Mustacchi 		    nsup_speeds)) == NULL) {
16140d2006e4SRobert Mustacchi 			topo_mod_dprintf(mod, "failed to allocate character "
16150d2006e4SRobert Mustacchi 			    "array for property %s",
16160d2006e4SRobert Mustacchi 			    TOPO_PGROUP_USB_PROPS_SUPPORTED_SPEEDS);
16170d2006e4SRobert Mustacchi 			goto error;
16180d2006e4SRobert Mustacchi 		}
16190d2006e4SRobert Mustacchi 
16200d2006e4SRobert Mustacchi 		/*
16210d2006e4SRobert Mustacchi 		 * devinfo string properties are concatenated NUL-terminated
16220d2006e4SRobert Mustacchi 		 * strings. We need to translate that to a string array.
16230d2006e4SRobert Mustacchi 		 */
16240d2006e4SRobert Mustacchi 		for (c = sup_speeds, i = 0; i < nsup_speeds; i++) {
16250d2006e4SRobert Mustacchi 			size_t len;
16260d2006e4SRobert Mustacchi 
16270d2006e4SRobert Mustacchi 			strings[i] = c;
16280d2006e4SRobert Mustacchi 			if (i + 1 < nsup_speeds) {
16290d2006e4SRobert Mustacchi 				len = strlen(c);
16300d2006e4SRobert Mustacchi 				c += len + 1;
16310d2006e4SRobert Mustacchi 			}
16320d2006e4SRobert Mustacchi 		}
16330d2006e4SRobert Mustacchi 
16340d2006e4SRobert Mustacchi 		rval = topo_prop_set_string_array(tn, TOPO_PGROUP_USB_PROPS,
16350d2006e4SRobert Mustacchi 		    TOPO_PGROUP_USB_PROPS_SUPPORTED_SPEEDS, TOPO_PROP_IMMUTABLE,
16360d2006e4SRobert Mustacchi 		    strings, nsup_speeds, &ret);
16370d2006e4SRobert Mustacchi 		topo_mod_free(mod, strings, sizeof (char *) * nsup_speeds);
16380d2006e4SRobert Mustacchi 		if (rval != 0) {
16390d2006e4SRobert Mustacchi 			topo_mod_dprintf(mod, "failed to create property %s: "
16400d2006e4SRobert Mustacchi 			    "%s", TOPO_PGROUP_USB_PROPS_SUPPORTED_SPEEDS,
16410d2006e4SRobert Mustacchi 			    topo_strerror(ret));
16420d2006e4SRobert Mustacchi 		}
16430d2006e4SRobert Mustacchi 	}
16440d2006e4SRobert Mustacchi 
1645672fc84aSRobert Mustacchi 	/*
1646672fc84aSRobert Mustacchi 	 * I/O pgroup
1647672fc84aSRobert Mustacchi 	 */
1648672fc84aSRobert Mustacchi 	if (topo_pgroup_create(tn, &topo_io_pgroup, &ret) != 0) {
1649672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create \"io\" "
1650672fc84aSRobert Mustacchi 		    "property group: %s", topo_strerror(ret));
1651672fc84aSRobert Mustacchi 		goto error;
1652672fc84aSRobert Mustacchi 	}
1653672fc84aSRobert Mustacchi 
1654672fc84aSRobert Mustacchi 	if (driver != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO,
1655672fc84aSRobert Mustacchi 	    TOPO_IO_DRIVER, TOPO_PROP_IMMUTABLE, driver, &ret) !=
1656672fc84aSRobert Mustacchi 	    0) {
1657672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1658672fc84aSRobert Mustacchi 		    TOPO_IO_DRIVER, topo_strerror(ret));
1659672fc84aSRobert Mustacchi 		goto error;
1660672fc84aSRobert Mustacchi 	}
1661672fc84aSRobert Mustacchi 
1662672fc84aSRobert Mustacchi 	if (inst != -1 && topo_prop_set_uint32(tn, TOPO_PGROUP_IO,
1663672fc84aSRobert Mustacchi 	    TOPO_IO_INSTANCE, TOPO_PROP_IMMUTABLE, inst, &ret) !=
1664672fc84aSRobert Mustacchi 	    0) {
1665672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1666672fc84aSRobert Mustacchi 		    TOPO_IO_INSTANCE, topo_strerror(ret));
1667672fc84aSRobert Mustacchi 		goto error;
1668672fc84aSRobert Mustacchi 	}
1669672fc84aSRobert Mustacchi 
1670672fc84aSRobert Mustacchi 	if (devfs != NULL && topo_prop_set_string(tn, TOPO_PGROUP_IO,
1671672fc84aSRobert Mustacchi 	    TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE, devfs, &ret) !=
1672672fc84aSRobert Mustacchi 	    0) {
1673672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1674672fc84aSRobert Mustacchi 		    TOPO_IO_DEV_PATH, topo_strerror(ret));
1675672fc84aSRobert Mustacchi 		goto error;
1676672fc84aSRobert Mustacchi 	}
1677672fc84aSRobert Mustacchi 
1678672fc84aSRobert Mustacchi 	if (driver != NULL && (modnvl = topo_mod_modfmri(mod,
1679672fc84aSRobert Mustacchi 	    FM_MOD_SCHEME_VERSION, driver)) != NULL &&
1680672fc84aSRobert Mustacchi 	    topo_prop_set_fmri(tn, TOPO_PGROUP_IO, TOPO_IO_MODULE,
1681672fc84aSRobert Mustacchi 	    TOPO_PROP_IMMUTABLE, modnvl, &ret) != 0) {
1682672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create property %s: %s",
1683672fc84aSRobert Mustacchi 		    TOPO_IO_MODULE, topo_strerror(ret));
1684672fc84aSRobert Mustacchi 		goto error;
1685672fc84aSRobert Mustacchi 	}
1686672fc84aSRobert Mustacchi 
1687672fc84aSRobert Mustacchi 	/*
1688672fc84aSRobert Mustacchi 	 * Check the drivers to determine special behavior that we should do.
1689672fc84aSRobert Mustacchi 	 * The following are cases that we want to handle:
1690672fc84aSRobert Mustacchi 	 *
1691672fc84aSRobert Mustacchi 	 *   o Creating disk nodes for scsa2usb devices
1692672fc84aSRobert Mustacchi 	 *   o Creating children ports and searching them for hubd
1693672fc84aSRobert Mustacchi 	 */
1694672fc84aSRobert Mustacchi 	if (driver != NULL && strcmp(driver, "scsa2usb") == 0) {
1695672fc84aSRobert Mustacchi 		if ((ret = topo_usb_enum_scsa2usb(mod, tn, lport)) != 0)
1696672fc84aSRobert Mustacchi 			goto error;
1697672fc84aSRobert Mustacchi 	}
1698672fc84aSRobert Mustacchi 
1699672fc84aSRobert Mustacchi 	if (lport->tul_nports > 0 && driver != NULL &&
1700672fc84aSRobert Mustacchi 	    strcmp(driver, "hubd") == 0) {
1701672fc84aSRobert Mustacchi 		if ((ret = topo_usb_enum_port_children(mod, tn, lport)) != 0)
1702672fc84aSRobert Mustacchi 			goto error;
1703672fc84aSRobert Mustacchi 	}
1704672fc84aSRobert Mustacchi 
1705672fc84aSRobert Mustacchi 	di_devfs_path_free(devfs);
1706672fc84aSRobert Mustacchi 	nvlist_free(fmri);
1707672fc84aSRobert Mustacchi 	nvlist_free(auth);
1708672fc84aSRobert Mustacchi 	nvlist_free(modnvl);
1709672fc84aSRobert Mustacchi 	return (0);
1710672fc84aSRobert Mustacchi 
1711672fc84aSRobert Mustacchi error:
1712672fc84aSRobert Mustacchi 	topo_node_unbind(tn);
1713672fc84aSRobert Mustacchi 	di_devfs_path_free(devfs);
1714672fc84aSRobert Mustacchi 	nvlist_free(fmri);
1715672fc84aSRobert Mustacchi 	nvlist_free(auth);
1716672fc84aSRobert Mustacchi 	nvlist_free(modnvl);
1717672fc84aSRobert Mustacchi 	return (-1);
1718672fc84aSRobert Mustacchi }
1719672fc84aSRobert Mustacchi 
1720672fc84aSRobert Mustacchi static int
topo_usb_enum_device(topo_mod_t * mod,tnode_t * pn,topo_usb_port_t * port)1721672fc84aSRobert Mustacchi topo_usb_enum_device(topo_mod_t *mod, tnode_t *pn, topo_usb_port_t *port)
1722672fc84aSRobert Mustacchi {
1723672fc84aSRobert Mustacchi 	int ret;
1724672fc84aSRobert Mustacchi 	topo_instance_t i, max;
1725672fc84aSRobert Mustacchi 	topo_usb_lport_t *l;
1726672fc84aSRobert Mustacchi 
1727672fc84aSRobert Mustacchi 	max = 0;
1728672fc84aSRobert Mustacchi 	for (l = topo_list_next(&port->tup_lports); l != NULL;
1729672fc84aSRobert Mustacchi 	    l = topo_list_next(l)) {
1730672fc84aSRobert Mustacchi 		if (l->tul_device != DI_NODE_NIL)
1731672fc84aSRobert Mustacchi 			max++;
1732672fc84aSRobert Mustacchi 	}
1733672fc84aSRobert Mustacchi 
1734672fc84aSRobert Mustacchi 	if (max == 0) {
1735672fc84aSRobert Mustacchi 		return (0);
1736672fc84aSRobert Mustacchi 	}
1737672fc84aSRobert Mustacchi 
1738672fc84aSRobert Mustacchi 	if ((ret = topo_node_range_create(mod, pn, USB_DEVICE, 0, max - 1)) !=
1739672fc84aSRobert Mustacchi 	    0) {
1740672fc84aSRobert Mustacchi 		return (-1);
1741672fc84aSRobert Mustacchi 	}
1742672fc84aSRobert Mustacchi 
1743672fc84aSRobert Mustacchi 	for (i = 0, l = topo_list_next(&port->tup_lports); l != NULL;
1744672fc84aSRobert Mustacchi 	    l = topo_list_next(l)) {
1745672fc84aSRobert Mustacchi 		if (l->tul_device != DI_NODE_NIL) {
1746672fc84aSRobert Mustacchi 			topo_mod_dprintf(mod, "enumerating device on lport "
1747*6597d6fcSRobert Mustacchi 			    "%u, log inst %" PRIu64 "", l->tul_portno, i);
1748672fc84aSRobert Mustacchi 			if ((ret = topo_usb_enum_lport(mod, pn, port, l,
1749672fc84aSRobert Mustacchi 			    i)) != 0) {
1750672fc84aSRobert Mustacchi 				return (ret);
1751672fc84aSRobert Mustacchi 			}
1752672fc84aSRobert Mustacchi 			i++;
1753672fc84aSRobert Mustacchi 		}
1754672fc84aSRobert Mustacchi 	}
1755672fc84aSRobert Mustacchi 
1756672fc84aSRobert Mustacchi 	return (0);
1757672fc84aSRobert Mustacchi }
1758672fc84aSRobert Mustacchi 
1759672fc84aSRobert Mustacchi static int
topo_usb_enum_controller(topo_mod_t * mod,tnode_t * pnode,topo_usb_controller_t * c,topo_instance_t base)1760672fc84aSRobert Mustacchi topo_usb_enum_controller(topo_mod_t *mod, tnode_t *pnode,
1761672fc84aSRobert Mustacchi     topo_usb_controller_t *c, topo_instance_t base)
1762672fc84aSRobert Mustacchi {
1763672fc84aSRobert Mustacchi 	int ret;
1764672fc84aSRobert Mustacchi 	topo_usb_port_t *port;
1765672fc84aSRobert Mustacchi 
1766672fc84aSRobert Mustacchi 	if (c->tuc_enumed)
1767672fc84aSRobert Mustacchi 		return (0);
1768672fc84aSRobert Mustacchi 
1769672fc84aSRobert Mustacchi 	c->tuc_enumed = B_TRUE;
1770672fc84aSRobert Mustacchi 	if (c->tuc_nports == 0)
1771672fc84aSRobert Mustacchi 		return (0);
1772672fc84aSRobert Mustacchi 
1773672fc84aSRobert Mustacchi 	for (port = topo_list_next(&c->tuc_ports); port != NULL;
1774672fc84aSRobert Mustacchi 	    port = topo_list_next(port)) {
1775672fc84aSRobert Mustacchi 		tnode_t *tn;
1776672fc84aSRobert Mustacchi 		if ((ret = port_create_usb(mod, pnode, base, &tn)) != 0)
1777672fc84aSRobert Mustacchi 			return (ret);
1778672fc84aSRobert Mustacchi 
1779672fc84aSRobert Mustacchi 		if ((ret = topo_usb_port_properties(mod, tn, port)) != 0) {
1780672fc84aSRobert Mustacchi 			return (ret);
1781672fc84aSRobert Mustacchi 		}
1782672fc84aSRobert Mustacchi 
1783672fc84aSRobert Mustacchi 		if ((ret = topo_usb_enum_device(mod, tn, port)) != 0)
1784672fc84aSRobert Mustacchi 			return (ret);
1785672fc84aSRobert Mustacchi 
1786672fc84aSRobert Mustacchi 		base++;
1787672fc84aSRobert Mustacchi 	}
1788672fc84aSRobert Mustacchi 
1789672fc84aSRobert Mustacchi 	return (0);
1790672fc84aSRobert Mustacchi }
1791672fc84aSRobert Mustacchi 
1792672fc84aSRobert Mustacchi static int
topo_usb_enum_mobo(topo_mod_t * mod,tnode_t * pnode,topo_usb_t * usb)1793672fc84aSRobert Mustacchi topo_usb_enum_mobo(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb)
1794672fc84aSRobert Mustacchi {
1795672fc84aSRobert Mustacchi 	int ret;
1796672fc84aSRobert Mustacchi 	topo_usb_controller_t *c;
1797672fc84aSRobert Mustacchi 	topo_instance_t inst = 0;
1798672fc84aSRobert Mustacchi 
1799672fc84aSRobert Mustacchi 	/*
1800672fc84aSRobert Mustacchi 	 * First count the number of ports, so we can create the right range.
1801672fc84aSRobert Mustacchi 	 * Then go back and actually create things. Some of the ports here may
1802672fc84aSRobert Mustacchi 	 * be actually on the chassis, that's OK, we don't mind over counting
1803672fc84aSRobert Mustacchi 	 * here.
1804672fc84aSRobert Mustacchi 	 */
1805672fc84aSRobert Mustacchi 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1806672fc84aSRobert Mustacchi 	    c = topo_list_next(c)) {
1807672fc84aSRobert Mustacchi 		inst += c->tuc_nports;
1808672fc84aSRobert Mustacchi 	}
1809672fc84aSRobert Mustacchi 
1810672fc84aSRobert Mustacchi 	if ((ret = port_range_create(mod, pnode, 0, inst)) != 0) {
1811*6597d6fcSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create port range [0, %"
1812*6597d6fcSRobert Mustacchi 		    PRIu64 ") for mobo", inst);
1813672fc84aSRobert Mustacchi 		return (ret);
1814672fc84aSRobert Mustacchi 	}
1815672fc84aSRobert Mustacchi 
1816672fc84aSRobert Mustacchi 	inst = 0;
1817672fc84aSRobert Mustacchi 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1818672fc84aSRobert Mustacchi 	    c = topo_list_next(c)) {
1819672fc84aSRobert Mustacchi 		if (c->tuc_enumed)
1820672fc84aSRobert Mustacchi 			continue;
1821672fc84aSRobert Mustacchi 		if ((ret = topo_usb_enum_controller(mod, pnode, c, inst)) !=
1822672fc84aSRobert Mustacchi 		    0) {
1823672fc84aSRobert Mustacchi 			return (ret);
1824672fc84aSRobert Mustacchi 		}
1825672fc84aSRobert Mustacchi 		inst += c->tuc_nports;
1826672fc84aSRobert Mustacchi 	}
1827672fc84aSRobert Mustacchi 
1828672fc84aSRobert Mustacchi 	return (0);
1829672fc84aSRobert Mustacchi }
1830672fc84aSRobert Mustacchi 
1831672fc84aSRobert Mustacchi static int
topo_usb_enum_pci(topo_mod_t * mod,tnode_t * pnode,topo_usb_t * usb,di_node_t din)1832672fc84aSRobert Mustacchi topo_usb_enum_pci(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb,
1833672fc84aSRobert Mustacchi     di_node_t din)
1834672fc84aSRobert Mustacchi {
1835672fc84aSRobert Mustacchi 	int ret;
1836672fc84aSRobert Mustacchi 	topo_usb_controller_t *c;
1837672fc84aSRobert Mustacchi 
1838672fc84aSRobert Mustacchi 	for (c = topo_list_next(&usb->tu_controllers); c != NULL;
1839672fc84aSRobert Mustacchi 	    c = topo_list_next(c)) {
1840672fc84aSRobert Mustacchi 		if (din == c->tuc_devinfo) {
1841672fc84aSRobert Mustacchi 			break;
1842672fc84aSRobert Mustacchi 		}
1843672fc84aSRobert Mustacchi 	}
1844672fc84aSRobert Mustacchi 
1845672fc84aSRobert Mustacchi 	if (c == NULL) {
1846672fc84aSRobert Mustacchi 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1847672fc84aSRobert Mustacchi 	}
1848672fc84aSRobert Mustacchi 
1849*6597d6fcSRobert Mustacchi 	if ((ret = port_range_create(mod, pnode, 0, c->tuc_nports)) != 0) {
1850*6597d6fcSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create port range [0, %u) "
1851*6597d6fcSRobert Mustacchi 		    "for controller %s", c->tuc_nports, c->tuc_path);
1852672fc84aSRobert Mustacchi 		return (ret);
1853672fc84aSRobert Mustacchi 	}
1854672fc84aSRobert Mustacchi 
1855*6597d6fcSRobert Mustacchi 	return (topo_usb_enum_controller(mod, pnode, c, 0));
1856672fc84aSRobert Mustacchi }
1857672fc84aSRobert Mustacchi 
1858672fc84aSRobert Mustacchi static int
topo_usb_enum_chassis(topo_mod_t * mod,tnode_t * pnode,topo_usb_t * usb)1859672fc84aSRobert Mustacchi topo_usb_enum_chassis(topo_mod_t *mod, tnode_t *pnode, topo_usb_t *usb)
1860672fc84aSRobert Mustacchi {
1861672fc84aSRobert Mustacchi 	int ret;
1862672fc84aSRobert Mustacchi 	topo_usb_port_t *p;
1863672fc84aSRobert Mustacchi 	topo_instance_t base = 0;
1864672fc84aSRobert Mustacchi 
1865672fc84aSRobert Mustacchi 	if (usb->tu_nchassis_ports == 0)
1866672fc84aSRobert Mustacchi 		return (0);
1867672fc84aSRobert Mustacchi 
1868672fc84aSRobert Mustacchi 	if ((ret = port_range_create(mod, pnode, 0, usb->tu_nchassis_ports)) !=
1869672fc84aSRobert Mustacchi 	    0) {
1870*6597d6fcSRobert Mustacchi 		topo_mod_dprintf(mod, "failed to create port range [0, %u) "
1871*6597d6fcSRobert Mustacchi 		    "for chassis", usb->tu_nchassis_ports);
1872672fc84aSRobert Mustacchi 		return (ret);
1873672fc84aSRobert Mustacchi 	}
1874672fc84aSRobert Mustacchi 
1875672fc84aSRobert Mustacchi 	for (p = topo_list_next(&usb->tu_chassis_ports); p != NULL;
1876672fc84aSRobert Mustacchi 	    p = topo_list_next(p)) {
1877672fc84aSRobert Mustacchi 		tnode_t *tn;
1878672fc84aSRobert Mustacchi 		if ((ret = port_create_usb(mod, pnode, base, &tn)) != 0)
1879672fc84aSRobert Mustacchi 			return (ret);
1880672fc84aSRobert Mustacchi 
1881672fc84aSRobert Mustacchi 		if ((ret = topo_usb_port_properties(mod, tn, p)) != 0) {
1882672fc84aSRobert Mustacchi 			return (ret);
1883672fc84aSRobert Mustacchi 		}
1884672fc84aSRobert Mustacchi 
1885672fc84aSRobert Mustacchi 		if ((ret = topo_usb_enum_device(mod, tn, p)) != 0)
1886672fc84aSRobert Mustacchi 			return (ret);
1887672fc84aSRobert Mustacchi 
1888672fc84aSRobert Mustacchi 		base++;
1889672fc84aSRobert Mustacchi 	}
1890672fc84aSRobert Mustacchi 
1891672fc84aSRobert Mustacchi 	return (0);
1892672fc84aSRobert Mustacchi }
1893672fc84aSRobert Mustacchi 
1894672fc84aSRobert Mustacchi /* ARGSUSED */
1895672fc84aSRobert Mustacchi static int
topo_usb_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * modarg,void * data)1896672fc84aSRobert Mustacchi topo_usb_enum(topo_mod_t *mod, tnode_t *pnode, const char *name,
1897672fc84aSRobert Mustacchi     topo_instance_t min, topo_instance_t max, void *modarg, void *data)
1898672fc84aSRobert Mustacchi {
1899672fc84aSRobert Mustacchi 	topo_usb_t *usb;
1900672fc84aSRobert Mustacchi 	topo_usb_type_t type;
1901672fc84aSRobert Mustacchi 
1902672fc84aSRobert Mustacchi 	if (strcmp(name, USB_PCI) == 0) {
1903672fc84aSRobert Mustacchi 		type = TOPO_USB_PCI;
1904672fc84aSRobert Mustacchi 	} else if (strcmp(name, USB_MOBO) == 0) {
1905672fc84aSRobert Mustacchi 		type = TOPO_USB_MOBO;
1906672fc84aSRobert Mustacchi 	} else if (strcmp(name, USB_CHASSIS) == 0) {
1907672fc84aSRobert Mustacchi 		type = TOPO_USB_CHASSIS;
1908672fc84aSRobert Mustacchi 	} else {
1909672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "usb_enum: asked to enumerate unknown "
1910672fc84aSRobert Mustacchi 		    "component: %s\n", name);
1911672fc84aSRobert Mustacchi 		return (-1);
1912672fc84aSRobert Mustacchi 	}
1913672fc84aSRobert Mustacchi 
1914672fc84aSRobert Mustacchi 	if (type == TOPO_USB_PCI && data == NULL) {
1915672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "usb_enum: missing argument to "
1916672fc84aSRobert Mustacchi 		    "PCI controller enum");
1917672fc84aSRobert Mustacchi 		return (-1);
1918672fc84aSRobert Mustacchi 	} else if (type != TOPO_USB_PCI && data != NULL) {
1919672fc84aSRobert Mustacchi 		topo_mod_dprintf(mod, "extraneous argument to non-controller "
1920672fc84aSRobert Mustacchi 		    "enum %s", name);
1921672fc84aSRobert Mustacchi 		return (-1);
1922672fc84aSRobert Mustacchi 	}
1923672fc84aSRobert Mustacchi 
1924672fc84aSRobert Mustacchi 	if ((usb = topo_mod_getspecific(mod)) == NULL) {
1925672fc84aSRobert Mustacchi 		return (-1);
1926672fc84aSRobert Mustacchi 	}
1927672fc84aSRobert Mustacchi 
1928672fc84aSRobert Mustacchi 	if (!usb->tu_enum_done) {
1929672fc84aSRobert Mustacchi 		if (topo_usb_gather(mod, usb, pnode) != 0)
1930672fc84aSRobert Mustacchi 			return (-1);
1931672fc84aSRobert Mustacchi 		usb->tu_enum_done = B_TRUE;
1932672fc84aSRobert Mustacchi 	}
1933672fc84aSRobert Mustacchi 
1934672fc84aSRobert Mustacchi 	/*
1935672fc84aSRobert Mustacchi 	 * Now that we've built up the topo nodes, enumerate the specific nodes
1936672fc84aSRobert Mustacchi 	 * based on the requested type.
1937672fc84aSRobert Mustacchi 	 */
1938672fc84aSRobert Mustacchi 	if (type == TOPO_USB_PCI) {
1939672fc84aSRobert Mustacchi 		return (topo_usb_enum_pci(mod, pnode, usb, data));
1940672fc84aSRobert Mustacchi 	} else if (type == TOPO_USB_MOBO) {
1941672fc84aSRobert Mustacchi 		return (topo_usb_enum_mobo(mod, pnode, usb));
1942672fc84aSRobert Mustacchi 	} else if (type == TOPO_USB_CHASSIS) {
1943672fc84aSRobert Mustacchi 		return (topo_usb_enum_chassis(mod, pnode, usb));
1944672fc84aSRobert Mustacchi 	}
1945672fc84aSRobert Mustacchi 
1946672fc84aSRobert Mustacchi 	return (0);
1947672fc84aSRobert Mustacchi }
1948672fc84aSRobert Mustacchi 
1949672fc84aSRobert Mustacchi static const topo_modops_t usb_ops = {
1950672fc84aSRobert Mustacchi 	topo_usb_enum, NULL
1951672fc84aSRobert Mustacchi };
1952672fc84aSRobert Mustacchi 
1953672fc84aSRobert Mustacchi static topo_modinfo_t usb_mod = {
1954672fc84aSRobert Mustacchi 	USB, FM_FMRI_SCHEME_HC, USB_VERSION, &usb_ops
1955672fc84aSRobert Mustacchi };
1956672fc84aSRobert Mustacchi 
1957672fc84aSRobert Mustacchi static void
topo_usb_port_free(topo_mod_t * mod,topo_usb_port_t * p)1958672fc84aSRobert Mustacchi topo_usb_port_free(topo_mod_t *mod, topo_usb_port_t *p)
1959672fc84aSRobert Mustacchi {
1960672fc84aSRobert Mustacchi 	topo_usb_lport_t *lport;
1961672fc84aSRobert Mustacchi 
1962672fc84aSRobert Mustacchi 	while ((lport = topo_list_next(&p->tup_lports)) != NULL) {
1963672fc84aSRobert Mustacchi 		topo_usb_port_t *child;
1964672fc84aSRobert Mustacchi 
1965672fc84aSRobert Mustacchi 		topo_list_delete(&p->tup_lports, lport);
1966672fc84aSRobert Mustacchi 		while ((child = topo_list_next(&lport->tul_ports)) != NULL) {
1967672fc84aSRobert Mustacchi 			topo_list_delete(&lport->tul_ports, child);
1968672fc84aSRobert Mustacchi 			topo_usb_port_free(mod, child);
1969672fc84aSRobert Mustacchi 		}
1970672fc84aSRobert Mustacchi 		topo_mod_free(mod, lport, sizeof (topo_usb_lport_t));
1971672fc84aSRobert Mustacchi 	}
1972672fc84aSRobert Mustacchi 
1973672fc84aSRobert Mustacchi 	topo_mod_free(mod, p, sizeof (topo_usb_port_t));
1974672fc84aSRobert Mustacchi }
1975672fc84aSRobert Mustacchi 
1976672fc84aSRobert Mustacchi static void
topo_usb_free(topo_mod_t * mod,topo_usb_t * usb)1977672fc84aSRobert Mustacchi topo_usb_free(topo_mod_t *mod, topo_usb_t *usb)
1978672fc84aSRobert Mustacchi {
1979672fc84aSRobert Mustacchi 	topo_usb_controller_t *c;
1980672fc84aSRobert Mustacchi 	topo_usb_port_t *p;
1981672fc84aSRobert Mustacchi 
1982672fc84aSRobert Mustacchi 	if (usb == NULL)
1983672fc84aSRobert Mustacchi 		return;
1984672fc84aSRobert Mustacchi 
1985672fc84aSRobert Mustacchi 	while ((p = topo_list_next(&usb->tu_chassis_ports)) != NULL) {
1986672fc84aSRobert Mustacchi 		topo_list_delete(&usb->tu_chassis_ports, p);
1987672fc84aSRobert Mustacchi 		topo_usb_port_free(mod, p);
1988672fc84aSRobert Mustacchi 	}
1989672fc84aSRobert Mustacchi 
1990672fc84aSRobert Mustacchi 	while ((c = topo_list_next(&usb->tu_controllers)) != NULL) {
1991672fc84aSRobert Mustacchi 
1992672fc84aSRobert Mustacchi 		topo_list_delete(&usb->tu_controllers, c);
1993672fc84aSRobert Mustacchi 		di_devfs_path_free(c->tuc_path);
1994672fc84aSRobert Mustacchi 
1995672fc84aSRobert Mustacchi 		while ((p = topo_list_next(&c->tuc_ports)) != NULL) {
1996672fc84aSRobert Mustacchi 			topo_list_delete(&c->tuc_ports, p);
1997672fc84aSRobert Mustacchi 			topo_usb_port_free(mod, p);
1998672fc84aSRobert Mustacchi 		}
1999672fc84aSRobert Mustacchi 		topo_mod_free(mod, c, sizeof (topo_usb_controller_t));
2000672fc84aSRobert Mustacchi 	}
2001672fc84aSRobert Mustacchi 
2002672fc84aSRobert Mustacchi 	topo_usb_free_metadata(mod, &usb->tu_metadata);
2003672fc84aSRobert Mustacchi 
2004672fc84aSRobert Mustacchi 	/*
2005672fc84aSRobert Mustacchi 	 * The devinfo handle came from fm, don't do anything ourselevs.
2006672fc84aSRobert Mustacchi 	 */
2007672fc84aSRobert Mustacchi 	usb->tu_devinfo = DI_NODE_NIL;
2008672fc84aSRobert Mustacchi 
2009672fc84aSRobert Mustacchi 	topo_mod_free(mod, usb, sizeof (topo_usb_t));
2010672fc84aSRobert Mustacchi }
2011672fc84aSRobert Mustacchi 
2012672fc84aSRobert Mustacchi static topo_usb_t *
topo_usb_alloc(topo_mod_t * mod)2013672fc84aSRobert Mustacchi topo_usb_alloc(topo_mod_t *mod)
2014672fc84aSRobert Mustacchi {
2015672fc84aSRobert Mustacchi 	topo_usb_t *usb = NULL;
2016672fc84aSRobert Mustacchi 
2017672fc84aSRobert Mustacchi 	if ((usb = topo_mod_zalloc(mod, sizeof (topo_usb_t))) == NULL) {
2018672fc84aSRobert Mustacchi 		goto free;
2019672fc84aSRobert Mustacchi 	}
2020672fc84aSRobert Mustacchi 
2021672fc84aSRobert Mustacchi 	if ((usb->tu_devinfo = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
2022672fc84aSRobert Mustacchi 		goto free;
2023672fc84aSRobert Mustacchi 	}
2024672fc84aSRobert Mustacchi 
2025672fc84aSRobert Mustacchi 	return (usb);
2026672fc84aSRobert Mustacchi 
2027672fc84aSRobert Mustacchi free:
2028672fc84aSRobert Mustacchi 	topo_usb_free(mod, usb);
2029672fc84aSRobert Mustacchi 	return (NULL);
2030672fc84aSRobert Mustacchi }
2031672fc84aSRobert Mustacchi 
2032672fc84aSRobert Mustacchi int
_topo_init(topo_mod_t * mod,topo_version_t version)2033672fc84aSRobert Mustacchi _topo_init(topo_mod_t *mod, topo_version_t version)
2034672fc84aSRobert Mustacchi {
2035672fc84aSRobert Mustacchi 	topo_usb_t *usb;
2036672fc84aSRobert Mustacchi 
2037672fc84aSRobert Mustacchi 	if (getenv("TOPOUSBDEBUG") != NULL)
2038672fc84aSRobert Mustacchi 		topo_mod_setdebug(mod);
2039672fc84aSRobert Mustacchi 
2040672fc84aSRobert Mustacchi 	topo_mod_dprintf(mod, "_mod_init: initializing %s enumerator\n", USB);
2041672fc84aSRobert Mustacchi 
2042672fc84aSRobert Mustacchi 	if (version != USB_VERSION) {
2043672fc84aSRobert Mustacchi 		return (-1);
2044672fc84aSRobert Mustacchi 	}
2045672fc84aSRobert Mustacchi 
2046672fc84aSRobert Mustacchi 	if ((usb = topo_usb_alloc(mod)) == NULL) {
2047672fc84aSRobert Mustacchi 		return (-1);
2048672fc84aSRobert Mustacchi 	}
2049672fc84aSRobert Mustacchi 
2050672fc84aSRobert Mustacchi 	if (topo_mod_register(mod, &usb_mod, TOPO_VERSION) != 0) {
2051672fc84aSRobert Mustacchi 		topo_usb_free(mod, usb);
2052672fc84aSRobert Mustacchi 		return (-1);
2053672fc84aSRobert Mustacchi 	}
2054672fc84aSRobert Mustacchi 
2055672fc84aSRobert Mustacchi 	topo_mod_setspecific(mod, usb);
2056672fc84aSRobert Mustacchi 
2057672fc84aSRobert Mustacchi 	return (0);
2058672fc84aSRobert Mustacchi }
2059672fc84aSRobert Mustacchi 
2060672fc84aSRobert Mustacchi void
_topo_fini(topo_mod_t * mod)2061672fc84aSRobert Mustacchi _topo_fini(topo_mod_t *mod)
2062672fc84aSRobert Mustacchi {
2063672fc84aSRobert Mustacchi 	topo_usb_free(mod, topo_mod_getspecific(mod));
2064672fc84aSRobert Mustacchi 	topo_mod_setspecific(mod, NULL);
2065672fc84aSRobert Mustacchi }
2066