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