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
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.
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]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
29 /* implementation specific to scsi nodes probing */
35 #include <sys/param.h>
36 #include <config_admin.h>
42 #include <libdevinfo.h>
43 #include <sys/types.h>
45 #include "piclfrutree.h"
47 #define SCSI_SLOT "scsi-bus"
48 #define SCSI_LOC_FORMAT "t%dd0"
49 #define TARGET "target"
53 #define SCSI_INITIATOR_ID 7
54 #define DRV_TYPE_DSK 1
55 #define DRV_TYPE_TAPE 2
56 #define NUM_DSK_TARGS 15
58 * No support for wide tapes for now.
59 * If required wide support, set this to 8
62 #define NUM_TAPE_TARGS 7
64 #define DIRLINK_DSK "dsk"
65 #define DIRLINK_RMT "rmt"
66 #define DRV_SCSI_DSK "sd"
67 #define DRV_SCSI_TAPE "st"
70 /* currently supported directory strings for SCSI FRUs in cfgadm APs */
71 static char *scsi_dirlink_names
[] = { DIRLINK_DSK
, DIRLINK_RMT
, NULL_ENTRY
};
72 /* currently supported SCSI FRU drivers */
73 static struct scsi_drv_info
{
78 DRV_SCSI_DSK
, NUM_DSK_TARGS
, DRV_TYPE_DSK
,
79 DRV_SCSI_TAPE
, NUM_TAPE_TARGS
, DRV_TYPE_TAPE
,
80 NULL_ENTRY
, NULL_ENTRY
, NULL_ENTRY
83 /* the following defs are based on defines in scsi cfgadm plugin */
84 #define CDROM "CD-ROM"
88 extern boolean_t
is_location_present_in_subtree(frutree_frunode_t
*,
89 const char *, const char *);
90 extern picl_errno_t
create_children(frutree_frunode_t
*, char *, char *,
91 int, char *, boolean_t
);
92 extern char *strtok_r(char *s1
, const char *s2
, char **lasts
);
93 extern boolean_t frutree_connects_initiated
;
94 extern int frutree_debug
;
98 cfga_list_data_t
*data
;
101 typedef struct linked_list
{
106 typedef struct scsi_info
{
107 frutree_frunode_t
*frup
;
108 cfga_list_data_t
*cfgalist
;
111 boolean_t compare_cfgadm
;
115 static plist_t
*scsi_list
= NULL
;
116 static cfga_list_data_t
*cfglist
= NULL
;
117 static int nlist
= 0;
120 free_list(plist_t
*list
)
122 node_t
*tmp
= NULL
, *tmp1
= NULL
;
127 while (tmp
!= NULL
) {
136 * This routine gets the list of scsi controllers present
139 populate_controllers_list(plist_t
*cntrl_list
, cfga_list_data_t
*list
, int num
)
142 node_t
*nodeptr
= NULL
;
143 cfga_list_data_t
*temp
= NULL
;
145 if (cntrl_list
== NULL
|| list
== NULL
) {
146 return (CFGA_ATTR_INVAL
);
149 cntrl_list
->first
= NULL
;
150 cntrl_list
->num_nodes
= 0;
156 for (i
= 0; i
< num
; i
++) {
157 if (strcmp(list
[i
].ap_type
, SCSI_SLOT
) != 0) {
161 /* scsi controller */
162 temp
= (cfga_list_data_t
*)malloc(sizeof (cfga_list_data_t
));
166 (void) memcpy(temp
, &list
[i
], sizeof (cfga_list_data_t
));
168 nodeptr
= (node_t
*)malloc(sizeof (node_t
));
169 if (nodeptr
== NULL
) {
173 nodeptr
->data
= temp
;
174 nodeptr
->next
= NULL
;
176 /* append to the list */
177 if (cntrl_list
->first
== NULL
) {
178 cntrl_list
->first
= nodeptr
;
179 cntrl_list
->num_nodes
++;
181 nodeptr
->next
= cntrl_list
->first
;
182 cntrl_list
->first
= nodeptr
;
183 cntrl_list
->num_nodes
++;
192 cfga_err_t ap_list_err
;
194 ap_list_err
= config_list_ext(0, NULL
, &cfglist
, &nlist
, NULL
,
195 NULL
, NULL
, CFGA_FLAG_LIST_ALL
);
197 if (ap_list_err
!= CFGA_OK
) {
198 if (ap_list_err
== CFGA_NOTSUPP
) {
199 return (PICL_SUCCESS
);
201 return (PICL_FAILURE
);
205 scsi_list
= (plist_t
*)malloc(sizeof (plist_t
));
206 if (scsi_list
== NULL
) {
208 return (PICL_NOSPACE
);
211 ap_list_err
= populate_controllers_list(scsi_list
, cfglist
, nlist
);
212 if (ap_list_err
!= CFGA_OK
) {
215 return (PICL_FAILURE
);
217 return (PICL_SUCCESS
);
224 free_list(scsi_list
);
229 * This routine searches the controllers list to find the mapping based
230 * on given devfs_path.
231 * caller should allocate memory for ap_id
234 find_scsi_controller(char *devfs_path
, plist_t
*list
, char *ap_id
)
239 char path
[MAXPATHLEN
];
241 if (devfs_path
== NULL
|| ap_id
== NULL
) {
242 return (PICL_INVALIDARG
);
244 (void) snprintf((char *)path
, sizeof (path
), "/devices%s", devfs_path
);
247 while (tmp
!= NULL
) {
248 lasts
= tmp
->data
->ap_phys_id
;
249 token
= (char *)strtok_r(lasts
, (const char *)":",
256 if (strcmp(path
, token
) == 0) { /* match found */
257 (void) strncpy(ap_id
, tmp
->data
->ap_log_id
,
259 return (PICL_SUCCESS
);
263 return (PICL_NODENOTFOUND
);
267 * This routine dynamically determines the cfgadm attachment point
268 * for a given devfspath and target id.
269 * memory for name should be allocated by the caller.
272 get_scsislot_name(char *devfs_path
, char *bus_addr
, char *name
)
278 cfga_err_t ap_list_err
;
279 cfga_list_data_t
*cfgalist
= NULL
;
280 char controller
[MAXPATHLEN
];
282 ap_list_err
= config_list_ext(0, NULL
, &cfgalist
,
283 &numlist
, NULL
, NULL
, NULL
, CFGA_FLAG_LIST_ALL
);
284 if (ap_list_err
!= CFGA_OK
) {
285 return (PICL_NODENOTFOUND
);
288 ap_list_err
= populate_controllers_list(&list
, cfgalist
,
290 if (ap_list_err
!= CFGA_OK
) {
293 return (PICL_NODENOTFOUND
);
296 if (list
.num_nodes
<= 0) {
298 return (PICL_NODENOTFOUND
);
301 if ((rc
= find_scsi_controller(devfs_path
, &list
,
302 controller
)) != PICL_SUCCESS
) {
307 target_id
= strtol(bus_addr
, (char **)NULL
, 16);
308 (void) sprintf(name
, "%s::dsk/%st%dd0", controller
,
309 controller
, target_id
);
312 return (PICL_SUCCESS
);
316 * Arg scsi_loc can be any of the following forms appearing in cfgadm output
323 * /devices/pci@1f,0/pci@1,1/scsi@2:scsi::dsk/c0t0d0
325 * On return, bus_addr contains the target id of the device.
326 * Please note that currently the target id is computed. It is better
327 * to eventually change this to getting from libdevinfo.
328 * Also, please note that SCSI_INITIATOR_ID should not
329 * be hardcoded, but should be dynamically retrieved from an OBP property.
332 get_bus_addr(char *scsi_loc
, char **bus_addr
)
334 char *ap
, *token
, *p
, *ap_idp
;
337 char addr
[BUF_SIZE
], ap_id
[BUF_SIZE
];
338 char fileinfo
[BUF_SIZE
], ap_id_link
[BUF_SIZE
];
340 (void) strncpy(ap_id
, scsi_loc
, sizeof (ap_id
));
341 ap
= strrchr(ap_id
, ':');
347 while (scsi_dirlink_names
[i
] && !len
) {
348 len
= strspn(ap
, scsi_dirlink_names
[i
++]);
350 * strspn may return positive len even when there is no
351 * complete string matches!!! hence the following check is
352 * necessary. So ensure the string match.
354 if (len
&& strstr(ap
, scsi_dirlink_names
[i
-1]))
362 while (scsi_drv
[i
].drv_name
&& !len
) {
363 len
= strspn(ap
, scsi_drv
[i
++].drv_name
);
364 if (len
&& strstr(ap
, scsi_drv
[i
-1].drv_name
))
370 if (strlen(ap
) && parse_link
) {
372 /* slice 0 must be present in the system */
373 if (strstr(ap
, "/c")) {
374 if (strstr(ap
, "s0") == NULL
)
375 (void) strcat(ap
, "s0");
377 /* get the devlink and read the target id from minor node */
378 (void) snprintf(ap_id_link
, sizeof (ap_id_link
), "/dev/%s",
380 (void) bzero(fileinfo
, sizeof (fileinfo
));
381 if (readlink(ap_id_link
, fileinfo
, sizeof (fileinfo
)) < 0)
385 ap
= strrchr(fileinfo
, '@');
388 token
= (char *)strtok_r(ap
, ",", &p
);
389 (void) strncpy(addr
, token
, sizeof (addr
));
391 int drv_inst
= atoi(token
);
392 int tmp_targ_id
= drv_inst
% scsi_drv
[i
-1].num_targets
;
393 int targ_id
= scsi_drv
[i
-1].drv_type
== DRV_TYPE_DSK
?
394 (tmp_targ_id
< SCSI_INITIATOR_ID
?
395 tmp_targ_id
: tmp_targ_id
+1):
396 DRV_TYPE_TAPE
? tmp_targ_id
: drv_inst
;
397 (void) snprintf(addr
, sizeof (addr
), "%d", targ_id
);
400 *bus_addr
= (char *)malloc(strlen(addr
)+1);
401 if ((*bus_addr
) == NULL
)
403 (void) strcpy((char *)*bus_addr
, addr
);
408 * This routine determines all the scsi nodes under a FRU and
409 * creates a subtree of all the scsi nodes with basic properties.
412 dyn_probe_for_scsi_frus(frutree_frunode_t
*frup
, cfga_list_data_t
*cfgalist
,
413 plist_t
*list
, int numlist
)
418 char *bus_addr
= NULL
;
419 char path
[MAXPATHLEN
];
420 char controller_name
[MAXPATHLEN
];
422 /* for each controller in the list, find if disk/fru is present */
424 while (curr
!= NULL
) {
425 /* compare the path */
426 (void) snprintf((char *)path
, sizeof (path
), "/devices%s",
428 if (strstr(curr
->data
->ap_phys_id
, path
) == NULL
) {
433 (void) snprintf(controller_name
, sizeof (controller_name
),
434 "%s::", curr
->data
->ap_log_id
);
436 for (i
= 0; i
< numlist
; i
++) {
437 if (strcmp(cfgalist
[i
].ap_type
, SCSI_SLOT
) == 0) {
440 if (strstr(cfgalist
[i
].ap_log_id
,
441 controller_name
) == NULL
) {
444 /* check if device is under fru */
445 if (strstr(cfgalist
[i
].ap_phys_id
, path
) == NULL
) {
449 /* we found a scsi fru */
451 /* check if the device is present in subtree */
452 if (is_location_present_in_subtree(frup
,
453 cfgalist
[i
].ap_log_id
, path
) == B_TRUE
) {
456 get_bus_addr(cfgalist
[i
].ap_log_id
, &bus_addr
);
457 if (bus_addr
== NULL
) {
460 rc
= create_children(frup
, cfgalist
[i
].ap_log_id
,
461 bus_addr
, geo_addr
, SANIBEL_SCSI_SLOT
, B_TRUE
);
463 if (rc
!= PICL_SUCCESS
) {
464 FRUTREE_DEBUG3(FRUTREE_INIT
, "SUNW_frutree:"
465 "Error in creating node %s under %s(error=%d)",
466 cfgalist
[i
].ap_log_id
, frup
->name
, rc
);
471 return (PICL_SUCCESS
);
475 * data used here is cached information (cfglist, nlist)
478 cache_probe_for_scsi_frus(frutree_frunode_t
*frup
)
483 char path
[MAXPATHLEN
];
484 char controller_name
[MAXPATHLEN
];
485 char *bus_addr
= NULL
;
487 /* for each controller in the list, find if disk/fru is present */
488 if (scsi_list
== NULL
) {
489 return (PICL_SUCCESS
);
491 curr
= scsi_list
->first
;
492 while (curr
!= NULL
) {
493 /* compare the path */
494 (void) snprintf((char *)path
, sizeof (path
), "/devices%s",
496 if (strstr(curr
->data
->ap_phys_id
, path
) == NULL
) {
500 (void) snprintf(controller_name
, sizeof (controller_name
),
501 "%s::", curr
->data
->ap_log_id
);
503 for (i
= 0; i
< nlist
; i
++) {
504 if (strcmp(cfglist
[i
].ap_type
, SCSI_SLOT
) == 0) {
507 if (strstr(cfglist
[i
].ap_log_id
,
508 controller_name
) == NULL
) {
511 /* check if the device is under fru */
512 if (strstr(cfglist
[i
].ap_phys_id
, path
) == NULL
) {
516 /* we found a scsi fru */
518 /* check if the device is present in subtree */
519 if (is_location_present_in_subtree(frup
,
520 cfglist
[i
].ap_log_id
, path
) == B_TRUE
) {
523 get_bus_addr(cfglist
[i
].ap_log_id
, &bus_addr
);
524 if (bus_addr
== NULL
) {
527 rc
= create_children(frup
, cfglist
[i
].ap_log_id
,
528 bus_addr
, geo_addr
, SANIBEL_SCSI_SLOT
, B_TRUE
);
530 if (rc
!= PICL_SUCCESS
) {
531 FRUTREE_DEBUG3(FRUTREE_INIT
, "SUNW_frutree:"
532 "Error in creating node %s under %s(error=%d)",
533 cfglist
[i
].ap_log_id
, frup
->name
, rc
);
538 return (PICL_SUCCESS
);
542 * This routine checks if the node (scsi device) is present in cfgadm data
544 * 1. traverse thru list of controllers and find
545 * the controller of interest
546 * 2. go thru list of devices under controller and compare if the target is same
548 * - device is already represented
550 * - The node must be repreented in PICL tree.
553 is_node_present(scsi_info_t
*scsi_info
, char *devfs_path
, int target
)
556 char path
[MAXPATHLEN
];
557 char controller
[MAXPATHLEN
];
558 char *bus_addr
= NULL
;
559 char *lasts
= NULL
, *token
= NULL
;
562 if (scsi_info
== NULL
) {
566 if (scsi_info
->list
== NULL
) {
570 (void) snprintf(path
, sizeof (path
), "/devices%s", devfs_path
);
572 curr
= scsi_info
->list
->first
;
573 while (curr
!= NULL
) {
575 lasts
= curr
->data
->ap_phys_id
;
576 token
= (char *)strtok_r(lasts
, (const char *)":",
583 if (strstr(path
, token
) == NULL
) {
584 /* this controller is not of interest */
589 (void) snprintf(controller
, sizeof (controller
), "%s::",
590 curr
->data
->ap_log_id
);
591 for (i
= 0; i
< scsi_info
->num_list
; i
++) {
592 if (strcmp(scsi_info
->cfgalist
[i
].ap_type
,
597 if (strstr(scsi_info
->cfgalist
[i
].ap_log_id
,
598 controller
) == NULL
) {
602 get_bus_addr(scsi_info
->cfgalist
[i
].ap_phys_id
,
605 * compare with target value
607 if (bus_addr
== NULL
) {
610 if (strtoul(bus_addr
, NULL
, 16) == target
) {
612 * this device is already represented
626 get_prop_by_name(di_node_t node
, char *name
)
628 di_prop_t prop
= DI_PROP_NIL
;
629 char *prop_name
= NULL
;
631 prop
= di_prop_next(node
, DI_PROP_NIL
);
632 while (prop
!= DI_PROP_NIL
) {
633 prop_name
= di_prop_name(prop
);
634 if (prop_name
!= NULL
) {
635 if (strcmp(prop_name
, name
) == 0) {
639 prop
= di_prop_next(node
, prop
);
641 return (DI_PROP_NIL
);
645 get_geoaddr(picl_nodehdl_t nodeh
, void *c_args
)
648 uint8_t *geo_addr
= NULL
;
649 char slot_type
[PICL_PROPNAMELEN_MAX
];
652 return (PICL_INVALIDARG
);
653 geo_addr
= (uint8_t *)c_args
;
655 if ((rc
= ptree_get_propval_by_name(nodeh
, PICL_PROP_SLOT_TYPE
,
656 slot_type
, sizeof (slot_type
))) != PICL_SUCCESS
) {
660 if (strcmp(slot_type
, SANIBEL_SCSI_SLOT
) == 0 ||
661 strcmp(slot_type
, SANIBEL_IDE_SLOT
) == 0) {
662 *geo_addr
= *geo_addr
+ 1;
664 return (PICL_WALK_CONTINUE
);
668 frutree_get_geoaddr(frutree_frunode_t
*frup
)
671 if (ptree_walk_tree_by_class(frup
->frunodeh
, PICL_CLASS_LOCATION
,
672 &geo_addr
, get_geoaddr
) != PICL_SUCCESS
) {
679 probe_disks(di_node_t node
, void *arg
)
683 int *target_val
= NULL
;
684 char *nodetype
= NULL
;
685 char *devfs_path
= NULL
;
686 char *bus_addr
= NULL
;
687 char *drv_name
= NULL
;
688 scsi_info_t
*data
= NULL
;
689 di_minor_t minor
= DI_MINOR_NIL
;
691 char node_name
[BUF_SIZE
];
692 char slot_type
[PICL_PROPNAMELEN_MAX
];
695 return (DI_WALK_TERMINATE
);
697 data
= *(scsi_info_t
**)arg
;
699 return (DI_WALK_TERMINATE
);
702 /* initialize the geo_addr value */
703 if (data
->geo_addr
== 0) {
704 if (data
->compare_cfgadm
== B_FALSE
) {
707 data
->geo_addr
= frutree_get_geoaddr(data
->frup
);
711 while ((minor
= di_minor_next(node
, minor
)) != DI_MINOR_NIL
) {
712 nodetype
= di_minor_nodetype(minor
);
713 if (nodetype
== NULL
) {
717 if (strcmp(nodetype
, DDI_NT_BLOCK_CHAN
) == 0 ||
718 strcmp(nodetype
, DDI_NT_BLOCK_WWN
) == 0) {
719 (void) snprintf(node_name
, sizeof (node_name
),
720 "%s%d", DISK
, data
->geo_addr
);
721 } else if (strcmp(nodetype
, DDI_NT_TAPE
) == 0) {
722 (void) snprintf(node_name
, sizeof (node_name
),
723 "%s%d", RMM
, data
->geo_addr
);
724 } else if (strcmp(nodetype
, DDI_NT_CD
) == 0 ||
725 strcmp(nodetype
, DDI_NT_CD_CHAN
) == 0) {
726 (void) snprintf(node_name
, sizeof (node_name
),
727 "%s%d", CDROM
, data
->geo_addr
);
732 devfs_path
= di_devfs_path(node
);
733 drv_name
= di_driver_name(node
);
734 bus_addr
= di_bus_addr(node
);
735 if (devfs_path
== NULL
) {
738 if (drv_name
== NULL
|| bus_addr
== NULL
) {
739 di_devfs_path_free(devfs_path
);
742 prop
= get_prop_by_name(node
, TARGET
);
743 if (prop
!= DI_PROP_NIL
) {
744 di_prop_ints(prop
, &target_val
);
745 if (data
->compare_cfgadm
) {
746 /* check if node is present in cfgadm data */
747 if (is_node_present(data
, devfs_path
,
748 *target_val
) == B_TRUE
) {
749 di_devfs_path_free(devfs_path
);
750 return (DI_WALK_CONTINUE
);
754 di_devfs_path_free(devfs_path
);
755 prop
= get_prop_by_name(node
, CLASS
);
756 if (prop
!= DI_PROP_NIL
) {
757 di_prop_strings(prop
, &class);
760 /* determine the slot type based on class code */
762 if (strcmp(class, DEVICE_CLASS_SCSI
) == 0) {
763 (void) strncpy(slot_type
,
766 } else if (strcmp(class,
767 DEVICE_CLASS_IDE
) == 0) {
768 (void) strncpy(slot_type
,
772 (void) strncpy(slot_type
,
773 SANIBEL_UNKNOWN_SLOT
,
778 (void) strncpy(slot_type
, SANIBEL_UNKNOWN_SLOT
,
782 if ((rc
= create_children(data
->frup
, node_name
,
783 bus_addr
, data
->geo_addr
, slot_type
,
784 B_FALSE
)) != PICL_SUCCESS
) {
787 /* increment the geo_addr */
790 di_devfs_path_free(devfs_path
);
793 return (DI_WALK_CONTINUE
);
795 return (DI_WALK_CONTINUE
);
799 probe_scsi_in_libdevinfo(frutree_frunode_t
*frup
, cfga_list_data_t
*cfgalist
,
800 plist_t
*list
, int num_list
, boolean_t compare_cfgadm
)
803 scsi_info_t
*scsi_data
= NULL
;
806 return (PICL_FAILURE
);
809 rnode
= di_init(frup
->fru_path
, DINFOCPYALL
);
810 if (rnode
== DI_NODE_NIL
) {
811 return (PICL_FAILURE
);
814 scsi_data
= (scsi_info_t
*)malloc(sizeof (scsi_info_t
));
815 if (scsi_data
== NULL
) {
817 return (PICL_NOSPACE
);
820 scsi_data
->frup
= frup
;
821 scsi_data
->cfgalist
= cfgalist
;
822 scsi_data
->list
= list
;
823 scsi_data
->num_list
= num_list
;
824 scsi_data
->compare_cfgadm
= compare_cfgadm
;
825 scsi_data
->geo_addr
= 0;
826 if (di_walk_node(rnode
, DI_WALK_CLDFIRST
, &scsi_data
,
830 return (PICL_FAILURE
);
835 return (PICL_SUCCESS
);
839 probe_for_scsi_frus(frutree_frunode_t
*frup
)
844 cfga_err_t ap_list_err
;
845 cfga_list_data_t
*cfgalist
= NULL
;
847 if (frutree_connects_initiated
== B_TRUE
) { /* probing after hotswap */
848 ap_list_err
= config_list_ext(0, NULL
, &cfgalist
,
849 &numlist
, NULL
, NULL
, NULL
, CFGA_FLAG_LIST_ALL
);
851 if (ap_list_err
!= CFGA_OK
) {
852 rc
= probe_scsi_in_libdevinfo(frup
, NULL
, NULL
,
857 /* get list of all controllers in the system */
858 ap_list_err
= populate_controllers_list(&list
, cfgalist
,
860 if (ap_list_err
!= CFGA_OK
) {
863 rc
= probe_scsi_in_libdevinfo(frup
, NULL
, NULL
,
868 /* no controllers found */
869 if (list
.num_nodes
<= 0) {
872 rc
= probe_scsi_in_libdevinfo(frup
, NULL
, NULL
,
877 * we have to fetch cfgadm, look for scsi controllers
880 (void) dyn_probe_for_scsi_frus(frup
, cfgalist
, &list
, numlist
);
881 rc
= probe_scsi_in_libdevinfo(frup
, cfgalist
, &list
,
887 /* during initialization */
888 /* use the cached cfgadm data */
889 rc
= cache_probe_for_scsi_frus(frup
);
890 if (scsi_list
&& scsi_list
->num_nodes
> 0) {
891 rc
= probe_scsi_in_libdevinfo(frup
, cfglist
,
892 scsi_list
, nlist
, B_TRUE
);
894 rc
= probe_scsi_in_libdevinfo(frup
, NULL
,