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