2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A
2N/A#include <strings.h>
2N/A#include <devid.h>
2N/A#include <pthread.h>
2N/A#include <inttypes.h>
2N/A#include <sys/pci.h>
2N/A#include <fm/topo_mod.h>
2N/A#include <fm/topo_list.h>
2N/A#include <sys/fm/protocol.h>
2N/A#include <fm/topo_mod.h>
2N/A#include <libdevinfo.h>
2N/A
2N/A#define MAX_USB_DEVS 127
2N/A#define MAX_USB_INTERFACE 127
2N/A
2N/A/* Global Definition */
2N/Atypedef struct usb_enum_dev {
2N/A topo_list_t ued_link;
2N/A topo_mod_t *ued_mod;
2N/A di_node_t ued_dnode;
2N/A int ued_inst; /* device instance */
2N/A char ued_name[8];
2N/A di_node_t ued_pnode; /* most recent pci bridge node */
2N/A int ued_enum; /* whether be enumed */
2N/A int ued_bus;
2N/A int ued_dev;
2N/A int ued_fun;
2N/A} usb_enum_dev_t;
2N/A
2N/Atypedef struct usb_enum_data {
2N/A topo_list_t ue_devs; /* host controller list */
2N/A topo_mod_t *ue_mod;
2N/A topo_instance_t ue_instance;
2N/A} usb_enum_data_t;
2N/A
2N/Atypedef struct usb_cbdata {
2N/A topo_mod_t *dcb_mod;
2N/A usb_enum_data_t *dcb_data;
2N/A} usb_cbdata_t;
2N/A
2N/A/* Static Definition */
2N/Astatic const topo_pgroup_info_t io_pgroup =
2N/A { TOPO_PGROUP_IO, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
2N/Astatic const topo_pgroup_info_t usb_pgroup =
2N/A { TOPO_PGROUP_USB, TOPO_STABILITY_PRIVATE, TOPO_STABILITY_PRIVATE, 1 };
2N/A
2N/Astatic int usb_is_from_pci = 0;
2N/A
2N/A/* Function Entry */
2N/Astatic tnode_t *
2N/Ausb_tnode_create(topo_mod_t *mp, tnode_t *pn, char *name,
2N/A topo_instance_t i, void *priv);
2N/A
2N/Astatic int
2N/Ausb_populate_prop(topo_mod_t *mod, tnode_t *tn, di_node_t dn);
2N/A
2N/Astatic int
2N/Ausb_children_instantiate(topo_mod_t *mod, tnode_t *pnode,
2N/A di_node_t pn, int depth);
2N/A
2N/Astatic int
2N/Ausb_set_asru(topo_mod_t *mod, tnode_t *tn, di_node_t dn);
2N/A
2N/Astatic int
2N/Ausb_enum_from_pci(topo_mod_t *mod, tnode_t *pnode,
2N/A const char *name, topo_instance_t min, topo_instance_t max,
2N/A void *arg, void *data);
2N/A
2N/Astatic int
2N/Ausb_promprop2int(topo_mod_t *mod, di_node_t n,
2N/A const char *propnm, int **val);
2N/A
2N/Astatic void
2N/Ausb_list_cleanup(topo_mod_t *mod, usb_enum_data_t *prvdata);
2N/A
2N/Astatic int
2N/Ausb_enum_from_hostbridge(topo_mod_t *mod, tnode_t *pnode,
2N/A usb_enum_data_t *data);
2N/A
2N/A/* Topo pluggin enum entry */
2N/Astatic int usb_enum(topo_mod_t *, tnode_t *, const char *,
2N/A topo_instance_t, topo_instance_t, void *, void *);
2N/A
2N/Astatic void usb_release(topo_mod_t *mod, tnode_t *tn);
2N/A
2N/A/* Topo pluggin enum ops struct */
2N/Astatic const topo_modops_t usb_ops =
2N/A { usb_enum, usb_release};
2N/A
2N/Astatic const topo_modinfo_t usb_info =
2N/A {USBTOPO, FM_FMRI_SCHEME_HC, TOPO_VERSION, &usb_ops};
2N/A
2N/A
2N/Astatic int
2N/Ausb_hwprop2uint(di_node_t n, const char *propnm, uint_t **val)
2N/A{
2N/A di_prop_t hp = DI_PROP_NIL;
2N/A uchar_t *buf;
2N/A
2N/A while ((hp = di_prop_next(n, hp)) != DI_PROP_NIL) {
2N/A if (strncmp(di_prop_name(hp), propnm, 32) == 0) {
2N/A if (di_prop_bytes(hp, &buf) < sizeof (uint_t))
2N/A continue;
2N/A /* LINTED E_BAD_PTR_CAST_ALIGN */
2N/A *val = (uint_t *)buf;
2N/A
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Get uint property from di_node_t
2N/A */
2N/Aint
2N/Ausb_di_uintprop_get(topo_mod_t *mod, di_node_t n, const char *pnm, uint_t **pv)
2N/A{
2N/A if (usb_hwprop2uint(n, pnm, pv) < 0)
2N/A if (usb_promprop2int(mod, n, pnm, (int **)pv) < 0)
2N/A return (-1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Add a host controller to the global list.
2N/A */
2N/Astatic int
2N/Ausb_host_di_node_add(di_node_t node, usb_cbdata_t *cbp)
2N/A{
2N/A usb_enum_dev_t *controller;
2N/A di_node_t pdn;
2N/A topo_mod_t *mod = cbp->dcb_mod;
2N/A uint_t *reg;
2N/A int bus, dev, fn;
2N/A topo_list_t *listp = &(cbp->dcb_data->ue_devs);
2N/A
2N/A if (usb_di_uintprop_get(mod, node, "reg", &reg) < 0) {
2N/A topo_mod_dprintf(mod, "usb_host_di_node_add: "
2N/A "fail to get BDF of %s%d node", di_driver_name(node),
2N/A di_instance(node));
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A controller = topo_mod_zalloc(cbp->dcb_mod, sizeof (usb_enum_dev_t));
2N/A if (controller == NULL) {
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A bus = PCI_REG_BUS_G(*reg);
2N/A dev = PCI_REG_DEV_G(*reg);
2N/A fn = PCI_REG_FUNC_G(*reg);
2N/A
2N/A topo_mod_dprintf(mod, "usb_host_di_node_add: BDF of %s%d node"
2N/A " : %02x %02x %02x", di_driver_name(node), di_instance(node),
2N/A bus, dev, fn);
2N/A
2N/A pdn = di_parent_node(node);
2N/A
2N/A controller->ued_dnode = node;
2N/A controller->ued_mod = cbp->dcb_mod;
2N/A controller->ued_inst = di_instance(node);
2N/A controller->ued_bus = bus;
2N/A controller->ued_dev = dev;
2N/A controller->ued_fun = fn;
2N/A
2N/A (void) snprintf(controller->ued_name, 8, "%s", di_driver_name(node));
2N/A
2N/A do {
2N/A topo_mod_dprintf(cbp->dcb_mod, "usb_host_di_node_add: %s",
2N/A di_driver_name(pdn));
2N/A
2N/A /* search for the most recent PCI bridge upward */
2N/A if ((strncmp(di_driver_name(pdn), "pci_pci", 16) == 0) ||
2N/A (strncmp(di_driver_name(pdn), "pcieb", 16) == 0) ||
2N/A (strncmp(di_driver_name(pdn), "npe", 16) == 0)) {
2N/A controller->ued_pnode = pdn;
2N/A
2N/A break;
2N/A }
2N/A } while (pdn = di_parent_node(pdn));
2N/A
2N/A /* append the host controller to the global list */
2N/A topo_list_append(listp, controller);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Use BDF information to find the corresponding HC in host controller list.
2N/A */
2N/Astatic int
2N/Ausb_host_di_node_search(topo_mod_t *mod, di_node_t node, usb_enum_data_t *data,
2N/A usb_enum_dev_t **host)
2N/A{
2N/A int bus, dev, fn;
2N/A usb_enum_dev_t *controller = NULL;
2N/A uint_t *reg;
2N/A topo_list_t *listp = &(data->ue_devs);
2N/A
2N/A topo_mod_dprintf(mod, "usb_host_di_node_search: "
2N/A "search for %s%d node", di_driver_name(node),
2N/A di_instance(node));
2N/A
2N/A if (usb_di_uintprop_get(mod, node, "reg", &reg) < 0) {
2N/A topo_mod_dprintf(mod, "usb_host_di_node_search: "
2N/A "fail to get BDF of %s%d node", di_driver_name(node),
2N/A di_instance(node));
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A bus = PCI_REG_BUS_G(*reg);
2N/A dev = PCI_REG_DEV_G(*reg);
2N/A fn = PCI_REG_FUNC_G(*reg);
2N/A
2N/A *host = NULL;
2N/A
2N/A /*
2N/A * Traverse the ue_devs list. ue_devs is the header
2N/A * node, search starts from the next
2N/A */
2N/A while ((listp = topo_list_next(listp)) != NULL) {
2N/A controller = (usb_enum_dev_t *)listp;
2N/A if (controller->ued_bus == bus &&
2N/A controller->ued_dev == dev &&
2N/A controller->ued_fun == fn) {
2N/A *host = controller;
2N/A
2N/A break;
2N/A }
2N/A controller = NULL;
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "usb_host_di_node_search: %s found BDF "
2N/A "= %02x %02x %02x\n", (controller != NULL) ? "":"not", bus,
2N/A dev, fn);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Callback entry for di_walk_node
2N/A * If the node is a USB host controllers, put it onto
2N/A * the host controller list.
2N/A */
2N/Astatic int
2N/Ausb_dev_walk_di_nodes(di_node_t node, void *arg)
2N/A{
2N/A char *driver_name;
2N/A usb_cbdata_t *cbp = (usb_cbdata_t *)arg;
2N/A topo_mod_t *mod = cbp->dcb_mod;
2N/A
2N/A driver_name = di_driver_name(node);
2N/A if (driver_name == NULL) {
2N/A
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A
2N/A if ((strncmp(driver_name, "ehci", 8) == 0) ||
2N/A (strncmp(driver_name, "ohci", 8) == 0) ||
2N/A (strncmp(driver_name, "uhci", 8) == 0) ||
2N/A (strncmp(driver_name, "xhci", 8) == 0)) {
2N/A topo_mod_dprintf(mod, "usb_dev_walk_di_nodes: "
2N/A "find %s device", driver_name);
2N/A
2N/A if (usb_host_di_node_add(node, arg) != 0) {
2N/A topo_mod_dprintf(mod, "usb_dev_walk_di_nodes: "
2N/A "fail to add %s%d node", driver_name,
2N/A di_instance(node));
2N/A
2N/A return (DI_WALK_CONTINUE);
2N/A }
2N/A }
2N/A
2N/A return (DI_WALK_CONTINUE);
2N/A}
2N/A
2N/A/*
2N/A * To collect all the host controllers in the system
2N/A */
2N/Aint
2N/Ausb_host_list_gather(topo_mod_t *mod, usb_enum_data_t *data)
2N/A{
2N/A di_node_t devtree;
2N/A usb_cbdata_t dcb;
2N/A
2N/A topo_mod_dprintf(mod, "usb_host_list_gather: entry");
2N/A
2N/A /* Get the devinfo tree */
2N/A if ((devtree = topo_mod_devinfo(mod)) == DI_NODE_NIL) {
2N/A topo_mod_dprintf(mod, "usb_host_list_gather: "
2N/A "topo_mod_devinfo() failed");
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A dcb.dcb_mod = mod;
2N/A dcb.dcb_data = data;
2N/A /* walk the devinfo snapshot looking for host controller nodes */
2N/A (void) di_walk_node(devtree, DI_WALK_CLDFIRST, &dcb,
2N/A usb_dev_walk_di_nodes);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Use the parent's FRU as the FRU of host controllers enumerated from PCI
2N/A */
2N/Aint
2N/Ausb_set_host_fru(topo_mod_t *mod, tnode_t *pnode, tnode_t *tn)
2N/A{
2N/A int err;
2N/A nvlist_t *fru = NULL;
2N/A
2N/A topo_mod_dprintf(mod, "usb_set_host_fru: %s", topo_node_name(tn));
2N/A
2N/A if (topo_node_fru(pnode, &fru, NULL, &err) != 0) {
2N/A topo_mod_dprintf(mod, "usb_set_host_fru: can't find the"
2N/A " motherboard");
2N/A
2N/A return (-1);
2N/A
2N/A } else {
2N/A (void) topo_node_fru_set(tn, fru, 0, &err);
2N/A nvlist_free(fru);
2N/A
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * When USB is enumerated from a Map file or x86pi, USB enumerator
2N/A * will create 'usb-bus' topo nodes for all the host controllers that
2N/A * have no PCI('pcifn' or 'pciexfn') nodes.
2N/A */
2N/Aint
2N/Ausb_process_host_controllers(topo_mod_t *mod, tnode_t *pnode,
2N/A usb_enum_data_t *data)
2N/A{
2N/A usb_enum_dev_t *phost;
2N/A int i = 0;
2N/A tnode_t *rn; /* root hub node */
2N/A int err = 0;
2N/A nvlist_t *pfmri = NULL;
2N/A di_node_t cdn;
2N/A
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers: parent %s",
2N/A topo_node_name(pnode));
2N/A
2N/A if (topo_node_resource(pnode, &pfmri, &err) < 0) {
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers: "
2N/A "can't find the parent FMRI");
2N/A } else {
2N/A nvlist_free(pfmri);
2N/A pfmri = NULL;
2N/A }
2N/A
2N/A /* Enumerated from the host controller we gathered */
2N/A for (phost = topo_list_next(&data->ue_devs); phost != NULL;
2N/A phost = topo_list_next(phost)) {
2N/A /* If it was enumerated from PCI before, skip to next */
2N/A if (phost->ued_enum) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_process_host_controllers: %s enumed before"
2N/A "goto next parent =%s", phost->ued_name,
2N/A (phost->ued_pnode) ?
2N/A di_driver_name(phost->ued_pnode) : "no parent");
2N/A
2N/A continue;
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers: %s, p=%s",
2N/A phost->ued_name,
2N/A (phost->ued_pnode) ? di_driver_name(phost->ued_pnode):
2N/A "no parent");
2N/A
2N/A /*
2N/A * we will not use device instance number here, because
2N/A * different host controller may have the same instance #.
2N/A */
2N/A if ((rn = usb_tnode_create(mod, pnode, USB_BUS, i++,
2N/A phost->ued_dnode)) == NULL) {
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers:"
2N/A "fail to create topo node for %s", phost->ued_name);
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A /* Set it as enumerated */
2N/A phost->ued_enum = 1;
2N/A
2N/A (void) usb_populate_prop(mod, rn, phost->ued_dnode);
2N/A
2N/A cdn = di_child_node(phost->ued_dnode);
2N/A if (cdn == DI_NODE_NIL) {
2N/A
2N/A continue;
2N/A }
2N/A
2N/A /* create node range for devices under the root hub */
2N/A if (topo_node_range_create(mod, rn, USB_DEV, 0,
2N/A MAX_USB_DEVS) < 0) {
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers:"
2N/A " can't create range");
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A /* Also create node range for hubs under root hub */
2N/A if (topo_node_range_create(mod, rn, USB_HUB, 0,
2N/A MAX_USB_DEVS) < 0) {
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers:"
2N/A " can't create range");
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A /* walk and instantiate each child node of the root hub */
2N/A if (usb_children_instantiate(mod, rn, cdn, 0) != 0) {
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers:"
2N/A " fail to instantiate children");
2N/A
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Create necessary information for a host controller which is enumerated
2N/A * by PCI enumerator. Also create topology nodes for all the devices connected
2N/A * to this controller.
2N/A */
2N/Aint
2N/Ausb_process_single_host(topo_mod_t *mod, tnode_t *pnode,
2N/A usb_enum_data_t *data)
2N/A{
2N/A tnode_t *rn; /* root hub node */
2N/A int err = 0;
2N/A nvlist_t *pfmri = NULL;
2N/A di_node_t cdn;
2N/A di_node_t pdn;
2N/A usb_enum_dev_t *host = NULL;
2N/A
2N/A topo_mod_dprintf(mod, "usb_process_single_host: parent %s",
2N/A topo_node_name(pnode));
2N/A
2N/A rn = pnode;
2N/A if (topo_node_resource(pnode, &pfmri, &err) < 0) {
2N/A topo_mod_dprintf(mod, "usb_process_single_host: "
2N/A "can't find the parent FMRI");
2N/A } else {
2N/A nvlist_free(pfmri);
2N/A pfmri = NULL;
2N/A }
2N/A
2N/A /* Parent topo_node need provide di_node from its specific data */
2N/A if ((pdn = topo_node_getspecific(pnode)) == DI_NODE_NIL) {
2N/A topo_mod_dprintf(mod,
2N/A "Parent %s node missing private data.\n"
2N/A "Unable to proceed with %s enumeration.",
2N/A topo_node_name(pnode), USBTOPO);
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * The 'usb-bus' node was ever created to represent 'roothub'. However,
2N/A * two topo nodes('usb-bus' & 'pcifn/pciexfn') for the same device will
2N/A * cause mess for diagnosis engine. To avoid such confusion, the
2N/A * 'usb-bus' node is no longer created for the devices which are
2N/A * enumerated from PCI parent.
2N/A */
2N/A
2N/A /*
2N/A * To avoid PCI, map file or x86pi enumerating the same host controller,
2N/A * we first check to see if other enumerator has processed this HC. If
2N/A * not, we'll process it and mark it as enumerated.
2N/A */
2N/A (void) usb_host_di_node_search(mod, pdn, data, &host);
2N/A if (host != NULL) {
2N/A host->ued_enum = 1;
2N/A } else {
2N/A topo_mod_dprintf(mod, "usb_process_single_host:"
2N/A "fail to search host node for %s",
2N/A topo_node_name(pnode));
2N/A }
2N/A
2N/A /*
2N/A * Check if HC has children device. If so, enumerate the children
2N/A * to generate the topo nodes.
2N/A */
2N/A cdn = di_child_node(pdn);
2N/A if (cdn == DI_NODE_NIL) {
2N/A
2N/A return (0);
2N/A }
2N/A
2N/A /* create node range for devices under the root hub */
2N/A if (topo_node_range_create(mod, rn, USB_DEV, 0,
2N/A MAX_USB_DEVS) < 0) {
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers:"
2N/A " can't create range");
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A /* Also create node range for hubs under root hub */
2N/A if (topo_node_range_create(mod, rn, USB_HUB, 0,
2N/A MAX_USB_DEVS) < 0) {
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers:"
2N/A " can't create range");
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A /* walk and instantiate each child node of the root hub */
2N/A if (usb_children_instantiate(mod, rn, cdn, 0)
2N/A != 0) {
2N/A topo_mod_dprintf(mod, "usb_process_host_controllers:"
2N/A " fail to instantiate children");
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * USB topo enum entry
2N/A */
2N/A/* ARGSUSED */
2N/Astatic int
2N/Ausb_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
2N/A topo_instance_t min, topo_instance_t max, void *arg, void *notused)
2N/A{
2N/A usb_enum_data_t *data;
2N/A char *rname;
2N/A
2N/A topo_mod_dprintf(mod, "usb_enum: %s %d %d nodename:%s ",
2N/A name, min, max, topo_node_name(rnode));
2N/A
2N/A /*
2N/A * Check to make sure we're being invoked sensibly, and that we're not
2N/A * being invoked as part of a post-processing step.
2N/A */
2N/A if (strncmp(name, USB_BUS, 8) != 0) {
2N/A
2N/A return (0);
2N/A }
2N/A
2N/A if ((data = topo_mod_getspecific(mod)) == NULL) {
2N/A topo_mod_dprintf(mod, "usb_enum: specific private "
2N/A "data not set");
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Normally, USB enumeration starts from parent PCI enumerator or from
2N/A * Maps. But, there are cases where the system has both on-chip(built
2N/A * in southbridge) USB host controllers and non-on-chip pci USB hosts.
2N/A * In this case, some of the host controllers will be enumerated by
2N/A * the parent PCI nodes, while others need to be enumerated by XML(from
2N/A * hostbridge). Please refer to *.xml under /usr/platform/'uname -i'/
2N/A * lib/fm/topo/maps
2N/A */
2N/A rname = topo_node_name(rnode);
2N/A topo_mod_dprintf(mod, "usb_enum: parent name %s", rname);
2N/A
2N/A if (strncmp(rname, HOSTBRIDGE, 16) == 0) {
2N/A topo_mod_dprintf(mod, "usb_enum: enum from hostbridge."
2N/A " PCI has %s enumerated", usb_is_from_pci ?
2N/A "" : " NOT");
2N/A
2N/A /* Enum from hostbridge, all HCs enumerated together */
2N/A (void) usb_enum_from_hostbridge(mod, rnode, data);
2N/A } else if (strncmp(rname, PCI_FUNCTION, 16) == 0 ||
2N/A strncmp(rname, PCIEX_FUNCTION, 16) == 0) {
2N/A /* Enum from PCI, here only one HC enumerated each time */
2N/A topo_mod_dprintf(mod, "usb_enum: enum from pci");
2N/A
2N/A (void) usb_enum_from_pci(mod, rnode, name, min, max, arg, data);
2N/A } else {
2N/A topo_mod_dprintf(mod, "usb_enum: enum error from ",
2N/A "invalid parent %s", rname);
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Ausb_list_cleanup(topo_mod_t *mod, usb_enum_data_t *prvdata)
2N/A{
2N/A topo_list_t *tpnode = NULL;
2N/A topo_list_t *cpl;
2N/A
2N/A cpl = &prvdata->ue_devs;
2N/A
2N/A /*
2N/A * Traverse the ue_devs list. ue_devs is the header
2N/A * node, search starts from the next
2N/A */
2N/A while ((tpnode = topo_list_next(cpl)) != NULL) {
2N/A topo_list_delete(cpl, tpnode);
2N/A topo_mod_free(mod, tpnode, sizeof (usb_enum_dev_t));
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Ausb_release(topo_mod_t *mod, tnode_t *tn)
2N/A{
2N/A topo_mod_dprintf(mod, "usb_release: entry nodename:%s ",
2N/A topo_node_name(tn));
2N/A
2N/A topo_mod_dprintf(mod, "usb_release: end");
2N/A}
2N/A
2N/A/* ARGSUSED */
2N/Astatic int
2N/Ausb_enum_from_pci(topo_mod_t *mod, tnode_t *pnode,
2N/A const char *name, topo_instance_t min, topo_instance_t max,
2N/A void *arg, void *data)
2N/A{
2N/A int err = 0;
2N/A
2N/A topo_mod_dprintf(mod, "usb_enum_from_pci: %s %d %d nodename:%s ",
2N/A name, min, max, topo_node_name(pnode));
2N/A
2N/A usb_is_from_pci++;
2N/A
2N/A err = usb_process_single_host(mod, pnode, data);
2N/A if (err != 0) {
2N/A topo_mod_dprintf(mod, "usb_enum_from_pci: process host fail");
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/Aint
2N/Ausb_enum_from_hostbridge(topo_mod_t *mod, tnode_t *pnode,
2N/A usb_enum_data_t *data)
2N/A{
2N/A return (usb_process_host_controllers(mod, pnode, data));
2N/A}
2N/A
2N/A/* ARGSUSED */
2N/Aint
2N/A_topo_init(topo_mod_t *mod, topo_version_t version)
2N/A{
2N/A usb_enum_data_t *data = NULL;
2N/A /*
2N/A * Turn on module debugging output
2N/A */
2N/A if (getenv("TOPOUSBDEBUG") != NULL)
2N/A topo_mod_setdebug(mod);
2N/A topo_mod_dprintf(mod, "_topo_init: "
2N/A "initializing %s enumerator", USBTOPO);
2N/A
2N/A if (topo_mod_register(mod, &usb_info, TOPO_VERSION) != 0) {
2N/A topo_mod_dprintf(mod, "_topo_init: "
2N/A "%s registration failed: %s",
2N/A USBTOPO, topo_mod_errmsg(mod));
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A if ((data = topo_mod_getspecific(mod)) == NULL) {
2N/A topo_mod_dprintf(mod, "_topo_init: %s enumerator alloc "
2N/A "specific private data", USBTOPO);
2N/A if ((data = topo_mod_zalloc(mod, sizeof (usb_enum_data_t))) ==
2N/A NULL) {
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A data->ue_mod = mod;
2N/A topo_mod_setspecific(mod, data);
2N/A
2N/A /*
2N/A * Gather all host controllers in system
2N/A */
2N/A if (usb_host_list_gather(mod, data) != 0) {
2N/A usb_list_cleanup(mod, data);
2N/A topo_mod_free(mod, data, sizeof (usb_enum_data_t));
2N/A
2N/A return (-1);
2N/A }
2N/A topo_mod_dprintf(mod, "_topo_init: %s enumerator usb host "
2N/A "controller first time gathered!", USBTOPO);
2N/A }
2N/A topo_mod_dprintf(mod, "_topo_init: "
2N/A "%s enumerator initialized", USBTOPO);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/A_topo_fini(topo_mod_t *mod)
2N/A{
2N/A usb_enum_data_t *data = NULL;
2N/A if ((data = topo_mod_getspecific(mod)) != NULL) {
2N/A topo_mod_dprintf(mod, "_topo_fini: %s enumerator "
2N/A "free private data for mod", USBTOPO);
2N/A usb_list_cleanup(mod, data);
2N/A topo_mod_free(mod, data, sizeof (usb_enum_data_t));
2N/A
2N/A /* Avoid duplicate mem free */
2N/A topo_mod_setspecific(mod, NULL);
2N/A }
2N/A
2N/A topo_mod_unregister(mod);
2N/A topo_mod_dprintf(mod, "_topo_fini: "
2N/A "%s enumerator uninitialized\n", USBTOPO);
2N/A}
2N/A
2N/Astatic int
2N/Aget_usb_vpid(topo_mod_t *mp, di_node_t dn, char *tname, char **serial,
2N/A char **part)
2N/A{
2N/A char tmp[32];
2N/A char *s = NULL;
2N/A int *vid = NULL, *pid = NULL;
2N/A di_node_t pdn;
2N/A
2N/A /*
2N/A * Get Part value in HC scheme
2N/A * Part is composed of Pid-Vid value pair
2N/A */
2N/A
2N/A if (strncmp(tname, USB_BUS, 8) == 0 ||
2N/A strncmp(tname, USB_HUB, 8) == 0) {
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "root-hub",
2N/A &vid) == 0) {
2N/A topo_mod_dprintf(mp,
2N/A "get_usb_vpid: root-hub");
2N/A
2N/A if ((di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
2N/A "vendor-id", &vid) < 0) &&
2N/A (usb_promprop2int(mp, dn, "vendor-id", &vid) < 0)) {
2N/A topo_mod_dprintf(mp,
2N/A "get_usb_vpid: can't get host vid: %s\n");
2N/A }
2N/A
2N/A if ((di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "device-id",
2N/A &pid) < 0) &&
2N/A (usb_promprop2int(mp, dn, "device-id", &pid) < 0)) {
2N/A topo_mod_dprintf(mp,
2N/A "get_usb_vpid: can't get host pid\n");
2N/A }
2N/A } else {
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
2N/A "usb-vendor-id", &vid) < 0) {
2N/A topo_mod_dprintf(mp,
2N/A "get_usb_vpid: fail to get vid: %s\n");
2N/A }
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
2N/A "usb-product-id", &pid) < 0) {
2N/A topo_mod_dprintf(mp,
2N/A "get_usb_vpid: fail to get pid\n");
2N/A }
2N/A }
2N/A } else if (strncmp(tname, USB_DEV, 8) == 0) {
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "usb-vendor-id",
2N/A &vid) < 0) {
2N/A topo_mod_dprintf(mp,
2N/A "get_usb_vpid: fail to get vid: %s\n");
2N/A }
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "usb-product-id",
2N/A &pid) < 0) {
2N/A topo_mod_dprintf(mp,
2N/A "get_usb_vpid: fail to get pid\n");
2N/A }
2N/A } else if (strncmp(tname, USB_IFC, 8) == 0) {
2N/A pdn = di_parent_node(dn);
2N/A
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, pdn, "usb-vendor-id",
2N/A &vid) < 0) {
2N/A topo_mod_dprintf(mp,
2N/A "get_usb_vpid: fail to get vid: %s\n");
2N/A }
2N/A
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, pdn, "usb-product-id",
2N/A &pid) < 0) {
2N/A topo_mod_dprintf(mp,
2N/A "get_usb_vpid: fail to get pid\n");
2N/A }
2N/A }
2N/A
2N/A bzero(tmp, sizeof (tmp));
2N/A (void) snprintf(tmp, 20, "%04x-%04x\0", pid ? *pid : 0, vid ? *vid : 0);
2N/A
2N/A *part = topo_mod_strdup(mp, tmp);
2N/A
2N/A /*
2N/A * Get serial no from usb-vendor-id
2N/A */
2N/A (void) di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
2N/A "usb-serialno", &s);
2N/A *serial = topo_mod_strdup(mp, s);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic tnode_t *
2N/Ausb_tnode_create(topo_mod_t *mp, tnode_t *pn, char *name,
2N/A topo_instance_t i, void *priv)
2N/A{
2N/A tnode_t *ntn;
2N/A nvlist_t *fmri;
2N/A nvlist_t *auth;
2N/A char *serial = NULL, *part = NULL;
2N/A char *str;
2N/A
2N/A topo_mod_dprintf(mp, "usb_tnode_create entry\n");
2N/A
2N/A auth = topo_mod_auth(mp, pn);
2N/A
2N/A (void) get_usb_vpid(mp, priv, name, &serial, &part);
2N/A
2N/A topo_mod_dprintf(mp, "usb_tnode_create:serial=%s,part=%s \n",
2N/A serial?serial:"NULL", part?part:"NULL");
2N/A
2N/A fmri = topo_mod_hcfmri(mp, pn, FM_HC_SCHEME_VERSION, name,
2N/A i, NULL, auth, part, NULL, serial);
2N/A
2N/A nvlist_free(auth);
2N/A topo_mod_strfree(mp, serial);
2N/A topo_mod_strfree(mp, part);
2N/A
2N/A if (fmri != NULL) {
2N/A (void) topo_mod_nvl2str(mp, fmri, &str);
2N/A topo_mod_dprintf(mp, "usb_tnode_create new fmri: %s \n", str);
2N/A topo_mod_strfree(mp, str);
2N/A } else {
2N/A topo_mod_dprintf(mp,
2N/A "Unable to make nvlist for %s bind.%s\n", name,
2N/A topo_mod_errmsg(mp));
2N/A
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Bind the fmri to topo_node */
2N/A ntn = topo_node_bind(mp, pn, name, i, fmri);
2N/A if (ntn == NULL) {
2N/A topo_mod_dprintf(mp,
2N/A "topo_node_bind (%s%d/%s%d) failed for %s: %s\n",
2N/A topo_node_name(pn), topo_node_instance(pn), name, i,
2N/A di_node_name(priv), topo_strerror(topo_mod_errno(mp)));
2N/A nvlist_free(fmri);
2N/A
2N/A return (NULL);
2N/A }
2N/A nvlist_free(fmri);
2N/A topo_node_setspecific(ntn, priv);
2N/A
2N/A topo_mod_dprintf(mp, "usb_tnode_create end\n");
2N/A
2N/A return (ntn);
2N/A}
2N/A
2N/Astatic int
2N/Ausb_set_asru(topo_mod_t *mod, tnode_t *tn, di_node_t dn)
2N/A{
2N/A char *devpath;
2N/A nvlist_t *fmri;
2N/A int e;
2N/A
2N/A devpath = di_devfs_path(dn);
2N/A
2N/A fmri = topo_mod_devfmri(mod, FM_DEV_SCHEME_VERSION, devpath, NULL);
2N/A if (fmri == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_set_asru: fail to create dev scheme for %s: %s\n",
2N/A devpath, topo_strerror(topo_mod_errno(mod)));
2N/A di_devfs_path_free(devpath);
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A if (topo_node_asru_set(tn, fmri, 0, &e) < 0) {
2N/A nvlist_free(fmri);
2N/A topo_mod_dprintf(mod,
2N/A "usb_set_asru: fail to set ASRU for %s\n", devpath);
2N/A
2N/A di_devfs_path_free(devpath);
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A nvlist_free(fmri);
2N/A di_devfs_path_free(devpath);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/* Set FRU */
2N/Aint
2N/Ausb_set_fru(topo_mod_t *mod, tnode_t *tn, di_node_t dn)
2N/A{
2N/A int *vid;
2N/A di_node_t pdn;
2N/A tnode_t *ptn;
2N/A nvlist_t *fmri;
2N/A int ret = 0;
2N/A
2N/A topo_mod_dprintf(mod, "usb_set_fru: %s\n", topo_node_name(tn));
2N/A /*
2N/A * if the dnode is bound to an interface, we have to find
2N/A * its parent device. The parent device can be set as FRU.
2N/A */
2N/A pdn = dn;
2N/A ptn = tn;
2N/A do {
2N/A ret = di_prop_lookup_ints(DDI_DEV_T_ANY, pdn,
2N/A "usb-vendor-id", &vid);
2N/A if (ret > 0) {
2N/A /* Only device node has "vendor-id" property */
2N/A break;
2N/A }
2N/A
2N/A pdn = di_parent_node(pdn);
2N/A ptn = topo_node_parent(ptn);
2N/A } while (ret <= 0);
2N/A
2N/A if (ret < 0) {
2N/A topo_mod_dprintf(mod, "usb_set_fru: find device error\n");
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A if (topo_node_resource(ptn, &fmri, &ret) < 0) {
2N/A topo_mod_dprintf(mod, "usb_set_fru: resource error %s\n",
2N/A topo_strerror(ret));
2N/A
2N/A return (ret);
2N/A }
2N/A
2N/A if (topo_node_fru_set(tn, fmri, 0, &ret) < 0) {
2N/A topo_mod_dprintf(mod, "usb_set_fru: fru_set error %s\n",
2N/A topo_strerror(ret));
2N/A nvlist_free(fmri);
2N/A
2N/A return (ret);
2N/A }
2N/A
2N/A nvlist_free(fmri);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * some of the properties can only be retrieved from PROM,
2N/A * specifically on SPARC provided by OBP.
2N/A */
2N/Astatic int
2N/Ausb_promprop2int(topo_mod_t *mod, di_node_t n, const char *propnm, int **val)
2N/A{
2N/A di_prom_handle_t ptp = DI_PROM_HANDLE_NIL;
2N/A di_prom_prop_t pp = DI_PROM_PROP_NIL;
2N/A uchar_t *buf;
2N/A
2N/A if ((ptp = topo_mod_prominfo(mod)) == DI_PROM_HANDLE_NIL) {
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A while ((pp = di_prom_prop_next(ptp, n, pp)) != DI_PROM_PROP_NIL) {
2N/A if (strncmp(di_prom_prop_name(pp), propnm, 32) == 0) {
2N/A if (di_prom_prop_data(pp, &buf) < sizeof (int)) {
2N/A continue;
2N/A }
2N/A
2N/A /* LINTED E_BAD_PTR_CAST_ALIGN */
2N/A *val = (int *)buf;
2N/A
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * set properties of a tnode
2N/A * tn - the topology node
2N/A * dn - the corresponding device node
2N/A */
2N/Astatic int
2N/Ausb_populate_prop(topo_mod_t *mod, tnode_t *tn, di_node_t dn)
2N/A{
2N/A int *vid = NULL, *pid = NULL;
2N/A char *drivname, *nodename;
2N/A int instance;
2N/A char *path;
2N/A char str[32];
2N/A char *model = NULL;
2N/A char *vname = NULL, *pname = NULL, *serialno = NULL;
2N/A int e;
2N/A int isroot = 0;
2N/A char vpstr[256]; /* vendor/product/serial name or no */
2N/A
2N/A topo_mod_dprintf(mod, "usb_populate_prop: %s\n", di_node_name(dn));
2N/A
2N/A if (topo_pgroup_create(tn, &io_pgroup, &e) < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: fail to create io pgroup: %s\n",
2N/A topo_strerror(e));
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A if (topo_pgroup_create(tn, &usb_pgroup, &e) < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: fail to create usb pgroup: %s\n",
2N/A topo_strerror(e));
2N/A
2N/A return (-1);
2N/A }
2N/A
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "root-hub", &vid) == 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: root-hub \n");
2N/A
2N/A isroot = 1;
2N/A }
2N/A
2N/A /* if this is an interface node, do not try to get vid/pid */
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "interface", &vid) > 0) {
2N/A if (topo_prop_set_int32(tn, TOPO_PGROUP_USB, "interface",
2N/A TOPO_PROP_IMMUTABLE, topo_node_instance(tn), &e) < 0) {
2N/A topo_mod_dprintf(mod, "usb_populate_prop: fail"
2N/A " to set interface,%s\n", topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A
2N/A goto skip_vpid;
2N/A }
2N/A
2N/A if (isroot) {
2N/A /*
2N/A * these properties are not present on SPARC
2N/A * and may be optional on X86
2N/A */
2N/A
2N/A if ((di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "vendor-id",
2N/A &vid) < 0) &&
2N/A (usb_promprop2int(mod, dn, "vendor-id", &vid) < 0)) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: can't get host vid: %s\n",
2N/A strerror(errno));
2N/A
2N/A }
2N/A
2N/A if ((di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "device-id",
2N/A &pid) < 0) &&
2N/A (usb_promprop2int(mod, dn, "device-id", &pid) < 0)) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: can't get host pid\n");
2N/A
2N/A }
2N/A
2N/A if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn, "model",
2N/A &model) < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: can't get host model\n");
2N/A
2N/A }
2N/A } else {
2N/A /* set usb group properties */
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "usb-vendor-id",
2N/A &vid) < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: fail to get vid: %s\n",
2N/A strerror(errno));
2N/A
2N/A return (-1);
2N/A }
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "usb-product-id",
2N/A &pid) < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: fail to get pid\n");
2N/A
2N/A return (-1);
2N/A }
2N/A (void) di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
2N/A "usb-vendor-name", &vname);
2N/A (void) di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
2N/A "usb-product-name", &pname);
2N/A (void) di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
2N/A "usb-serialno", &serialno);
2N/A }
2N/A
2N/A if (vid) {
2N/A (void) snprintf(str, 20, "%x", topo_node_instance(tn));
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_USB, "label",
2N/A TOPO_PROP_IMMUTABLE, str, &e) < 0) {
2N/A
2N/A topo_mod_dprintf(mod, "usb_populate_prop: fail"
2N/A " to set parent-port,%s\n", topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A
2N/A (void) snprintf(str, 20, "%x", *vid);
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_USB, "vendor-id",
2N/A TOPO_PROP_IMMUTABLE, str, &e) < 0) {
2N/A topo_mod_dprintf(mod, "usb_populate_prop: fail"
2N/A " to set vid,%s\n", topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A }
2N/A
2N/A if (pid) {
2N/A (void) snprintf(str, 20, "%x", *pid);
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_USB, "product-id",
2N/A TOPO_PROP_IMMUTABLE, str, &e) < 0) {
2N/A topo_mod_dprintf(mod, "usb_populate_prop: fail to"
2N/A " set pid, %s\n", topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A }
2N/A
2N/A /* vendor name possibly present only for USB devices */
2N/A if (vname) {
2N/A (void) snprintf(vpstr, 256, "%s", vname);
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_USB, "vendor-name",
2N/A TOPO_PROP_IMMUTABLE, vpstr, &e) < 0) {
2N/A topo_mod_dprintf(mod, "usb_populate_prop: fail to set"
2N/A " vname,%s\n", topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A }
2N/A
2N/A /* product name possibly present only for USB devices */
2N/A if (pname) {
2N/A (void) snprintf(vpstr, 256, "%s", pname);
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_USB, "product-name",
2N/A TOPO_PROP_IMMUTABLE, vpstr, &e) < 0) {
2N/A topo_mod_dprintf(mod, "usb_populate_prop: fail to set"
2N/A " pname,%s\n", topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A }
2N/A
2N/A /* serial no possibly present only for USB devices */
2N/A if (serialno) {
2N/A (void) snprintf(vpstr, 256, "%s", serialno);
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_USB, "serial-number",
2N/A TOPO_PROP_IMMUTABLE, vpstr, &e) < 0) {
2N/A topo_mod_dprintf(mod, "usb_populate_prop: fail to set"
2N/A " serial, %s\n", topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A }
2N/A
2N/Askip_vpid:
2N/A /* set dev group properties */
2N/A
2N/A /* driver name */
2N/A drivname = di_driver_name(dn);
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_IO, "driver",
2N/A TOPO_PROP_IMMUTABLE, drivname, &e) < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: fail to set driver, %s\n",
2N/A topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "usb_populate_prop: bus_addr, %s\n",
2N/A di_bus_addr(dn));
2N/A
2N/A instance = di_instance(dn);
2N/A if (topo_prop_set_int32(tn, TOPO_PGROUP_IO, "instance",
2N/A TOPO_PROP_IMMUTABLE, instance, &e) < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: fail to set instance, %s\n",
2N/A topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A
2N/A if (isroot) {
2N/A if (model && (topo_prop_set_string(tn, TOPO_PGROUP_IO, "model",
2N/A TOPO_PROP_IMMUTABLE, model, &e) < 0)) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: fail to set devtype, %s\n",
2N/A topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A } else {
2N/A nodename = di_node_name(dn);
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_IO, "devtype",
2N/A TOPO_PROP_IMMUTABLE, nodename, &e) < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: fail to set devtype, %s\n",
2N/A topo_strerror(e));
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A }
2N/A
2N/A /* device path */
2N/A path = di_devfs_path(dn);
2N/A if (topo_prop_set_string(tn, TOPO_PGROUP_IO, "dev",
2N/A TOPO_PROP_IMMUTABLE, path, &e) < 0) {
2N/A topo_mod_dprintf(mod,
2N/A "usb_populate_prop: fail to set dev path %s, %s\n",
2N/A path, topo_strerror(e));
2N/A di_devfs_path_free(path);
2N/A
2N/A return (topo_mod_seterrno(mod, e));
2N/A }
2N/A di_devfs_path_free(path);
2N/A
2N/A if (!isroot) {
2N/A /* set FRU */
2N/A (void) usb_set_fru(mod, tn, dn);
2N/A } else {
2N/A tnode_t *pnode = topo_node_parent(tn);
2N/A
2N/A if (usb_set_host_fru(mod, pnode, tn) < 0) {
2N/A topo_mod_dprintf(mod, "usb_populate_prop:"
2N/A "fail to set FRU for %s", drivname);
2N/A
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A /* Set ASRU */
2N/A (void) usb_set_asru(mod, tn, dn);
2N/A
2N/A topo_mod_dprintf(mod, "usb_populate_prop: %s end\n",
2N/A di_node_name(dn));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * pnode -- parent tnode
2N/A * pdn -- the device node which we're to create tnode for
2N/A */
2N/Astatic void
2N/Ausb_declare_dev_and_if(topo_mod_t *mod, tnode_t *pnode, di_node_t pdn, int seq)
2N/A{
2N/A tnode_t *ntn;
2N/A di_node_t cdn;
2N/A int i = 0, n;
2N/A int *data;
2N/A char *nodename;
2N/A
2N/A topo_mod_dprintf(mod, "usb_declare_dev_and_if: entry, %s\n",
2N/A di_node_name(pdn));
2N/A
2N/A /*
2N/A * skip scsa2usb children, the disk(sd), since
2N/A * they're not handled by USB stack.
2N/A */
2N/A if (strncmp(di_node_name(pdn), "disk", 8) == 0) {
2N/A topo_mod_dprintf(mod, "usb_declare_dev_and_if: skip %s\n",
2N/A di_node_name(pdn));
2N/A
2N/A return;
2N/A }
2N/A
2N/A n = di_prop_lookup_ints(DDI_DEV_T_ANY, pdn, "reg", &data);
2N/A
2N/A if (n < 0) {
2N/A topo_mod_dprintf(mod, "usb_declare_dev_and_if: can't find"
2N/A " reg for %s\n", di_node_name(pdn));
2N/A
2N/A return;
2N/A }
2N/A /* see the logic in common/io/usb/usba/usba.c */
2N/A if ((n == 1) || ((n > 1) && (data[1] == 1))) {
2N/A /*
2N/A * n=1, the port number
2N/A * n>1, data[0] is the interface # and data[1] is the cfg #
2N/A */
2N/A seq = data[0];
2N/A } else {
2N/A /* Need verify if config is not 1 */
2N/A topo_mod_dprintf(mod, "usb_declare_dev_and_if: interface "
2N/A "reg %d.%d", data[0], data[1]);
2N/A
2N/A seq = data[0];
2N/A }
2N/A
2N/A /* if this is an interface node, do not try to get vid/pid */
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, pdn, "interface", &data) > 0) {
2N/A nodename = USB_IFC;
2N/A } else if (di_prop_lookup_ints(DDI_DEV_T_ANY, pdn, "usb-port-count",
2N/A &data) > 0) {
2N/A nodename = USB_HUB;
2N/A } else {
2N/A nodename = USB_DEV;
2N/A }
2N/A
2N/A if ((ntn = usb_tnode_create(mod, pnode,
2N/A nodename, seq, pdn)) == NULL) {
2N/A topo_mod_dprintf(mod, "dev_declare: can't create node");
2N/A
2N/A return;
2N/A }
2N/A
2N/A (void) usb_populate_prop(mod, ntn, pdn);
2N/A
2N/A /*
2N/A * if we don't have child, return. Otherwise, process each child.
2N/A */
2N/A cdn = di_child_node(pdn);
2N/A if (cdn == DI_NODE_NIL) {
2N/A topo_mod_dprintf(mod, "%s don't have child devices\n",
2N/A di_node_name(pdn));
2N/A
2N/A return;
2N/A }
2N/A
2N/A if (strncmp(nodename, USB_HUB, 8) == 0) {
2N/A if (topo_node_range_create(mod, ntn, USB_HUB, 0,
2N/A MAX_USB_DEVS) < 0) {
2N/A topo_mod_dprintf(mod, "child_range_add for"
2N/A "USB failed: %s\n",
2N/A topo_strerror(topo_mod_errno(mod)));
2N/A
2N/A return;
2N/A }
2N/A
2N/A if (topo_node_range_create(mod, ntn, USB_DEV, 0,
2N/A MAX_USB_DEVS) < 0) {
2N/A topo_mod_dprintf(mod, "child_range_add for"
2N/A "USB failed: %s\n",
2N/A topo_strerror(topo_mod_errno(mod)));
2N/A
2N/A return;
2N/A }
2N/A } else if (strncmp(nodename, USB_DEV, 8) == 0) {
2N/A if (topo_node_range_create(mod, ntn, USB_IFC, 0,
2N/A MAX_USB_INTERFACE) < 0) {
2N/A topo_mod_dprintf(mod, "child_range_add for"
2N/A "USB failed: %s\n",
2N/A topo_strerror(topo_mod_errno(mod)));
2N/A
2N/A return;
2N/A }
2N/A } else {
2N/A /*
2N/A * there may be separate drivers/nodes for individual
2N/A * interface of this IA
2N/A */
2N/A if (strncmp(di_driver_name(pdn), "usb_ia", 8) == 0) {
2N/A if (topo_node_range_create(mod, ntn, USB_IFC, 0,
2N/A MAX_USB_INTERFACE) < 0) {
2N/A topo_mod_dprintf(mod, "child_range_add for"
2N/A "USB failed: %s\n",
2N/A topo_strerror(topo_mod_errno(mod)));
2N/A
2N/A return;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* instantiate each child sequencely */
2N/A i = 0;
2N/A cdn = di_child_node(pdn);
2N/A if (cdn != DI_NODE_NIL) {
2N/A topo_mod_dprintf(mod, "usb_declare_dev_and_if:"
2N/A " instantiate child %s\n", di_node_name(cdn));
2N/A
2N/A if (usb_children_instantiate(mod, ntn, cdn, i++) != 0) {
2N/A topo_mod_dprintf(mod, "dev_declare: can't instantiate"
2N/A " further");
2N/A }
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "usb_declare_dev_and_if: %s end\n",
2N/A di_node_name(pdn));
2N/A}
2N/A
2N/A/*
2N/A * instantiate every child
2N/A *
2N/A * pnode - parent tnode
2N/A */
2N/Astatic int
2N/Ausb_children_instantiate(topo_mod_t *mod, tnode_t *pnode,
2N/A di_node_t pn, int depth)
2N/A{
2N/A di_node_t cdn, tdn;
2N/A
2N/A topo_mod_dprintf(mod, "usb_children_instantiate: entry, process %s\n",
2N/A di_node_name(pn));
2N/A
2N/A if (di_prop_lookup_strings(DDI_DEV_T_ANY, pn, "root-hub", NULL) == 0) {
2N/A /* do nothing */
2N/A topo_mod_dprintf(mod,
2N/A "usb_children_instantiate: root hub %s\n",
2N/A di_node_name(pn));
2N/A } else {
2N/A topo_mod_dprintf(mod,
2N/A "usb_children_instantiate: declare %s\n",
2N/A di_node_name(pn));
2N/A }
2N/A
2N/A cdn = pn;
2N/A while (cdn != DI_NODE_NIL) {
2N/A topo_mod_dprintf(mod, "usb_children_instantiate: child %s\n",
2N/A di_node_name(cdn));
2N/A
2N/A /* depth first */
2N/A usb_declare_dev_and_if(mod, pnode, cdn, depth++);
2N/A
2N/A tdn = cdn;
2N/A /* breadth next */
2N/A cdn = di_sibling_node(cdn);
2N/A
2N/A topo_mod_dprintf(mod, "usb_children_instantiate: child %s, "
2N/A "sibling %s\n", di_node_name(tdn),
2N/A (cdn == DI_NODE_NIL)?"nosibling":di_node_name(cdn));
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "usb_children_instantiate: %s end \n",
2N/A di_node_name(pn));
2N/A
2N/A return (0);
2N/A}