xref: /titanic_50/usr/src/lib/cfgadm_plugins/scsi/common/cfga_list.c (revision 4c06356b0f0fffb4fc1b6eccc8e5d8e2254a84d6)
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 	const char *pathname;
45 } scfga_devtype_t;
46 
47 /* The TYPE field is parseable and should not contain spaces */
48 #define	SCFGA_BUS_TYPE		"scsi-bus"
49 
50 /* Function prototypes */
51 static scfga_ret_t postprocess_list_data(const ldata_list_t *listp,
52     scfga_cmd_t cmd, cfga_stat_t chld_config, int *np);
53 static int stat_dev(di_node_t node, void *arg);
54 static scfga_ret_t do_stat_bus(scfga_list_t *lap, int limited_bus_stat);
55 static int get_bus_state(di_node_t node, void *arg);
56 
57 static scfga_ret_t do_stat_dev(const di_node_t node, const char *nodepath,
58     scfga_list_t *lap, int limited_dev_stat);
59 static cfga_stat_t bus_devinfo_to_recep_state(uint_t bus_di_state);
60 static cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state);
61 static char *get_device_type(di_node_t, dyncomp_t);
62 static void get_hw_info(di_node_t node, cfga_list_data_t *clp, dyncomp_t type);
63 static scfga_ret_t create_pathinfo_ldata(di_path_t pi_node, scfga_list_t *lap,
64     int *l_errnop);
65 
66 
67 static scfga_devtype_t device_list[] = {
68 	{ DTYPE_DIRECT,	    DDI_NT_BLOCK_CHAN,	"disk",		"disk-path"},
69 	{ DTYPE_DIRECT,	    DDI_NT_BLOCK,	"disk",		"disk-path"},
70 	{ DTYPE_DIRECT,	    DDI_NT_BLOCK_WWN,	"disk",		"disk-path"},
71 	{ DTYPE_DIRECT,	    DDI_NT_BLOCK_FABRIC,    "disk",	"disk-path"},
72 	{ DTYPE_DIRECT,	    DDI_NT_BLOCK_SAS,   "disk",		"disk-path"},
73 	{ DTYPE_SEQUENTIAL, DDI_NT_TAPE,	"tape",		"tape-path"},
74 	{ DTYPE_PRINTER,    NULL,		"printer",	"printer-path"},
75 	{ DTYPE_PROCESSOR,  NULL,		"processor",	"PRCS-path"},
76 	{ DTYPE_WORM,	    NULL,		"WORM",		"WORM-path"},
77 	{ DTYPE_RODIRECT,   DDI_NT_CD_CHAN,	"CD-ROM",	"CD-ROM-path"},
78 	{ DTYPE_RODIRECT,   DDI_NT_CD,		"CD-ROM",	"CD-ROM-path"},
79 	{ DTYPE_SCANNER,    NULL,		"scanner",	"scanner-path"},
80 	{ DTYPE_OPTICAL,    NULL,		"optical",	"optical-path"},
81 	{ DTYPE_CHANGER,    NULL,		"med-changer",	"MEDCHGR-path"},
82 	{ DTYPE_COMM,	    NULL,		"comm-device",	"COMDEV-path"},
83 	{ DTYPE_ARRAY_CTRL, NULL,		"array-ctrl",	"ARRCTRL-path"},
84 	{ DTYPE_ESI,	    NULL,		"ESI",		"ESI-path"}
85 };
86 
87 #define	N_DEVICE_TYPES	(sizeof (device_list) / sizeof (device_list[0]))
88 
89 scfga_ret_t
do_list(apid_t * apidp,scfga_cmd_t cmd,ldata_list_t ** llpp,int * nelemp,char ** errstring)90 do_list(
91 	apid_t *apidp,
92 	scfga_cmd_t cmd,
93 	ldata_list_t **llpp,
94 	int *nelemp,
95 	char **errstring)
96 {
97 	int n = -1, l_errno = 0, limited_bus_stat;
98 	walkarg_t u;
99 	scfga_list_t larg = {NULL};
100 	scfga_ret_t ret;
101 	int init_flag;
102 
103 	assert(apidp->hba_phys != NULL && apidp->path != NULL);
104 
105 	if (*llpp != NULL || *nelemp != 0) {
106 		return (SCFGA_ERR);
107 	}
108 
109 	/* Create the HBA logid (also base component of logical ap_id) */
110 	ret = make_hba_logid(apidp->hba_phys, &larg.hba_logp, &l_errno);
111 	if (ret != SCFGA_OK) {
112 		cfga_err(errstring, l_errno, ERR_LIST, 0);
113 		return (SCFGA_ERR);
114 	}
115 
116 	assert(larg.hba_logp != NULL);
117 
118 	larg.cmd = cmd;
119 	larg.apidp = apidp;
120 	larg.hba_rstate = CFGA_STAT_NONE;
121 
122 
123 	/*
124 	 * For all list commands, the bus  and 1 or more devices
125 	 * needs to be stat'ed
126 	 */
127 
128 	/*
129 	 * By default we use DINFOCACHE to get a "full" snapshot
130 	 * This much faster than DINFOFORCE which actually
131 	 * attaches devices. DINFOFORCE used only if caller
132 	 * explicitly requests it via a private option.
133 	 */
134 	init_flag = (apidp->flags & FLAG_USE_DIFORCE) ? DINFOFORCE : DINFOCACHE;
135 	limited_bus_stat = 0;
136 
137 	switch (larg.cmd) {
138 		case SCFGA_STAT_DEV:
139 			limited_bus_stat = 1; /* We need only bus state */
140 			/*FALLTHRU*/
141 		case SCFGA_STAT_ALL:
142 			break;
143 		case SCFGA_STAT_BUS:
144 			/* limited_bus_stat = 0 and no DINFOCACHE/DINFOFORCE */
145 			init_flag = 0;
146 			break;
147 		default:
148 			cfga_err(errstring, EINVAL, ERR_LIST, 0);
149 			goto out;
150 	}
151 
152 	/*
153 	 * DINFOCACHE implies DINFOCPYALL. DINFOCPYALL shouldn't
154 	 * be ORed with DINFOCACHE, else libdevinfo will return
155 	 * error
156 	 */
157 	if (init_flag != DINFOCACHE)
158 		init_flag |= DINFOCPYALL;
159 
160 	if ((ret = do_stat_bus(&larg, limited_bus_stat)) != SCFGA_OK) {
161 		cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
162 		goto out;
163 	}
164 
165 #ifdef DEBUG
166 	if (limited_bus_stat) {
167 		assert(larg.listp == NULL);
168 	} else {
169 		assert(larg.listp != NULL);
170 	}
171 #endif
172 
173 	/* Assume that the bus has no configured children */
174 	larg.chld_config = CFGA_STAT_UNCONFIGURED;
175 
176 	/*
177 	 * If stat'ing a specific device, we don't know if it exists yet.
178 	 * If stat'ing a bus or a bus and child devices, we have at least the
179 	 * bus stat data at this point.
180 	 */
181 	if (larg.cmd == SCFGA_STAT_DEV) {
182 		larg.ret = SCFGA_APID_NOEXIST;
183 	} else {
184 		larg.ret = SCFGA_OK;
185 	}
186 
187 	/* we need to stat at least 1 device for all commands */
188 	if (apidp->dyntype == PATH_APID) {
189 		/*
190 		 * When cmd is SCFGA_STAT_DEV and the ap id is pathinfo
191 		 * related.
192 		 */
193 		ret = walk_tree(apidp->hba_phys, &larg, init_flag, NULL,
194 		    SCFGA_WALK_PATH, &larg.l_errno);
195 	} else {
196 		/* we need to stat at least 1 device for all commands */
197 		u.node_args.flags = DI_WALK_CLDFIRST;
198 		u.node_args.fcn = stat_dev;
199 
200 		/*
201 		 * Subtree is ALWAYS rooted at the HBA (not at the device) as
202 		 * otherwise deadlock may occur if bus is disconnected.
203 		 */
204 		ret = walk_tree(apidp->hba_phys, &larg, init_flag, &u,
205 		    SCFGA_WALK_NODE, &larg.l_errno);
206 
207 		/*
208 		 * Check path info on the following conditions.
209 		 *
210 		 * - chld_config is still set to CFGA_STAT_UNCONFIGURED for
211 		 *   SCFGA_STAT_BUS cmd after walking any child node.
212 		 * - walking node succeeded for SCFGA_STAT_ALL cmd(Continue on
213 		 *   stating path info node).
214 		 * - apid is pathinfo associated and larg.ret is still set to
215 		 *   SCFGA_APID_NOEXIST for SCFGA_STAT_DEV cmd.
216 		 */
217 		if (((cmd == SCFGA_STAT_BUS) &&
218 		    (larg.chld_config == CFGA_STAT_UNCONFIGURED)) ||
219 		    ((cmd == SCFGA_STAT_ALL) && (ret == SCFGA_OK))) {
220 			ret = walk_tree(apidp->hba_phys, &larg, init_flag, NULL,
221 			    SCFGA_WALK_PATH, &larg.l_errno);
222 		}
223 	}
224 
225 	if (ret != SCFGA_OK || (ret = larg.ret) != SCFGA_OK) {
226 		if (ret != SCFGA_APID_NOEXIST) {
227 			cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
228 		}
229 		goto out;
230 	}
231 
232 	assert(larg.listp != NULL);
233 
234 	n = 0;
235 	ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n);
236 	if (ret != SCFGA_OK) {
237 		cfga_err(errstring, 0, ERR_LIST, 0);
238 		ret = SCFGA_LIB_ERR;
239 		goto out;
240 	}
241 
242 	*nelemp = n;
243 	*llpp = larg.listp;
244 	ret = SCFGA_OK;
245 	/* FALLTHROUGH */
246 out:
247 	if (ret != SCFGA_OK) list_free(&larg.listp);
248 	S_FREE(larg.hba_logp);
249 	return (ret);
250 }
251 
252 static scfga_ret_t
postprocess_list_data(const ldata_list_t * listp,scfga_cmd_t cmd,cfga_stat_t chld_config,int * np)253 postprocess_list_data(
254 	const ldata_list_t *listp,
255 	scfga_cmd_t cmd,
256 	cfga_stat_t chld_config,
257 	int *np)
258 {
259 	ldata_list_t *tmplp = NULL;
260 	cfga_list_data_t *hba_ldatap = NULL;
261 	int i;
262 
263 
264 	*np = 0;
265 
266 	if (listp == NULL) {
267 		return (SCFGA_ERR);
268 	}
269 
270 	hba_ldatap = NULL;
271 	tmplp = (ldata_list_t *)listp;
272 	for (i = 0; tmplp != NULL; tmplp = tmplp->next) {
273 		i++;
274 		if (GET_DYN(tmplp->ldata.ap_phys_id) == NULL) {
275 			/* A bus stat data */
276 			assert(GET_DYN(tmplp->ldata.ap_log_id) == NULL);
277 			hba_ldatap = &tmplp->ldata;
278 #ifdef DEBUG
279 		} else {
280 			assert(GET_DYN(tmplp->ldata.ap_log_id) != NULL);
281 #endif
282 		}
283 	}
284 
285 	switch (cmd) {
286 	case SCFGA_STAT_DEV:
287 		if (i != 1 || hba_ldatap != NULL) {
288 			return (SCFGA_LIB_ERR);
289 		}
290 		break;
291 	case SCFGA_STAT_BUS:
292 		if (i != 1 || hba_ldatap == NULL) {
293 			return (SCFGA_LIB_ERR);
294 		}
295 		break;
296 	case SCFGA_STAT_ALL:
297 		if (i < 1 || hba_ldatap == NULL) {
298 			return (SCFGA_LIB_ERR);
299 		}
300 		break;
301 	default:
302 		return (SCFGA_LIB_ERR);
303 	}
304 
305 	*np = i;
306 
307 	/* Fill in the occupant (child) state. */
308 	if (hba_ldatap != NULL) {
309 		hba_ldatap->ap_o_state = chld_config;
310 	}
311 	return (SCFGA_OK);
312 }
313 
314 static int
stat_dev(di_node_t node,void * arg)315 stat_dev(di_node_t node, void *arg)
316 {
317 	scfga_list_t *lap = NULL;
318 	char *devfsp = NULL, *nodepath = NULL;
319 	size_t len = 0;
320 	int limited_dev_stat = 0, match_minor, rv;
321 	scfga_ret_t ret;
322 
323 	lap = (scfga_list_t *)arg;
324 
325 	/* Skip stub nodes */
326 	if (IS_STUB_NODE(node)) {
327 		return (DI_WALK_CONTINUE);
328 	}
329 
330 	/* Skip partial nodes */
331 	if (!known_state(node)) {
332 		return (DI_WALK_CONTINUE);
333 	}
334 
335 	devfsp = di_devfs_path(node);
336 	if (devfsp == NULL) {
337 		rv = DI_WALK_CONTINUE;
338 		goto out;
339 	}
340 
341 	len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;
342 
343 	nodepath = calloc(1, len);
344 	if (nodepath == NULL) {
345 		lap->l_errno = errno;
346 		lap->ret = SCFGA_LIB_ERR;
347 		rv = DI_WALK_TERMINATE;
348 		goto out;
349 	}
350 
351 	(void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);
352 
353 	/* Skip node if it is HBA */
354 	match_minor = 0;
355 	if (!dev_cmp(lap->apidp->hba_phys, nodepath, match_minor)) {
356 		rv = DI_WALK_CONTINUE;
357 		goto out;
358 	}
359 
360 	/* If stat'ing a specific device, is this that device */
361 	if (lap->cmd == SCFGA_STAT_DEV) {
362 		assert(lap->apidp->path != NULL);
363 		if (dev_cmp(lap->apidp->path, nodepath, match_minor)) {
364 			rv = DI_WALK_CONTINUE;
365 			goto out;
366 		}
367 	}
368 
369 	/*
370 	 * If stat'ing a bus only, we look at device nodes only to get
371 	 * bus configuration status. So a limited stat will suffice.
372 	 */
373 	if (lap->cmd == SCFGA_STAT_BUS) {
374 		limited_dev_stat = 1;
375 	} else {
376 		limited_dev_stat = 0;
377 	}
378 
379 	/*
380 	 * Ignore errors if stat'ing a bus or listing all
381 	 */
382 	ret = do_stat_dev(node, nodepath, lap, limited_dev_stat);
383 	if (ret != SCFGA_OK) {
384 		if (lap->cmd == SCFGA_STAT_DEV) {
385 			lap->ret = ret;
386 			rv = DI_WALK_TERMINATE;
387 		} else {
388 			rv = DI_WALK_CONTINUE;
389 		}
390 		goto out;
391 	}
392 
393 	/* Are we done ? */
394 	rv = DI_WALK_CONTINUE;
395 	if (lap->cmd == SCFGA_STAT_BUS &&
396 	    lap->chld_config == CFGA_STAT_CONFIGURED) {
397 		rv = DI_WALK_TERMINATE;
398 	} else if (lap->cmd == SCFGA_STAT_DEV) {
399 		/*
400 		 * If stat'ing a specific device, we are done at this point.
401 		 */
402 		lap->ret = SCFGA_OK;
403 		rv = DI_WALK_TERMINATE;
404 	}
405 
406 	/*FALLTHRU*/
407 out:
408 	S_FREE(nodepath);
409 	if (devfsp != NULL) di_devfs_path_free(devfsp);
410 	return (rv);
411 }
412 
413 /*
414  * Create list date entry and add to ldata list.
415  */
416 static scfga_ret_t
create_pathinfo_ldata(di_path_t pi_node,scfga_list_t * lap,int * l_errnop)417 create_pathinfo_ldata(di_path_t pi_node, scfga_list_t *lap, int *l_errnop)
418 {
419 	ldata_list_t	*listp = NULL;
420 	cfga_list_data_t	*clp;
421 	di_node_t	client_node = DI_NODE_NIL;
422 	di_minor_t	minor;
423 	scfga_ret_t 	ret;
424 	di_path_state_t	pi_state;
425 	char		*dyncomp = NULL, *client_path = NULL;
426 	char		pathbuf[MAXPATHLEN], *client_devlink = NULL;
427 	int		match_minor;
428 
429 	listp = calloc(1, sizeof (ldata_list_t));
430 	if (listp == NULL) {
431 		lap->l_errno = errno;
432 		return (SCFGA_LIB_ERR);
433 	}
434 	clp = &listp->ldata;
435 	ret = make_path_dyncomp(pi_node, &dyncomp, &lap->l_errno);
436 	if (ret != SCFGA_OK) {
437 		S_FREE(listp);
438 		return (ret);
439 	}
440 
441 	client_node = di_path_client_node(pi_node);
442 	if (client_node == DI_NODE_NIL) {
443 		*l_errnop = errno;
444 		S_FREE(dyncomp);
445 		return (SCFGA_LIB_ERR);
446 	}
447 
448 	/* Create logical and physical ap_id */
449 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
450 	    lap->hba_logp, DYN_SEP, dyncomp);
451 
452 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
453 	    lap->apidp->hba_phys, DYN_SEP, dyncomp);
454 
455 	S_FREE(dyncomp);
456 
457 	/* ap class filled in by libcfgadm */
458 	clp->ap_class[0] = '\0';
459 	clp->ap_r_state = lap->hba_rstate;
460 	/* path info exist so set to configured. */
461 	clp->ap_o_state = CFGA_STAT_CONFIGURED;
462 
463 	/* now fill up ap_info field with client dev link and instance #. */
464 	client_path = di_devfs_path(client_node);
465 	if (client_path) {
466 		/* get first minor node. */
467 		minor = di_minor_next(client_node, DI_MINOR_NIL);
468 		if (minor == DI_MINOR_NIL) {
469 			match_minor = 0;
470 			(void) snprintf(pathbuf, MAXPATHLEN, "%s:%s",
471 			    DEVICES_DIR, client_path);
472 		} else {
473 			match_minor = 1;
474 			(void) snprintf(pathbuf, MAXPATHLEN, "%s%s:%s",
475 			    DEVICES_DIR, client_path, di_minor_name(minor));
476 		}
477 		(void) physpath_to_devlink(pathbuf, &client_devlink, l_errnop,
478 		    match_minor);
479 		di_devfs_path_free(client_path);
480 	}
481 
482 	if (client_devlink) {
483 		(void) snprintf(clp->ap_info, CFGA_INFO_LEN,
484 		    "%s: %s", "Client Device", client_devlink);
485 		S_FREE(client_devlink);
486 	}
487 
488 	get_hw_info(client_node, clp, PATH_APID);
489 
490 	if ((pi_state = di_path_state(pi_node)) == DI_PATH_STATE_OFFLINE) {
491 		clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
492 	}
493 
494 	if (pi_state == DI_PATH_STATE_FAULT) {
495 		clp->ap_cond = CFGA_COND_FAILED;
496 	} else {
497 		clp->ap_cond = CFGA_COND_UNKNOWN;
498 	}
499 
500 	/* no way to determine state change */
501 	clp->ap_busy = 0;
502 	clp->ap_status_time = (time_t)-1;
503 
504 	/* Link it in */
505 	listp->next = lap->listp;
506 	lap->listp = listp;
507 
508 	return (SCFGA_OK);
509 }
510 
511 /*
512  * Routine to stat pathinfo nodes.
513  *
514  * No pathinfo founds returns a success.
515  * When cmd is SCFGA_STAT_DEV, finds a matching pathinfo node and
516  * and create ldata if found.
517  * When cmd is SCFGA_STAT_ALL, create ldata for each pathinfo node.
518  * When cmd is SCFGA_STAT_BUS, checks if any pathinfo exist.
519  *
520  * Return:
521  *  0 for success
522  *  -1 for failure.
523  */
524 int
stat_path_info(di_node_t root,void * arg,int * l_errnop)525 stat_path_info(
526 	di_node_t 	root,
527 	void		*arg,
528 	int 		*l_errnop)
529 {
530 	scfga_list_t	*lap = (scfga_list_t *)arg;
531 	di_path_t	pi_node;
532 
533 	if (root == DI_NODE_NIL) {
534 		return (-1);
535 	}
536 
537 	/*
538 	 * when there is no path_info node return SCFGA_OK.
539 	 */
540 	if (di_path_next_client(root, DI_PATH_NIL) == DI_PATH_NIL) {
541 		return (0);
542 	}
543 
544 	if (lap->cmd == SCFGA_STAT_BUS) {
545 		lap->chld_config = CFGA_STAT_CONFIGURED;
546 		return (0);
547 	} else if (lap->cmd == SCFGA_STAT_DEV) {
548 		assert(lap->apidp->dyntype == PATH_APID);
549 		for (pi_node = di_path_next_client(root, DI_PATH_NIL); pi_node;
550 		    pi_node = di_path_next_client(root, pi_node)) {
551 			/*
552 			 * NOTE: apidt_create() validated pathinfo apid so
553 			 * the apid should have a valid format.
554 			 */
555 
556 			/* check the length first. */
557 			if (strlen(di_path_bus_addr(pi_node)) !=
558 			    strlen(lap->apidp->dyncomp)) {
559 				continue;
560 			}
561 
562 			/* check for full match. */
563 			if (strcmp(di_path_bus_addr(pi_node),
564 			    lap->apidp->dyncomp)) {
565 				continue;
566 			}
567 
568 			/* found match, record information */
569 			if (create_pathinfo_ldata(pi_node, lap,
570 			    l_errnop) == SCFGA_OK) {
571 				lap->ret = SCFGA_OK;
572 				return (0);
573 			} else {
574 				return (-1);
575 			}
576 		}
577 	} else { /* cmd = STAT_ALL */
578 		/* set child config to configured */
579 		lap->chld_config = CFGA_STAT_CONFIGURED;
580 		for (pi_node = di_path_next_client(root, DI_PATH_NIL); pi_node;
581 		    pi_node = di_path_next_client(root, pi_node)) {
582 			/* continue on even if there is an error on one path. */
583 			(void) create_pathinfo_ldata(pi_node, lap, l_errnop);
584 		}
585 	}
586 
587 	lap->ret = SCFGA_OK;
588 	return (0);
589 
590 }
591 
592 struct bus_state {
593 	int	b_state;
594 	int	b_retired;
595 	char	iconnect_type[16];
596 };
597 
598 static scfga_ret_t
do_stat_bus(scfga_list_t * lap,int limited_bus_stat)599 do_stat_bus(scfga_list_t *lap, int limited_bus_stat)
600 {
601 	cfga_list_data_t *clp = NULL;
602 	ldata_list_t *listp = NULL;
603 	int l_errno = 0;
604 	struct bus_state bstate = {0};
605 	walkarg_t u;
606 	scfga_ret_t ret;
607 	int i;
608 	char itypelower[MAXNAMELEN];
609 
610 	assert(lap->hba_logp != NULL);
611 
612 	/* Get bus state */
613 	u.node_args.flags = 0;
614 	u.node_args.fcn = get_bus_state;
615 
616 	ret = walk_tree(lap->apidp->hba_phys, &bstate, DINFOPROP, &u,
617 	    SCFGA_WALK_NODE, &l_errno);
618 	if (ret == SCFGA_OK) {
619 		lap->hba_rstate = bus_devinfo_to_recep_state(bstate.b_state);
620 	} else {
621 		lap->hba_rstate = CFGA_STAT_NONE;
622 	}
623 
624 	if (limited_bus_stat) {
625 		/* We only want to know bus(receptacle) connect status */
626 		return (SCFGA_OK);
627 	}
628 
629 	listp = calloc(1, sizeof (ldata_list_t));
630 	if (listp == NULL) {
631 		lap->l_errno = errno;
632 		return (SCFGA_LIB_ERR);
633 	}
634 
635 	clp = &listp->ldata;
636 
637 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s",
638 	    lap->hba_logp);
639 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s",
640 	    lap->apidp->hba_phys);
641 
642 	clp->ap_class[0] = '\0';	/* Filled by libcfgadm */
643 	clp->ap_r_state = lap->hba_rstate;
644 	clp->ap_o_state = CFGA_STAT_NONE; /* filled in later by the plug-in */
645 	clp->ap_cond =
646 	    (bstate.b_retired) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN;
647 	clp->ap_busy = 0;
648 	clp->ap_status_time = (time_t)-1;
649 	clp->ap_info[0] = '\0';
650 
651 	if (bstate.iconnect_type) {
652 		/*
653 		 * For SPI type, keep the existing SCFGA_BUS_TYPE.
654 		 * For other types, the ap type will be scsi-'interconnct-type'.
655 		 */
656 		if (strcmp(bstate.iconnect_type, "SPI") == 0) {
657 			(void) snprintf(clp->ap_type, sizeof (clp->ap_type),
658 			    "%s", SCFGA_BUS_TYPE);
659 		} else {
660 			for (i = 0; i < strlen(bstate.iconnect_type); i++) {
661 				itypelower[i] =
662 				    tolower(bstate.iconnect_type[i]);
663 			}
664 			itypelower[i] = '\0';
665 			(void) snprintf(clp->ap_type, sizeof (clp->ap_type),
666 			    "%s-%s", "scsi", itypelower);
667 		}
668 	}
669 
670 	/* Link it in */
671 	listp->next = lap->listp;
672 	lap->listp = listp;
673 
674 	return (SCFGA_OK);
675 }
676 
677 static int
get_bus_state(di_node_t node,void * arg)678 get_bus_state(di_node_t node, void *arg)
679 {
680 	struct bus_state *bsp = (struct bus_state *)arg;
681 	char *itype = NULL;
682 
683 	bsp->b_state = di_state(node);
684 	bsp->b_retired = di_retired(node);
685 	(void) di_prop_lookup_strings(DDI_DEV_T_ANY,
686 	    node, "initiator-interconnect-type", &itype);
687 	if (itype != NULL) {
688 		(void) strlcpy(bsp->iconnect_type, itype, 16);
689 	} else {
690 		bsp->iconnect_type[0] = '\0';
691 	}
692 
693 	return (DI_WALK_TERMINATE);
694 }
695 
696 static scfga_ret_t
do_stat_dev(const di_node_t node,const char * nodepath,scfga_list_t * lap,int limited_dev_stat)697 do_stat_dev(
698 	const di_node_t node,
699 	const char *nodepath,
700 	scfga_list_t *lap,
701 	int limited_dev_stat)
702 {
703 	uint_t devinfo_state = 0;
704 	char *dyncomp = NULL;
705 	cfga_list_data_t *clp = NULL;
706 	ldata_list_t *listp = NULL;
707 	cfga_stat_t ostate;
708 	scfga_ret_t ret;
709 
710 	assert(lap->apidp->hba_phys != NULL);
711 	assert(lap->hba_logp != NULL);
712 
713 	devinfo_state = di_state(node);
714 	ostate = dev_devinfo_to_occupant_state(devinfo_state);
715 
716 	/* If child device is configured, record it */
717 	if (ostate == CFGA_STAT_CONFIGURED) {
718 		lap->chld_config = CFGA_STAT_CONFIGURED;
719 	}
720 
721 	if (limited_dev_stat) {
722 		/* We only want to know device config state */
723 		return (SCFGA_OK);
724 	}
725 
726 	listp = calloc(1, sizeof (ldata_list_t));
727 	if (listp == NULL) {
728 		lap->l_errno = errno;
729 		return (SCFGA_LIB_ERR);
730 	}
731 
732 	clp = &listp->ldata;
733 
734 	/* Create the dynamic component */
735 	ret = make_dyncomp(node, nodepath, &dyncomp, &lap->l_errno);
736 	if (ret != SCFGA_OK) {
737 		S_FREE(listp);
738 		return (ret);
739 	}
740 
741 	assert(dyncomp != NULL);
742 
743 	/* Create logical and physical ap_id */
744 	(void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
745 	    lap->hba_logp, DYN_SEP, dyncomp);
746 
747 	(void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
748 	    lap->apidp->hba_phys, DYN_SEP, dyncomp);
749 
750 	S_FREE(dyncomp);
751 
752 	clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
753 	clp->ap_r_state = lap->hba_rstate;
754 	clp->ap_o_state = ostate;
755 	clp->ap_cond = di_retired(node) ? CFGA_COND_FAILED : CFGA_COND_UNKNOWN;
756 	clp->ap_busy = 0; /* no way to determine state change */
757 	clp->ap_status_time = (time_t)-1;
758 
759 	get_hw_info(node, clp, DEV_APID);
760 
761 	/* Link it in */
762 	listp->next = lap->listp;
763 	lap->listp = listp;
764 
765 	return (SCFGA_OK);
766 }
767 
768 /* fill in device type, vid, pid from properties */
769 static void
get_hw_info(di_node_t node,cfga_list_data_t * clp,dyncomp_t type)770 get_hw_info(di_node_t node, cfga_list_data_t *clp, dyncomp_t type)
771 {
772 	char *cp = NULL;
773 	char *inq_vid, *inq_pid;
774 	char client_inst[MAXNAMELEN];
775 
776 	/*
777 	 * Fill in type information
778 	 */
779 	cp = (char *)get_device_type(node, type);
780 	if (cp == NULL) {
781 		cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
782 	}
783 	(void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s", S_STR(cp));
784 
785 	if (type == DEV_APID) {
786 		/*
787 		 * Fill in vendor and product ID.
788 		 */
789 		if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
790 		    "inquiry-product-id", &inq_pid) == 1) &&
791 		    (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
792 		    "inquiry-vendor-id", &inq_vid) == 1)) {
793 			(void) snprintf(clp->ap_info, sizeof (clp->ap_info),
794 			    "%s %s", inq_vid, inq_pid);
795 		}
796 	} else {
797 		if ((di_driver_name(node) != NULL) &&
798 		    (di_instance(node) != -1)) {
799 			if (clp->ap_info == NULL) {
800 				(void) snprintf(client_inst, MAXNAMELEN - 1,
801 				    "%s%d", di_driver_name(node),
802 				    di_instance(node));
803 				(void) snprintf(clp->ap_info, MAXNAMELEN - 1,
804 				    "Client Device: %s", client_inst);
805 			} else {
806 				(void) snprintf(client_inst, MAXNAMELEN - 1,
807 				    "(%s%d)", di_driver_name(node),
808 				    di_instance(node));
809 				(void) strlcat(clp->ap_info, client_inst,
810 				    CFGA_INFO_LEN);
811 			}
812 		}
813 
814 	}
815 }
816 
817 /*
818  * Get dtype from "inquiry-device-type" property. If not present,
819  * derive it from minor node type
820  */
821 static char *
get_device_type(di_node_t node,dyncomp_t type)822 get_device_type(di_node_t node, dyncomp_t type)
823 {
824 	char *name = NULL;
825 	int *inq_dtype;
826 	int i;
827 
828 	if (di_prop_find(DDI_DEV_T_ANY, node, "smp-device") != DI_PROP_NIL) {
829 		return ("smp");
830 	}
831 
832 	/* first, derive type based on inquiry property */
833 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
834 	    &inq_dtype) == 1) {
835 		int itype = (*inq_dtype) & DTYPE_MASK;
836 
837 		for (i = 0; i < N_DEVICE_TYPES; i++) {
838 			if (device_list[i].itype == DTYPE_UNKNOWN)
839 				continue;
840 			if (itype == device_list[i].itype) {
841 				name = (type == DEV_APID) ?
842 				    (char *)device_list[i].name :
843 				    (char *)device_list[i].pathname;
844 				break;
845 			}
846 		}
847 	}
848 
849 	/* if property fails, use minor nodetype */
850 	if (name == NULL) {
851 		char *nodetype;
852 		di_minor_t minor = di_minor_next(node, DI_MINOR_NIL);
853 
854 		if ((minor != DI_MINOR_NIL) &&
855 		    ((nodetype = di_minor_nodetype(minor)) != NULL)) {
856 			for (i = 0; i < N_DEVICE_TYPES; i++) {
857 				if (device_list[i].ntype &&
858 				    (strcmp(nodetype, device_list[i].ntype)
859 				    == 0)) {
860 					name = (type == DEV_APID) ?
861 					    (char *)device_list[i].name :
862 					    (char *)device_list[i].pathname;
863 					break;
864 				}
865 			}
866 		}
867 	}
868 
869 	if (name == NULL)	/* default to unknown */
870 		name = "unknown";
871 	return (name);
872 }
873 
874 /* Transform linked list into an array */
875 scfga_ret_t
list_ext_postprocess(ldata_list_t ** llpp,int nelem,cfga_list_data_t ** ap_id_list,int * nlistp,char ** errstring)876 list_ext_postprocess(
877 	ldata_list_t		**llpp,
878 	int			nelem,
879 	cfga_list_data_t	**ap_id_list,
880 	int			*nlistp,
881 	char			**errstring)
882 {
883 	cfga_list_data_t *ldatap = NULL;
884 	ldata_list_t *tmplp = NULL;
885 	int i = -1;
886 
887 	*ap_id_list = NULL;
888 	*nlistp = 0;
889 
890 	if (*llpp == NULL || nelem < 0) {
891 		return (SCFGA_LIB_ERR);
892 	}
893 
894 	if (nelem == 0) {
895 		return (SCFGA_APID_NOEXIST);
896 	}
897 
898 	ldatap = calloc(nelem, sizeof (cfga_list_data_t));
899 	if (ldatap == NULL) {
900 		cfga_err(errstring, errno, ERR_LIST, 0);
901 		return (SCFGA_LIB_ERR);
902 	}
903 
904 	/* Extract the list_data structures from the linked list */
905 	tmplp = *llpp;
906 	for (i = 0; i < nelem && tmplp != NULL; i++) {
907 		ldatap[i] = tmplp->ldata;
908 		tmplp = tmplp->next;
909 	}
910 
911 	if (i < nelem || tmplp != NULL) {
912 		S_FREE(ldatap);
913 		return (SCFGA_LIB_ERR);
914 	}
915 
916 	*nlistp = nelem;
917 	*ap_id_list = ldatap;
918 
919 	return (SCFGA_OK);
920 }
921 
922 /*
923  * Convert bus state to receptacle state
924  */
925 static cfga_stat_t
bus_devinfo_to_recep_state(uint_t bus_di_state)926 bus_devinfo_to_recep_state(uint_t bus_di_state)
927 {
928 	if (bus_di_state & (DI_BUS_QUIESCED | DI_BUS_DOWN))
929 		return (CFGA_STAT_DISCONNECTED);
930 
931 	return (CFGA_STAT_CONNECTED);
932 }
933 
934 /*
935  * Convert device state to occupant state
936  */
937 static cfga_stat_t
dev_devinfo_to_occupant_state(uint_t dev_di_state)938 dev_devinfo_to_occupant_state(uint_t dev_di_state)
939 {
940 	if (dev_di_state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN))
941 		return (CFGA_STAT_UNCONFIGURED);
942 
943 	if (!(dev_di_state & DI_DRIVER_DETACHED))
944 		return (CFGA_STAT_CONFIGURED);
945 
946 	return (CFGA_STAT_NONE);
947 }
948