/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <strings.h>
#include <devid.h>
#include <pthread.h>
#include <inttypes.h>
#include <fm/topo_mod.h>
#include <fm/topo_list.h>
#include <fm/topo_mod.h>
#include <libdevinfo.h>
/* Global Definition */
typedef struct usb_enum_dev {
int ued_bus;
int ued_dev;
int ued_fun;
typedef struct usb_enum_data {
typedef struct usb_cbdata {
} usb_cbdata_t;
/* Static Definition */
static int usb_is_from_pci = 0;
/* Function Entry */
static tnode_t *
topo_instance_t i, void *priv);
static int
static int
static int
static int
static int
static void
static int
/* Topo pluggin enum entry */
topo_instance_t, topo_instance_t, void *, void *);
/* Topo pluggin enum ops struct */
{ usb_enum, usb_release};
static int
{
continue;
/* LINTED E_BAD_PTR_CAST_ALIGN */
return (0);
}
}
return (-1);
}
/*
* Get uint property from di_node_t
*/
int
{
return (-1);
return (0);
}
/*
* Add a host controller to the global list.
*/
static int
{
di_instance(node));
return (-1);
}
if (controller == NULL) {
return (-1);
}
do {
/* search for the most recent PCI bridge upward */
break;
}
/* append the host controller to the global list */
return (0);
}
/*
* Use BDF information to find the corresponding HC in host controller list.
*/
static int
{
di_instance(node));
di_instance(node));
return (-1);
}
/*
* Traverse the ue_devs list. ue_devs is the header
* node, search starts from the next
*/
*host = controller;
break;
}
controller = NULL;
}
return (0);
}
/*
* Callback entry for di_walk_node
* If the node is a USB host controllers, put it onto
* the host controller list.
*/
static int
{
char *driver_name;
if (driver_name == NULL) {
return (DI_WALK_CONTINUE);
}
"find %s device", driver_name);
"fail to add %s%d node", driver_name,
di_instance(node));
return (DI_WALK_CONTINUE);
}
}
return (DI_WALK_CONTINUE);
}
/*
* To collect all the host controllers in the system
*/
int
{
/* Get the devinfo tree */
"topo_mod_devinfo() failed");
return (-1);
}
/* walk the devinfo snapshot looking for host controller nodes */
return (0);
}
/*
* Use the parent's FRU as the FRU of host controllers enumerated from PCI
*/
int
{
int err;
" motherboard");
return (-1);
} else {
return (0);
}
}
/*
* When USB is enumerated from a Map file or x86pi, USB enumerator
* will create 'usb-bus' topo nodes for all the host controllers that
* have no PCI('pcifn' or 'pciexfn') nodes.
*/
int
{
int i = 0;
int err = 0;
"can't find the parent FMRI");
} else {
}
/* Enumerated from the host controller we gathered */
/* If it was enumerated from PCI before, skip to next */
"usb_process_host_controllers: %s enumed before"
continue;
}
"no parent");
/*
* we will not use device instance number here, because
* different host controller may have the same instance #.
*/
return (-1);
}
/* Set it as enumerated */
if (cdn == DI_NODE_NIL) {
continue;
}
/* create node range for devices under the root hub */
MAX_USB_DEVS) < 0) {
" can't create range");
return (-1);
}
/* Also create node range for hubs under root hub */
MAX_USB_DEVS) < 0) {
" can't create range");
return (-1);
}
/* walk and instantiate each child node of the root hub */
" fail to instantiate children");
return (-1);
}
}
return (0);
}
/*
* Create necessary information for a host controller which is enumerated
* by PCI enumerator. Also create topology nodes for all the devices connected
* to this controller.
*/
int
{
int err = 0;
"can't find the parent FMRI");
} else {
}
/* Parent topo_node need provide di_node from its specific data */
"Parent %s node missing private data.\n"
"Unable to proceed with %s enumeration.",
return (-1);
}
/*
* The 'usb-bus' node was ever created to represent 'roothub'. However,
* cause mess for diagnosis engine. To avoid such confusion, the
* 'usb-bus' node is no longer created for the devices which are
* enumerated from PCI parent.
*/
/*
* To avoid PCI, map file or x86pi enumerating the same host controller,
* we first check to see if other enumerator has processed this HC. If
* not, we'll process it and mark it as enumerated.
*/
} else {
"fail to search host node for %s",
}
/*
* Check if HC has children device. If so, enumerate the children
* to generate the topo nodes.
*/
if (cdn == DI_NODE_NIL) {
return (0);
}
/* create node range for devices under the root hub */
MAX_USB_DEVS) < 0) {
" can't create range");
return (-1);
}
/* Also create node range for hubs under root hub */
MAX_USB_DEVS) < 0) {
" can't create range");
return (-1);
}
/* walk and instantiate each child node of the root hub */
!= 0) {
" fail to instantiate children");
return (-1);
}
return (0);
}
/*
* USB topo enum entry
*/
/* ARGSUSED */
static int
{
char *rname;
/*
* Check to make sure we're being invoked sensibly, and that we're not
* being invoked as part of a post-processing step.
*/
return (0);
}
"data not set");
return (-1);
}
/*
* Normally, USB enumeration starts from parent PCI enumerator or from
* Maps. But, there are cases where the system has both on-chip(built
* in southbridge) USB host controllers and non-on-chip pci USB hosts.
* In this case, some of the host controllers will be enumerated by
* the parent PCI nodes, while others need to be enumerated by XML(from
*/
" PCI has %s enumerated", usb_is_from_pci ?
"" : " NOT");
/* Enum from hostbridge, all HCs enumerated together */
/* Enum from PCI, here only one HC enumerated each time */
} else {
"invalid parent %s", rname);
return (-1);
}
return (0);
}
static void
{
/*
* Traverse the ue_devs list. ue_devs is the header
* node, search starts from the next
*/
}
}
static void
{
topo_node_name(tn));
}
/* ARGSUSED */
static int
{
int err = 0;
if (err != 0) {
}
return (err);
}
int
{
}
/* ARGSUSED */
int
{
/*
* Turn on module debugging output
*/
"initializing %s enumerator", USBTOPO);
"%s registration failed: %s",
return (-1);
}
"specific private data", USBTOPO);
NULL) {
return (-1);
}
/*
* Gather all host controllers in system
*/
return (-1);
}
"controller first time gathered!", USBTOPO);
}
"%s enumerator initialized", USBTOPO);
return (0);
}
void
{
"free private data for mod", USBTOPO);
/* Avoid duplicate mem free */
}
"%s enumerator uninitialized\n", USBTOPO);
}
static int
char **part)
{
char *s = NULL;
/*
* Get Part value in HC scheme
* Part is composed of Pid-Vid value pair
*/
&vid) == 0) {
"get_usb_vpid: root-hub");
"vendor-id", &vid) < 0) &&
"get_usb_vpid: can't get host vid: %s\n");
}
&pid) < 0) &&
"get_usb_vpid: can't get host pid\n");
}
} else {
"usb-vendor-id", &vid) < 0) {
"get_usb_vpid: fail to get vid: %s\n");
}
"usb-product-id", &pid) < 0) {
"get_usb_vpid: fail to get pid\n");
}
}
&vid) < 0) {
"get_usb_vpid: fail to get vid: %s\n");
}
&pid) < 0) {
"get_usb_vpid: fail to get pid\n");
}
&vid) < 0) {
"get_usb_vpid: fail to get vid: %s\n");
}
&pid) < 0) {
"get_usb_vpid: fail to get pid\n");
}
}
/*
* Get serial no from usb-vendor-id
*/
"usb-serialno", &s);
return (0);
}
static tnode_t *
topo_instance_t i, void *priv)
{
char *str;
} else {
"Unable to make nvlist for %s bind.%s\n", name,
return (NULL);
}
/* Bind the fmri to topo_node */
"topo_node_bind (%s%d/%s%d) failed for %s: %s\n",
return (NULL);
}
return (ntn);
}
static int
{
char *devpath;
int e;
"usb_set_asru: fail to create dev scheme for %s: %s\n",
return (-1);
}
"usb_set_asru: fail to set ASRU for %s\n", devpath);
return (topo_mod_seterrno(mod, e));
}
return (0);
}
/* Set FRU */
int
{
int *vid;
int ret = 0;
/*
* if the dnode is bound to an interface, we have to find
* its parent device. The parent device can be set as FRU.
*/
do {
"usb-vendor-id", &vid);
if (ret > 0) {
/* Only device node has "vendor-id" property */
break;
}
} while (ret <= 0);
if (ret < 0) {
return (-1);
}
topo_strerror(ret));
return (ret);
}
topo_strerror(ret));
return (ret);
}
return (0);
}
/*
* some of the properties can only be retrieved from PROM,
* specifically on SPARC provided by OBP.
*/
static int
{
return (-1);
}
continue;
}
/* LINTED E_BAD_PTR_CAST_ALIGN */
return (0);
}
}
return (-1);
}
/*
* set properties of a tnode
* tn - the topology node
* dn - the corresponding device node
*/
static int
{
int instance;
char *path;
int e;
int isroot = 0;
"usb_populate_prop: fail to create io pgroup: %s\n",
topo_strerror(e));
return (-1);
}
"usb_populate_prop: fail to create usb pgroup: %s\n",
topo_strerror(e));
return (-1);
}
"usb_populate_prop: root-hub \n");
isroot = 1;
}
" to set interface,%s\n", topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
goto skip_vpid;
}
if (isroot) {
/*
* these properties are not present on SPARC
* and may be optional on X86
*/
&vid) < 0) &&
"usb_populate_prop: can't get host vid: %s\n",
}
&pid) < 0) &&
"usb_populate_prop: can't get host pid\n");
}
&model) < 0) {
"usb_populate_prop: can't get host model\n");
}
} else {
/* set usb group properties */
&vid) < 0) {
"usb_populate_prop: fail to get vid: %s\n",
return (-1);
}
&pid) < 0) {
"usb_populate_prop: fail to get pid\n");
return (-1);
}
"usb-vendor-name", &vname);
"usb-product-name", &pname);
"usb-serialno", &serialno);
}
if (vid) {
TOPO_PROP_IMMUTABLE, str, &e) < 0) {
" to set parent-port,%s\n", topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
TOPO_PROP_IMMUTABLE, str, &e) < 0) {
" to set vid,%s\n", topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
}
if (pid) {
TOPO_PROP_IMMUTABLE, str, &e) < 0) {
" set pid, %s\n", topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
}
/* vendor name possibly present only for USB devices */
if (vname) {
TOPO_PROP_IMMUTABLE, vpstr, &e) < 0) {
" vname,%s\n", topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
}
/* product name possibly present only for USB devices */
if (pname) {
TOPO_PROP_IMMUTABLE, vpstr, &e) < 0) {
" pname,%s\n", topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
}
/* serial no possibly present only for USB devices */
if (serialno) {
TOPO_PROP_IMMUTABLE, vpstr, &e) < 0) {
" serial, %s\n", topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
}
/* set dev group properties */
/* driver name */
TOPO_PROP_IMMUTABLE, drivname, &e) < 0) {
"usb_populate_prop: fail to set driver, %s\n",
topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
di_bus_addr(dn));
TOPO_PROP_IMMUTABLE, instance, &e) < 0) {
"usb_populate_prop: fail to set instance, %s\n",
topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
if (isroot) {
TOPO_PROP_IMMUTABLE, model, &e) < 0)) {
"usb_populate_prop: fail to set devtype, %s\n",
topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
} else {
TOPO_PROP_IMMUTABLE, nodename, &e) < 0) {
"usb_populate_prop: fail to set devtype, %s\n",
topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
}
/* device path */
TOPO_PROP_IMMUTABLE, path, &e) < 0) {
"usb_populate_prop: fail to set dev path %s, %s\n",
path, topo_strerror(e));
return (topo_mod_seterrno(mod, e));
}
if (!isroot) {
/* set FRU */
} else {
"fail to set FRU for %s", drivname);
return (-1);
}
}
/* Set ASRU */
di_node_name(dn));
return (0);
}
/*
* pnode -- parent tnode
* pdn -- the device node which we're to create tnode for
*/
static void
{
int i = 0, n;
int *data;
char *nodename;
di_node_name(pdn));
/*
* skip scsa2usb children, the disk(sd), since
* they're not handled by USB stack.
*/
di_node_name(pdn));
return;
}
if (n < 0) {
return;
}
/*
* n=1, the port number
* n>1, data[0] is the interface # and data[1] is the cfg #
*/
} else {
/* Need verify if config is not 1 */
}
&data) > 0) {
} else {
}
return;
}
/*
* if we don't have child, return. Otherwise, process each child.
*/
if (cdn == DI_NODE_NIL) {
di_node_name(pdn));
return;
}
MAX_USB_DEVS) < 0) {
"USB failed: %s\n",
return;
}
MAX_USB_DEVS) < 0) {
"USB failed: %s\n",
return;
}
MAX_USB_INTERFACE) < 0) {
"USB failed: %s\n",
return;
}
} else {
/*
* interface of this IA
*/
MAX_USB_INTERFACE) < 0) {
"USB failed: %s\n",
return;
}
}
}
/* instantiate each child sequencely */
i = 0;
if (cdn != DI_NODE_NIL) {
" further");
}
}
di_node_name(pdn));
}
/*
* instantiate every child
*
* pnode - parent tnode
*/
static int
{
di_node_name(pn));
/* do nothing */
"usb_children_instantiate: root hub %s\n",
di_node_name(pn));
} else {
"usb_children_instantiate: declare %s\n",
di_node_name(pn));
}
while (cdn != DI_NODE_NIL) {
di_node_name(cdn));
/* depth first */
/* breadth next */
}
di_node_name(pn));
return (0);
}