cfga_list.c revision 4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include "cfga_scsi.h"
/* Structure for walking the tree */
typedef struct {
apid_t *apidp;
char *hba_logp;
ldata_list_t *listp;
scfga_cmd_t cmd;
cfga_stat_t chld_config;
cfga_stat_t hba_rstate;
scfga_ret_t ret;
int l_errno;
} scfga_list_t;
typedef struct {
uint_t itype;
const char *ntype;
const char *name;
const char *pathname;
} scfga_devtype_t;
/* The TYPE field is parseable and should not contain spaces */
#define SCFGA_BUS_TYPE "scsi-bus"
/* Function prototypes */
static scfga_ret_t postprocess_list_data(const ldata_list_t *listp,
scfga_cmd_t cmd, cfga_stat_t chld_config, int *np);
static int stat_dev(di_node_t node, void *arg);
static scfga_ret_t do_stat_bus(scfga_list_t *lap, int limited_bus_stat);
static int get_bus_state(di_node_t node, void *arg);
static scfga_ret_t do_stat_dev(const di_node_t node, const char *nodepath,
scfga_list_t *lap, int limited_dev_stat);
static cfga_stat_t bus_devinfo_to_recep_state(uint_t bus_di_state);
static cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state);
static char *get_device_type(di_node_t, dyncomp_t);
static void get_hw_info(di_node_t node, cfga_list_data_t *clp, dyncomp_t type);
static scfga_ret_t create_pathinfo_ldata(di_path_t pi_node, scfga_list_t *lap,
int *l_errnop);
static scfga_devtype_t device_list[] = {
{ DTYPE_DIRECT, DDI_NT_BLOCK_CHAN, "disk", "disk-path"},
{ DTYPE_DIRECT, DDI_NT_BLOCK, "disk", "disk-path"},
{ DTYPE_DIRECT, DDI_NT_BLOCK_WWN, "disk", "disk-path"},
{ DTYPE_DIRECT, DDI_NT_BLOCK_FABRIC, "disk", "disk-path"},
{ DTYPE_DIRECT, DDI_NT_BLOCK_SAS, "disk", "disk-path"},
{ DTYPE_SEQUENTIAL, DDI_NT_TAPE, "tape", "tape-path"},
{ DTYPE_PRINTER, NULL, "printer", "printer-path"},
{ DTYPE_PROCESSOR, NULL, "processor", "PRCS-path"},
{ DTYPE_WORM, NULL, "WORM", "WORM-path"},
{ DTYPE_RODIRECT, DDI_NT_CD_CHAN, "CD-ROM", "CD-ROM-path"},
{ DTYPE_RODIRECT, DDI_NT_CD, "CD-ROM", "CD-ROM-path"},
{ DTYPE_SCANNER, NULL, "scanner", "scanner-path"},
{ DTYPE_OPTICAL, NULL, "optical", "optical-path"},
{ DTYPE_CHANGER, NULL, "med-changer", "MEDCHGR-path"},
{ DTYPE_COMM, NULL, "comm-device", "COMDEV-path"},
{ DTYPE_ARRAY_CTRL, NULL, "array-ctrl", "ARRCTRL-path"},
{ DTYPE_ESI, NULL, "ESI", "ESI-path"}
};
#define N_DEVICE_TYPES (sizeof (device_list) / sizeof (device_list[0]))
scfga_ret_t
do_list(
apid_t *apidp,
scfga_cmd_t cmd,
ldata_list_t **llpp,
int *nelemp,
char **errstring)
{
int n = -1, l_errno = 0, limited_bus_stat;
walkarg_t u;
scfga_list_t larg = {NULL};
scfga_ret_t ret;
int init_flag;
assert(apidp->hba_phys != NULL && apidp->path != NULL);
if (*llpp != NULL || *nelemp != 0) {
return (SCFGA_ERR);
}
/* Create the HBA logid (also base component of logical ap_id) */
ret = make_hba_logid(apidp->hba_phys, &larg.hba_logp, &l_errno);
if (ret != SCFGA_OK) {
cfga_err(errstring, l_errno, ERR_LIST, 0);
return (SCFGA_ERR);
}
assert(larg.hba_logp != NULL);
larg.cmd = cmd;
larg.apidp = apidp;
larg.hba_rstate = CFGA_STAT_NONE;
/*
* For all list commands, the bus and 1 or more devices
* needs to be stat'ed
*/
/*
* By default we use DINFOCACHE to get a "full" snapshot
* This much faster than DINFOFORCE which actually
* attaches devices. DINFOFORCE used only if caller
* explicitly requests it via a private option.
*/
init_flag = (apidp->flags & FLAG_USE_DIFORCE) ? DINFOFORCE : DINFOCACHE;
limited_bus_stat = 0;
switch (larg.cmd) {
case SCFGA_STAT_DEV:
limited_bus_stat = 1; /* We need only bus state */
/*FALLTHRU*/
case SCFGA_STAT_ALL:
break;
case SCFGA_STAT_BUS:
/* limited_bus_stat = 0 and no DINFOCACHE/DINFOFORCE */
init_flag = 0;
break;
default:
cfga_err(errstring, EINVAL, ERR_LIST, 0);
goto out;
}
/*
* DINFOCACHE implies DINFOCPYALL. DINFOCPYALL shouldn't
* be ORed with DINFOCACHE, else libdevinfo will return
* error
*/
if (init_flag != DINFOCACHE)
init_flag |= DINFOCPYALL;
if ((ret = do_stat_bus(&larg, limited_bus_stat)) != SCFGA_OK) {
cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
goto out;
}
#ifdef DEBUG
if (limited_bus_stat) {
assert(larg.listp == NULL);
} else {
assert(larg.listp != NULL);
}
#endif
/* Assume that the bus has no configured children */
larg.chld_config = CFGA_STAT_UNCONFIGURED;
/*
* If stat'ing a specific device, we don't know if it exists yet.
* If stat'ing a bus or a bus and child devices, we have at least the
* bus stat data at this point.
*/
if (larg.cmd == SCFGA_STAT_DEV) {
larg.ret = SCFGA_APID_NOEXIST;
} else {
larg.ret = SCFGA_OK;
}
/* we need to stat at least 1 device for all commands */
if (apidp->dyntype == PATH_APID) {
/*
* When cmd is SCFGA_STAT_DEV and the ap id is pathinfo
* related.
*/
ret = walk_tree(apidp->hba_phys, &larg, init_flag, NULL,
SCFGA_WALK_PATH, &larg.l_errno);
} else {
/* we need to stat at least 1 device for all commands */
u.node_args.flags = DI_WALK_CLDFIRST;
u.node_args.fcn = stat_dev;
/*
* Subtree is ALWAYS rooted at the HBA (not at the device) as
* otherwise deadlock may occur if bus is disconnected.
*/
ret = walk_tree(apidp->hba_phys, &larg, init_flag, &u,
SCFGA_WALK_NODE, &larg.l_errno);
/*
* Check path info on the following conditions.
*
* - chld_config is still set to CFGA_STAT_UNCONFIGURED for
* SCFGA_STAT_BUS cmd after walking any child node.
* - walking node succeeded for SCFGA_STAT_ALL cmd(Continue on
* stating path info node).
* - apid is pathinfo associated and larg.ret is still set to
* SCFGA_APID_NOEXIST for SCFGA_STAT_DEV cmd.
*/
if (((cmd == SCFGA_STAT_BUS) &&
(larg.chld_config == CFGA_STAT_UNCONFIGURED)) ||
((cmd == SCFGA_STAT_ALL) && (ret == SCFGA_OK))) {
ret = walk_tree(apidp->hba_phys, &larg, init_flag, NULL,
SCFGA_WALK_PATH, &larg.l_errno);
}
}
if (ret != SCFGA_OK || (ret = larg.ret) != SCFGA_OK) {
if (ret != SCFGA_APID_NOEXIST) {
cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
}
goto out;
}
assert(larg.listp != NULL);
n = 0;
ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n);
if (ret != SCFGA_OK) {
cfga_err(errstring, 0, ERR_LIST, 0);
ret = SCFGA_LIB_ERR;
goto out;
}
*nelemp = n;
*llpp = larg.listp;
ret = SCFGA_OK;
/* FALLTHROUGH */
out:
if (ret != SCFGA_OK) list_free(&larg.listp);
S_FREE(larg.hba_logp);
return (ret);
}
static scfga_ret_t
postprocess_list_data(
const ldata_list_t *listp,
scfga_cmd_t cmd,
cfga_stat_t chld_config,
int *np)
{
ldata_list_t *tmplp = NULL;
cfga_list_data_t *hba_ldatap = NULL;
int i;
*np = 0;
if (listp == NULL) {
return (SCFGA_ERR);
}
hba_ldatap = NULL;
tmplp = (ldata_list_t *)listp;
for (i = 0; tmplp != NULL; tmplp = tmplp->next) {
i++;
if (GET_DYN(tmplp->ldata.ap_phys_id) == NULL) {
/* A bus stat data */
assert(GET_DYN(tmplp->ldata.ap_log_id) == NULL);
hba_ldatap = &tmplp->ldata;
#ifdef DEBUG
} else {
assert(GET_DYN(tmplp->ldata.ap_log_id) != NULL);
#endif
}
}
switch (cmd) {
case SCFGA_STAT_DEV:
if (i != 1 || hba_ldatap != NULL) {
return (SCFGA_LIB_ERR);
}
break;
case SCFGA_STAT_BUS:
if (i != 1 || hba_ldatap == NULL) {
return (SCFGA_LIB_ERR);
}
break;
case SCFGA_STAT_ALL:
if (i < 1 || hba_ldatap == NULL) {
return (SCFGA_LIB_ERR);
}
break;
default:
return (SCFGA_LIB_ERR);
}
*np = i;
/* Fill in the occupant (child) state. */
if (hba_ldatap != NULL) {
hba_ldatap->ap_o_state = chld_config;
}
return (SCFGA_OK);
}
static int
stat_dev(di_node_t node, void *arg)
{
scfga_list_t *lap = NULL;
char *devfsp = NULL, *nodepath = NULL;
size_t len = 0;
int limited_dev_stat = 0, match_minor, rv;
scfga_ret_t ret;
lap = (scfga_list_t *)arg;
/* Skip stub nodes */
if (IS_STUB_NODE(node)) {
return (DI_WALK_CONTINUE);
}
/* Skip partial nodes */
if (!known_state(node)) {
return (DI_WALK_CONTINUE);
}
devfsp = di_devfs_path(node);
if (devfsp == NULL) {
rv = DI_WALK_CONTINUE;
goto out;
}
len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;
nodepath = calloc(1, len);
if (nodepath == NULL) {
lap->l_errno = errno;
lap->ret = SCFGA_LIB_ERR;
rv = DI_WALK_TERMINATE;
goto out;
}
(void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);
/* Skip node if it is HBA */
match_minor = 0;
if (!dev_cmp(lap->apidp->hba_phys, nodepath, match_minor)) {
rv = DI_WALK_CONTINUE;
goto out;
}
/* If stat'ing a specific device, is this that device */
if (lap->cmd == SCFGA_STAT_DEV) {
assert(lap->apidp->path != NULL);
if (dev_cmp(lap->apidp->path, nodepath, match_minor)) {
rv = DI_WALK_CONTINUE;
goto out;
}
}
/*
* If stat'ing a bus only, we look at device nodes only to get
* bus configuration status. So a limited stat will suffice.
*/
if (lap->cmd == SCFGA_STAT_BUS) {
limited_dev_stat = 1;
} else {
limited_dev_stat = 0;
}
/*
* Ignore errors if stat'ing a bus or listing all
*/
ret = do_stat_dev(node, nodepath, lap, limited_dev_stat);
if (ret != SCFGA_OK) {
if (lap->cmd == SCFGA_STAT_DEV) {
lap->ret = ret;
rv = DI_WALK_TERMINATE;
} else {
rv = DI_WALK_CONTINUE;
}
goto out;
}
/* Are we done ? */
rv = DI_WALK_CONTINUE;
if (lap->cmd == SCFGA_STAT_BUS &&
lap->chld_config == CFGA_STAT_CONFIGURED) {
rv = DI_WALK_TERMINATE;
} else if (lap->cmd == SCFGA_STAT_DEV) {
/*
* If stat'ing a specific device, we are done at this point.
*/
lap->ret = SCFGA_OK;
rv = DI_WALK_TERMINATE;
}
/*FALLTHRU*/
out:
S_FREE(nodepath);
if (devfsp != NULL) di_devfs_path_free(devfsp);
return (rv);
}
/*
* Create list date entry and add to ldata list.
*/
static scfga_ret_t
create_pathinfo_ldata(di_path_t pi_node, scfga_list_t *lap, int *l_errnop)
{
ldata_list_t *listp = NULL;
cfga_list_data_t *clp;
di_node_t client_node = DI_NODE_NIL;
di_minor_t minor;
scfga_ret_t ret;
di_path_state_t pi_state;
char *dyncomp = NULL, *client_path = NULL;
char pathbuf[MAXPATHLEN], *client_devlink = NULL;
int match_minor;
listp = calloc(1, sizeof (ldata_list_t));
if (listp == NULL) {
lap->l_errno = errno;
return (SCFGA_LIB_ERR);
}
clp = &listp->ldata;
ret = make_path_dyncomp(pi_node, &dyncomp, &lap->l_errno);
if (ret != SCFGA_OK) {
S_FREE(listp);
return (ret);
}
client_node = di_path_client_node(pi_node);
if (client_node == DI_NODE_NIL) {
*l_errnop = errno;
S_FREE(dyncomp);
return (SCFGA_LIB_ERR);
}
/* Create logical and physical ap_id */
(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
lap->hba_logp, DYN_SEP, dyncomp);
(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
lap->apidp->hba_phys, DYN_SEP, dyncomp);
S_FREE(dyncomp);
/* ap class filled in by libcfgadm */
clp->ap_class[0] = '\0';
clp->ap_r_state = lap->hba_rstate;
/* path info exist so set to configured. */
clp->ap_o_state = CFGA_STAT_CONFIGURED;
/* now fill up ap_info field with client dev link and instance #. */
client_path = di_devfs_path(client_node);
if (client_path) {
/* get first minor node. */
minor = di_minor_next(client_node, DI_MINOR_NIL);
if (minor == DI_MINOR_NIL) {
match_minor = 0;
(void) snprintf(pathbuf, MAXPATHLEN, "%s:%s",
DEVICES_DIR, client_path);
} else {
match_minor = 1;
(void) snprintf(pathbuf, MAXPATHLEN, "%s%s:%s",
DEVICES_DIR, client_path, di_minor_name(minor));
}
(void) physpath_to_devlink(pathbuf, &client_devlink, l_errnop,
match_minor);
di_devfs_path_free(client_path);
}
if (client_devlink) {
(void) snprintf(clp->ap_info, CFGA_INFO_LEN,
"%s: %s", "Client Device", client_devlink);
S_FREE(client_devlink);
}
get_hw_info(client_node, clp, PATH_APID);
if ((pi_state = di_path_state(pi_node)) == DI_PATH_STATE_OFFLINE) {
clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
}
if (pi_state == DI_PATH_STATE_FAULT) {
clp->ap_cond = CFGA_COND_FAILED;
} else {
clp->ap_cond = CFGA_COND_UNKNOWN;
}
/* no way to determine state change */
clp->ap_busy = 0;
clp->ap_status_time = (time_t)-1;
/* Link it in */
listp->next = lap->listp;
lap->listp = listp;
return (SCFGA_OK);
}
/*
* Routine to stat pathinfo nodes.
*
* No pathinfo founds returns a success.
* When cmd is SCFGA_STAT_DEV, finds a matching pathinfo node and
* and create ldata if found.
* When cmd is SCFGA_STAT_ALL, create ldata for each pathinfo node.
* When cmd is SCFGA_STAT_BUS, checks if any pathinfo exist.
*
* Return:
* 0 for success
* -1 for failure.
*/
int
stat_path_info(
di_node_t root,
void *arg,
int *l_errnop)
{
scfga_list_t *lap = (scfga_list_t *)arg;
di_path_t pi_node;
if (root == DI_NODE_NIL) {
return (-1);
}
/*
* when there is no path_info node return SCFGA_OK.
*/
if (di_path_next_client(root, DI_PATH_NIL) == DI_PATH_NIL) {
return (0);
}
if (lap->cmd == SCFGA_STAT_BUS) {
lap->chld_config = CFGA_STAT_CONFIGURED;
return (0);
} else if (lap->cmd == SCFGA_STAT_DEV) {
assert(lap->apidp->dyntype == PATH_APID);
for (pi_node = di_path_next_client(root, DI_PATH_NIL); pi_node;
pi_node = di_path_next_client(root, pi_node)) {
/*
* NOTE: apidt_create() validated pathinfo apid so
* the apid should have a valid format.
*/
/* check the length first. */
if (strlen(di_path_bus_addr(pi_node)) !=
strlen(lap->apidp->dyncomp)) {
continue;
}
/* check for full match. */
if (strcmp(di_path_bus_addr(pi_node),
lap->apidp->dyncomp)) {
continue;
}
/* found match, record information */
if (create_pathinfo_ldata(pi_node, lap,
l_errnop) == SCFGA_OK) {
lap->ret = SCFGA_OK;
return (0);
} else {
return (-1);
}
}
} else { /* cmd = STAT_ALL */
/* set child config to configured */
lap->chld_config = CFGA_STAT_CONFIGURED;
for (pi_node = di_path_next_client(root, DI_PATH_NIL); pi_node;
pi_node = di_path_next_client(root, pi_node)) {
/* continue on even if there is an error on one path. */
(void) create_pathinfo_ldata(pi_node, lap, l_errnop);
}
}
lap->ret = SCFGA_OK;
return (0);
}
struct bus_state {
int b_state;
int b_retired;
char iconnect_type[16];
};
static scfga_ret_t
do_stat_bus(scfga_list_t *lap, int limited_bus_stat)
{
cfga_list_data_t *clp = NULL;
ldata_list_t *listp = NULL;
int l_errno = 0;
struct bus_state bstate = {0};
walkarg_t u;
scfga_ret_t ret;
int i;
char itypelower[MAXNAMELEN];
assert(lap->hba_logp != NULL);
/* Get bus state */
u.node_args.flags = 0;
u.node_args.fcn = get_bus_state;
ret = walk_tree(lap->apidp->hba_phys, &bstate, DINFOPROP, &u,
SCFGA_WALK_NODE, &l_errno);
if (ret == SCFGA_OK) {
lap->hba_rstate = bus_devinfo_to_recep_state(bstate.b_state);
} else {
lap->hba_rstate = CFGA_STAT_NONE;
}
if (limited_bus_stat) {
/* We only want to know bus(receptacle) connect status */
return (SCFGA_OK);
}
listp = calloc(1, sizeof (ldata_list_t));
if (listp == NULL) {
lap->l_errno = errno;
return (SCFGA_LIB_ERR);
}
clp = &listp->ldata;
(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s",
lap->hba_logp);
(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s",
lap->apidp->hba_phys);
clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
clp->ap_r_state = lap->hba_rstate;
clp->ap_o_state = CFGA_STAT_NONE; /* filled in later by the plug-in */
clp->ap_cond =
(bstate.b_retired) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN;
clp->ap_busy = 0;
clp->ap_status_time = (time_t)-1;
clp->ap_info[0] = '\0';
if (bstate.iconnect_type) {
/*
* For SPI type, keep the existing SCFGA_BUS_TYPE.
* For other types, the ap type will be scsi-'interconnct-type'.
*/
if (strcmp(bstate.iconnect_type, "SPI") == 0) {
(void) snprintf(clp->ap_type, sizeof (clp->ap_type),
"%s", SCFGA_BUS_TYPE);
} else {
for (i = 0; i < strlen(bstate.iconnect_type); i++) {
itypelower[i] =
tolower(bstate.iconnect_type[i]);
}
itypelower[i] = '\0';
(void) snprintf(clp->ap_type, sizeof (clp->ap_type),
"%s-%s", "scsi", itypelower);
}
}
/* Link it in */
listp->next = lap->listp;
lap->listp = listp;
return (SCFGA_OK);
}
static int
get_bus_state(di_node_t node, void *arg)
{
struct bus_state *bsp = (struct bus_state *)arg;
char *itype = NULL;
bsp->b_state = di_state(node);
bsp->b_retired = di_retired(node);
(void) di_prop_lookup_strings(DDI_DEV_T_ANY,
node, "initiator-interconnect-type", &itype);
if (itype != NULL) {
(void) strlcpy(bsp->iconnect_type, itype, 16);
} else {
bsp->iconnect_type[0] = '\0';
}
return (DI_WALK_TERMINATE);
}
static scfga_ret_t
do_stat_dev(
const di_node_t node,
const char *nodepath,
scfga_list_t *lap,
int limited_dev_stat)
{
uint_t devinfo_state = 0;
char *dyncomp = NULL;
cfga_list_data_t *clp = NULL;
ldata_list_t *listp = NULL;
cfga_stat_t ostate;
scfga_ret_t ret;
assert(lap->apidp->hba_phys != NULL);
assert(lap->hba_logp != NULL);
devinfo_state = di_state(node);
ostate = dev_devinfo_to_occupant_state(devinfo_state);
/* If child device is configured, record it */
if (ostate == CFGA_STAT_CONFIGURED) {
lap->chld_config = CFGA_STAT_CONFIGURED;
}
if (limited_dev_stat) {
/* We only want to know device config state */
return (SCFGA_OK);
}
listp = calloc(1, sizeof (ldata_list_t));
if (listp == NULL) {
lap->l_errno = errno;
return (SCFGA_LIB_ERR);
}
clp = &listp->ldata;
/* Create the dynamic component */
ret = make_dyncomp(node, nodepath, &dyncomp, &lap->l_errno);
if (ret != SCFGA_OK) {
S_FREE(listp);
return (ret);
}
assert(dyncomp != NULL);
/* Create logical and physical ap_id */
(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
lap->hba_logp, DYN_SEP, dyncomp);
(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
lap->apidp->hba_phys, DYN_SEP, dyncomp);
S_FREE(dyncomp);
clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
clp->ap_r_state = lap->hba_rstate;
clp->ap_o_state = ostate;
clp->ap_cond = di_retired(node) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN;
clp->ap_busy = 0; /* no way to determine state change */
clp->ap_status_time = (time_t)-1;
get_hw_info(node, clp, DEV_APID);
/* Link it in */
listp->next = lap->listp;
lap->listp = listp;
return (SCFGA_OK);
}
/* fill in device type, vid, pid from properties */
static void
get_hw_info(di_node_t node, cfga_list_data_t *clp, dyncomp_t type)
{
char *cp = NULL;
char *inq_vid, *inq_pid;
char client_inst[MAXNAMELEN];
/*
* Fill in type information
*/
cp = (char *)get_device_type(node, type);
if (cp == NULL) {
cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
}
(void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s", S_STR(cp));
if (type == DEV_APID) {
/*
* Fill in vendor and product ID.
*/
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
"inquiry-product-id", &inq_pid) == 1) &&
(di_prop_lookup_strings(DDI_DEV_T_ANY, node,
"inquiry-vendor-id", &inq_vid) == 1)) {
(void) snprintf(clp->ap_info, sizeof (clp->ap_info),
"%s %s", inq_vid, inq_pid);
}
} else {
if ((di_driver_name(node) != NULL) &&
(di_instance(node) != -1)) {
if (clp->ap_info == NULL) {
(void) snprintf(client_inst, MAXNAMELEN - 1,
"%s%d", di_driver_name(node),
di_instance(node));
(void) snprintf(clp->ap_info, MAXNAMELEN - 1,
"Client Device: %s", client_inst);
} else {
(void) snprintf(client_inst, MAXNAMELEN - 1,
"(%s%d)", di_driver_name(node),
di_instance(node));
(void) strlcat(clp->ap_info, client_inst,
CFGA_INFO_LEN);
}
}
}
}
/*
* Get dtype from "inquiry-device-type" property. If not present,
* derive it from minor node type
*/
static char *
get_device_type(di_node_t node, dyncomp_t type)
{
char *name = NULL;
int *inq_dtype;
int i;
if (di_prop_find(DDI_DEV_T_ANY, node, "smp-device") != DI_PROP_NIL) {
return ("smp");
}
/* first, derive type based on inquiry property */
if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
&inq_dtype) == 1) {
int itype = (*inq_dtype) & DTYPE_MASK;
for (i = 0; i < N_DEVICE_TYPES; i++) {
if (device_list[i].itype == DTYPE_UNKNOWN)
continue;
if (itype == device_list[i].itype) {
name = (type == DEV_APID) ?
(char *)device_list[i].name :
(char *)device_list[i].pathname;
break;
}
}
}
/* if property fails, use minor nodetype */
if (name == NULL) {
char *nodetype;
di_minor_t minor = di_minor_next(node, DI_MINOR_NIL);
if ((minor != DI_MINOR_NIL) &&
((nodetype = di_minor_nodetype(minor)) != NULL)) {
for (i = 0; i < N_DEVICE_TYPES; i++) {
if (device_list[i].ntype &&
(strcmp(nodetype, device_list[i].ntype)
== 0)) {
name = (type == DEV_APID) ?
(char *)device_list[i].name :
(char *)device_list[i].pathname;
break;
}
}
}
}
if (name == NULL) /* default to unknown */
name = "unknown";
return (name);
}
/* Transform linked list into an array */
scfga_ret_t
list_ext_postprocess(
ldata_list_t **llpp,
int nelem,
cfga_list_data_t **ap_id_list,
int *nlistp,
char **errstring)
{
cfga_list_data_t *ldatap = NULL;
ldata_list_t *tmplp = NULL;
int i = -1;
*ap_id_list = NULL;
*nlistp = 0;
if (*llpp == NULL || nelem < 0) {
return (SCFGA_LIB_ERR);
}
if (nelem == 0) {
return (SCFGA_APID_NOEXIST);
}
ldatap = calloc(nelem, sizeof (cfga_list_data_t));
if (ldatap == NULL) {
cfga_err(errstring, errno, ERR_LIST, 0);
return (SCFGA_LIB_ERR);
}
/* Extract the list_data structures from the linked list */
tmplp = *llpp;
for (i = 0; i < nelem && tmplp != NULL; i++) {
ldatap[i] = tmplp->ldata;
tmplp = tmplp->next;
}
if (i < nelem || tmplp != NULL) {
S_FREE(ldatap);
return (SCFGA_LIB_ERR);
}
*nlistp = nelem;
*ap_id_list = ldatap;
return (SCFGA_OK);
}
/*
* Convert bus state to receptacle state
*/
static cfga_stat_t
bus_devinfo_to_recep_state(uint_t bus_di_state)
{
if (bus_di_state & (DI_BUS_QUIESCED | DI_BUS_DOWN))
return (CFGA_STAT_DISCONNECTED);
return (CFGA_STAT_CONNECTED);
}
/*
* Convert device state to occupant state
*/
static cfga_stat_t
dev_devinfo_to_occupant_state(uint_t dev_di_state)
{
if (dev_di_state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN))
return (CFGA_STAT_UNCONFIGURED);
if (!(dev_di_state & DI_DRIVER_DETACHED))
return (CFGA_STAT_CONFIGURED);
return (CFGA_STAT_NONE);
}