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