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