1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include "cfga_scsi.h"
27 
28 /* Structure for walking the tree */
29 typedef struct {
30 	apid_t		*apidp;
31 	char		*hba_logp;
32 	ldata_list_t	*listp;
33 	scfga_cmd_t	cmd;
34 	cfga_stat_t	chld_config;
35 	cfga_stat_t	hba_rstate;
36 	scfga_ret_t	ret;
37 	int		l_errno;
38 } scfga_list_t;
39 
40 typedef struct {
41 	uint_t itype;
42 	const char *ntype;
43 	const char *name;
44 } scfga_devtype_t;
45 
46 /* The TYPE field is parseable and should not contain spaces */
47 #define	SCFGA_BUS_TYPE		"scsi-bus"
48 
49 /* Function prototypes */
50 static scfga_ret_t postprocess_list_data(const ldata_list_t *listp,
51     scfga_cmd_t cmd, cfga_stat_t chld_config, int *np);
52 static int stat_dev(di_node_t node, void *arg);
53 static scfga_ret_t do_stat_bus(scfga_list_t *lap, int limited_bus_stat);
54 static int get_bus_state(di_node_t node, void *arg);
55 
56 static scfga_ret_t do_stat_dev(const di_node_t node, const char *nodepath,
57     scfga_list_t *lap, int limited_dev_stat);
58 static cfga_stat_t bus_devinfo_to_recep_state(uint_t bus_di_state);
59 static cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state);
60 static char *get_device_type(di_node_t);
61 static void get_hw_info(di_node_t node, cfga_list_data_t *clp);
62 
63 
64 static scfga_devtype_t device_list[] = {
65 	{ DTYPE_DIRECT,		DDI_NT_BLOCK_CHAN,	"disk"},
66 	{ DTYPE_DIRECT,		DDI_NT_BLOCK,		"disk"},
67 	{ DTYPE_DIRECT,		DDI_NT_BLOCK_WWN,	"disk"},
68 	{ DTYPE_DIRECT,		DDI_NT_BLOCK_FABRIC,	"disk"},
69 	{ DTYPE_DIRECT,		DDI_NT_BLOCK_SAS,	"disk"},
70 	{ DTYPE_SEQUENTIAL,	DDI_NT_TAPE,		"tape"},
71 	{ DTYPE_PRINTER,	NULL,			"printer"},
72 	{ DTYPE_PROCESSOR,	NULL,			"processor"},
73 	{ DTYPE_WORM,		NULL,			"WORM"},
74 	{ DTYPE_RODIRECT,	DDI_NT_CD_CHAN,		"CD-ROM"},
75 	{ DTYPE_RODIRECT,	DDI_NT_CD,		"CD-ROM"},
76 	{ DTYPE_SCANNER,	NULL,			"scanner"},
77 	{ DTYPE_OPTICAL,	NULL,			"optical"},
78 	{ DTYPE_CHANGER,	NULL,			"med-changer"},
79 	{ DTYPE_COMM,		NULL,			"comm-device"},
80 	{ DTYPE_ARRAY_CTRL,	NULL,			"array-ctrl"},
81 	{ DTYPE_ESI,		NULL,			"ESI"}
82 };
83 
84 #define	N_DEVICE_TYPES	(sizeof (device_list) / sizeof (device_list[0]))
85 
86 scfga_ret_t
87 do_list(
88 	apid_t *apidp,
89 	scfga_cmd_t cmd,
90 	ldata_list_t **llpp,
91 	int *nelemp,
92 	char **errstring)
93 {
94 	int n = -1, l_errno = 0, limited_bus_stat;
95 	walkarg_t u;
96 	scfga_list_t larg = {NULL};
97 	scfga_ret_t ret;
98 	int init_flag;
99 
100 	assert(apidp->hba_phys != NULL && apidp->path != NULL);
101 
102 	if (*llpp != NULL || *nelemp != 0) {
103 		return (SCFGA_ERR);
104 	}
105 
106 	/* Create the HBA logid (also base component of logical ap_id) */
107 	ret = make_hba_logid(apidp->hba_phys, &larg.hba_logp, &l_errno);
108 	if (ret != SCFGA_OK) {
109 		cfga_err(errstring, l_errno, ERR_LIST, 0);
110 		return (SCFGA_ERR);
111 	}
112 
113 	assert(larg.hba_logp != NULL);
114 
115 	larg.cmd = cmd;
116 	larg.apidp = apidp;
117 	larg.hba_rstate = CFGA_STAT_NONE;
118 
119 
120 	/*
121 	 * For all list commands, the bus  and 1 or more devices
122 	 * needs to be stat'ed
123 	 */
124 
125 	/*
126 	 * By default we use DINFOCACHE to get a "full" snapshot
127 	 * This much faster than DINFOFORCE which actually
128 	 * attaches devices. DINFOFORCE used only if caller
129 	 * explicitly requests it via a private option.
130 	 */
131 	init_flag = (apidp->flags & FLAG_USE_DIFORCE) ? DINFOFORCE : DINFOCACHE;
132 	limited_bus_stat = 0;
133 
134 	switch (larg.cmd) {
135 		case SCFGA_STAT_DEV:
136 			limited_bus_stat = 1; /* We need only bus state */
137 			/*FALLTHRU*/
138 		case SCFGA_STAT_ALL:
139 			break;
140 		case SCFGA_STAT_BUS:
141 			/* limited_bus_stat = 0 and no DINFOCACHE/DINFOFORCE */
142 			init_flag = 0;
143 			break;
144 		default:
145 			cfga_err(errstring, EINVAL, ERR_LIST, 0);
146 			goto out;
147 	}
148 
149 	/*
150 	 * DINFOCACHE implies DINFOCPYALL. DINFOCPYALL shouldn't
151 	 * be ORed with DINFOCACHE, else libdevinfo will return
152 	 * error
153 	 */
154 	if (init_flag != DINFOCACHE)
155 		init_flag |= DINFOCPYALL;
156 
157 	if ((ret = do_stat_bus(&larg, limited_bus_stat)) != SCFGA_OK) {
158 		cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
159 		goto out;
160 	}
161 
162 #ifdef DEBUG
163 	if (limited_bus_stat) {
164 		assert(larg.listp == NULL);
165 	} else {
166 		assert(larg.listp != NULL);
167 	}
168 #endif
169 
170 	/* Assume that the bus has no configured children */
171 	larg.chld_config = CFGA_STAT_UNCONFIGURED;
172 
173 	/*
174 	 * If stat'ing a specific device, we don't know if it exists yet.
175 	 * If stat'ing a bus or a bus and child devices, we have at least the
176 	 * bus stat data at this point.
177 	 */
178 	if (larg.cmd == SCFGA_STAT_DEV) {
179 		larg.ret = SCFGA_APID_NOEXIST;
180 	} else {
181 		larg.ret = SCFGA_OK;
182 	}
183 
184 	/* we need to stat at least 1 device for all commands */
185 	u.node_args.flags = DI_WALK_CLDFIRST;
186 	u.node_args.fcn = stat_dev;
187 
188 	/*
189 	 * Subtree is ALWAYS rooted at the HBA (not at the device) as
190 	 * otherwise deadlock may occur if bus is disconnected.
191 	 */
192 	ret = walk_tree(apidp->hba_phys, &larg, init_flag, &u,
193 	    SCFGA_WALK_NODE, &larg.l_errno);
194 
195 	if (ret != SCFGA_OK || (ret = larg.ret) != SCFGA_OK) {
196 		if (ret != SCFGA_APID_NOEXIST) {
197 			cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
198 		}
199 		goto out;
200 	}
201 
202 	assert(larg.listp != NULL);
203 
204 	n = 0;
205 	ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n);
206 	if (ret != SCFGA_OK) {
207 		cfga_err(errstring, 0, ERR_LIST, 0);
208 		ret = SCFGA_LIB_ERR;
209 		goto out;
210 	}
211 
212 	*nelemp = n;
213 	*llpp = larg.listp;
214 	ret = SCFGA_OK;
215 	/* FALLTHROUGH */
216 out:
217 	if (ret != SCFGA_OK) list_free(&larg.listp);
218 	S_FREE(larg.hba_logp);
219 	return (ret);
220 }
221 
222 static scfga_ret_t
223 postprocess_list_data(
224 	const ldata_list_t *listp,
225 	scfga_cmd_t cmd,
226 	cfga_stat_t chld_config,
227 	int *np)
228 {
229 	ldata_list_t *tmplp = NULL;
230 	cfga_list_data_t *hba_ldatap = NULL;
231 	int i;
232 
233 
234 	*np = 0;
235 
236 	if (listp == NULL) {
237 		return (SCFGA_ERR);
238 	}
239 
240 	hba_ldatap = NULL;
241 	tmplp = (ldata_list_t *)listp;
242 	for (i = 0; tmplp != NULL; tmplp = tmplp->next) {
243 		i++;
244 		if (GET_DYN(tmplp->ldata.ap_phys_id) == NULL) {
245 			/* A bus stat data */
246 			assert(GET_DYN(tmplp->ldata.ap_log_id) == NULL);
247 			hba_ldatap = &tmplp->ldata;
248 #ifdef DEBUG
249 		} else {
250 			assert(GET_DYN(tmplp->ldata.ap_log_id) != NULL);
251 #endif
252 		}
253 	}
254 
255 	switch (cmd) {
256 	case SCFGA_STAT_DEV:
257 		if (i != 1 || hba_ldatap != NULL) {
258 			return (SCFGA_LIB_ERR);
259 		}
260 		break;
261 	case SCFGA_STAT_BUS:
262 		if (i != 1 || hba_ldatap == NULL) {
263 			return (SCFGA_LIB_ERR);
264 		}
265 		break;
266 	case SCFGA_STAT_ALL:
267 		if (i < 1 || hba_ldatap == NULL) {
268 			return (SCFGA_LIB_ERR);
269 		}
270 		break;
271 	default:
272 		return (SCFGA_LIB_ERR);
273 	}
274 
275 	*np = i;
276 
277 	/* Fill in the occupant (child) state. */
278 	if (hba_ldatap != NULL) {
279 		hba_ldatap->ap_o_state = chld_config;
280 	}
281 	return (SCFGA_OK);
282 }
283 
284 static int
285 stat_dev(di_node_t node, void *arg)
286 {
287 	scfga_list_t *lap = NULL;
288 	char *devfsp = NULL, *nodepath = NULL;
289 	size_t len = 0;
290 	int limited_dev_stat = 0, match_minor, rv;
291 	scfga_ret_t ret;
292 
293 	lap = (scfga_list_t *)arg;
294 
295 	/* Skip stub nodes */
296 	if (IS_STUB_NODE(node)) {
297 		return (DI_WALK_CONTINUE);
298 	}
299 
300 	/* Skip partial nodes */
301 	if (!known_state(node)) {
302 		return (DI_WALK_CONTINUE);
303 	}
304 
305 	devfsp = di_devfs_path(node);
306 	if (devfsp == NULL) {
307 		rv = DI_WALK_CONTINUE;
308 		goto out;
309 	}
310 
311 	len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;
312 
313 	nodepath = calloc(1, len);
314 	if (nodepath == NULL) {
315 		lap->l_errno = errno;
316 		lap->ret = SCFGA_LIB_ERR;
317 		rv = DI_WALK_TERMINATE;
318 		goto out;
319 	}
320 
321 	(void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);
322 
323 	/* Skip node if it is HBA */
324 	match_minor = 0;
325 	if (!dev_cmp(lap->apidp->hba_phys, nodepath, match_minor)) {
326 		rv = DI_WALK_CONTINUE;
327 		goto out;
328 	}
329 
330 	/* If stat'ing a specific device, is this that device */
331 	if (lap->cmd == SCFGA_STAT_DEV) {
332 		assert(lap->apidp->path != NULL);
333 		if (dev_cmp(lap->apidp->path, nodepath, match_minor)) {
334 			rv = DI_WALK_CONTINUE;
335 			goto out;
336 		}
337 	}
338 
339 	/*
340 	 * If stat'ing a bus only, we look at device nodes only to get
341 	 * bus configuration status. So a limited stat will suffice.
342 	 */
343 	if (lap->cmd == SCFGA_STAT_BUS) {
344 		limited_dev_stat = 1;
345 	} else {
346 		limited_dev_stat = 0;
347 	}
348 
349 	/*
350 	 * Ignore errors if stat'ing a bus or listing all
351 	 */
352 	ret = do_stat_dev(node, nodepath, lap, limited_dev_stat);
353 	if (ret != SCFGA_OK) {
354 		if (lap->cmd == SCFGA_STAT_DEV) {
355 			lap->ret = ret;
356 			rv = DI_WALK_TERMINATE;
357 		} else {
358 			rv = DI_WALK_CONTINUE;
359 		}
360 		goto out;
361 	}
362 
363 	/* Are we done ? */
364 	rv = DI_WALK_CONTINUE;
365 	if (lap->cmd == SCFGA_STAT_BUS &&
366 	    lap->chld_config == CFGA_STAT_CONFIGURED) {
367 		rv = DI_WALK_TERMINATE;
368 	} else if (lap->cmd == SCFGA_STAT_DEV) {
369 		/*
370 		 * If stat'ing a specific device, we are done at this point.
371 		 */
372 		lap->ret = SCFGA_OK;
373 		rv = DI_WALK_TERMINATE;
374 	}
375 
376 	/*FALLTHRU*/
377 out:
378 	S_FREE(nodepath);
379 	if (devfsp != NULL) di_devfs_path_free(devfsp);
380 	return (rv);
381 }
382 
383 
384 struct bus_state {
385 	int	b_state;
386 	int	b_retired;
387 };
388 
389 static scfga_ret_t
390 do_stat_bus(scfga_list_t *lap, int limited_bus_stat)
391 {
392 	cfga_list_data_t *clp = NULL;
393 	ldata_list_t *listp = NULL;
394 	int l_errno = 0;
395 	struct bus_state bstate = {0};
396 	walkarg_t u;
397 	scfga_ret_t ret;
398 
399 	assert(lap->hba_logp != NULL);
400 
401 	/* Get bus state */
402 	u.node_args.flags = 0;
403 	u.node_args.fcn = get_bus_state;
404 
405 	ret = walk_tree(lap->apidp->hba_phys, &bstate, DINFOPROP, &u,
406 	    SCFGA_WALK_NODE, &l_errno);
407 	if (ret == SCFGA_OK) {
408 		lap->hba_rstate = bus_devinfo_to_recep_state(bstate.b_state);
409 	} else {
410 		lap->hba_rstate = CFGA_STAT_NONE;
411 	}
412 
413 	if (limited_bus_stat) {
414 		/* We only want to know bus(receptacle) connect status */
415 		return (SCFGA_OK);
416 	}
417 
418 	listp = calloc(1, sizeof (ldata_list_t));
419 	if (listp == NULL) {
420 		lap->l_errno = errno;
421 		return (SCFGA_LIB_ERR);
422 	}
423 
424 	clp = &listp->ldata;
425 
426 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s",
427 	    lap->hba_logp);
428 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s",
429 	    lap->apidp->hba_phys);
430 
431 	clp->ap_class[0] = '\0';	/* Filled by libcfgadm */
432 	clp->ap_r_state = lap->hba_rstate;
433 	clp->ap_o_state = CFGA_STAT_NONE; /* filled in later by the plug-in */
434 	clp->ap_cond =
435 	    (bstate.b_retired) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN;
436 	clp->ap_busy = 0;
437 	clp->ap_status_time = (time_t)-1;
438 	clp->ap_info[0] = '\0';
439 
440 	(void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
441 	    SCFGA_BUS_TYPE);
442 
443 	/* Link it in */
444 	listp->next = lap->listp;
445 	lap->listp = listp;
446 
447 	return (SCFGA_OK);
448 }
449 
450 static int
451 get_bus_state(di_node_t node, void *arg)
452 {
453 	struct bus_state *bsp = (struct bus_state *)arg;
454 
455 	bsp->b_state = di_state(node);
456 	bsp->b_retired = di_retired(node);
457 
458 	return (DI_WALK_TERMINATE);
459 }
460 
461 static scfga_ret_t
462 do_stat_dev(
463 	const di_node_t node,
464 	const char *nodepath,
465 	scfga_list_t *lap,
466 	int limited_dev_stat)
467 {
468 	uint_t devinfo_state = 0;
469 	char *dyncomp = NULL;
470 	cfga_list_data_t *clp = NULL;
471 	ldata_list_t *listp = NULL;
472 	cfga_stat_t ostate;
473 	scfga_ret_t ret;
474 
475 	assert(lap->apidp->hba_phys != NULL);
476 	assert(lap->hba_logp != NULL);
477 
478 	devinfo_state = di_state(node);
479 	ostate = dev_devinfo_to_occupant_state(devinfo_state);
480 
481 	/* If child device is configured, record it */
482 	if (ostate == CFGA_STAT_CONFIGURED) {
483 		lap->chld_config = CFGA_STAT_CONFIGURED;
484 	}
485 
486 	if (limited_dev_stat) {
487 		/* We only want to know device config state */
488 		return (SCFGA_OK);
489 	}
490 
491 	listp = calloc(1, sizeof (ldata_list_t));
492 	if (listp == NULL) {
493 		lap->l_errno = errno;
494 		return (SCFGA_LIB_ERR);
495 	}
496 
497 	clp = &listp->ldata;
498 
499 	/* Create the dynamic component */
500 	ret = make_dyncomp(node, nodepath, &dyncomp, &lap->l_errno);
501 	if (ret != SCFGA_OK) {
502 		S_FREE(listp);
503 		return (ret);
504 	}
505 
506 	assert(dyncomp != NULL);
507 
508 	/* Create logical and physical ap_id */
509 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
510 	    lap->hba_logp, DYN_SEP, dyncomp);
511 
512 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
513 	    lap->apidp->hba_phys, DYN_SEP, dyncomp);
514 
515 	S_FREE(dyncomp);
516 
517 	clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
518 	clp->ap_r_state = lap->hba_rstate;
519 	clp->ap_o_state = ostate;
520 	clp->ap_cond = di_retired(node) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN;
521 	clp->ap_busy = 0; /* no way to determine state change */
522 	clp->ap_status_time = (time_t)-1;
523 
524 	get_hw_info(node, clp);
525 
526 	/* Link it in */
527 	listp->next = lap->listp;
528 	lap->listp = listp;
529 
530 	return (SCFGA_OK);
531 }
532 
533 /* fill in device type, vid, pid from properties */
534 static void
535 get_hw_info(di_node_t node, cfga_list_data_t *clp)
536 {
537 	char *cp = NULL;
538 	char *inq_vid, *inq_pid;
539 
540 	/*
541 	 * Fill in type information
542 	 */
543 	cp = (char *)get_device_type(node);
544 	if (cp == NULL) {
545 		cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
546 	}
547 	(void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s", S_STR(cp));
548 
549 	/*
550 	 * Fill in vendor and product ID.
551 	 */
552 	if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
553 	    "inquiry-product-id", &inq_pid) == 1) &&
554 	    (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
555 	    "inquiry-vendor-id", &inq_vid) == 1)) {
556 		(void) snprintf(clp->ap_info, sizeof (clp->ap_info),
557 		    "%s %s", inq_vid, inq_pid);
558 	}
559 }
560 
561 /*
562  * Get dtype from "inquiry-device-type" property. If not present,
563  * derive it from minor node type
564  */
565 static char *
566 get_device_type(di_node_t node)
567 {
568 	char *name = NULL;
569 	int *inq_dtype;
570 	int i;
571 
572 	if (di_prop_find(DDI_DEV_T_ANY, node, "smp-device") != DI_PROP_NIL) {
573 		return ("smp");
574 	}
575 
576 	/* first, derive type based on inquiry property */
577 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
578 	    &inq_dtype) == 1) {
579 		int itype = (*inq_dtype) & DTYPE_MASK;
580 
581 		for (i = 0; i < N_DEVICE_TYPES; i++) {
582 			if (device_list[i].itype == DTYPE_UNKNOWN)
583 				continue;
584 			if (itype == device_list[i].itype) {
585 				name = (char *)device_list[i].name;
586 				break;
587 			}
588 		}
589 	}
590 
591 	/* if property fails, use minor nodetype */
592 	if (name == NULL) {
593 		char *nodetype;
594 		di_minor_t minor = di_minor_next(node, DI_MINOR_NIL);
595 
596 		if ((minor != DI_MINOR_NIL) &&
597 		    ((nodetype = di_minor_nodetype(minor)) != NULL)) {
598 			for (i = 0; i < N_DEVICE_TYPES; i++) {
599 				if (device_list[i].ntype &&
600 				    (strcmp(nodetype, device_list[i].ntype)
601 				    == 0)) {
602 					name = (char *)device_list[i].name;
603 					break;
604 				}
605 			}
606 		}
607 	}
608 
609 	if (name == NULL)	/* default to unknown */
610 		name = "unknown";
611 	return (name);
612 }
613 
614 /* Transform linked list into an array */
615 scfga_ret_t
616 list_ext_postprocess(
617 	ldata_list_t		**llpp,
618 	int			nelem,
619 	cfga_list_data_t	**ap_id_list,
620 	int			*nlistp,
621 	char			**errstring)
622 {
623 	cfga_list_data_t *ldatap = NULL;
624 	ldata_list_t *tmplp = NULL;
625 	int i = -1;
626 
627 	*ap_id_list = NULL;
628 	*nlistp = 0;
629 
630 	if (*llpp == NULL || nelem < 0) {
631 		return (SCFGA_LIB_ERR);
632 	}
633 
634 	if (nelem == 0) {
635 		return (SCFGA_APID_NOEXIST);
636 	}
637 
638 	ldatap = calloc(nelem, sizeof (cfga_list_data_t));
639 	if (ldatap == NULL) {
640 		cfga_err(errstring, errno, ERR_LIST, 0);
641 		return (SCFGA_LIB_ERR);
642 	}
643 
644 	/* Extract the list_data structures from the linked list */
645 	tmplp = *llpp;
646 	for (i = 0; i < nelem && tmplp != NULL; i++) {
647 		ldatap[i] = tmplp->ldata;
648 		tmplp = tmplp->next;
649 	}
650 
651 	if (i < nelem || tmplp != NULL) {
652 		S_FREE(ldatap);
653 		return (SCFGA_LIB_ERR);
654 	}
655 
656 	*nlistp = nelem;
657 	*ap_id_list = ldatap;
658 
659 	return (SCFGA_OK);
660 }
661 
662 /*
663  * Convert bus state to receptacle state
664  */
665 static cfga_stat_t
666 bus_devinfo_to_recep_state(uint_t bus_di_state)
667 {
668 	cfga_stat_t rs;
669 
670 	switch (bus_di_state) {
671 	case DI_BUS_QUIESCED:
672 	case DI_BUS_DOWN:
673 		rs = CFGA_STAT_DISCONNECTED;
674 		break;
675 	/*
676 	 * NOTE: An explicit flag for active should probably be added to
677 	 * libdevinfo.
678 	 */
679 	default:
680 		rs = CFGA_STAT_CONNECTED;
681 		break;
682 	}
683 
684 	return (rs);
685 }
686 
687 /*
688  * Convert device state to occupant state
689  */
690 static cfga_stat_t
691 dev_devinfo_to_occupant_state(uint_t dev_di_state)
692 {
693 	/* Driver attached ? */
694 	if ((dev_di_state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
695 		return (CFGA_STAT_CONFIGURED);
696 	}
697 
698 	if ((dev_di_state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
699 	    (dev_di_state & DI_DEVICE_DOWN) == DI_DEVICE_DOWN) {
700 		return (CFGA_STAT_UNCONFIGURED);
701 	} else {
702 		return (CFGA_STAT_NONE);
703 	}
704 }
705