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"
30 * Implementation to interact with libdevinfo to find port nodes,
31 * and information regarding each node (fru, port, location).
35 #include <libdevinfo.h>
40 #include <config_admin.h>
41 #include <sys/types.h>
42 #include <sys/obpdefs.h>
45 #include "piclfrutree.h"
49 static di_prom_handle_t prom_handle
= DI_PROM_HANDLE_NIL
;
50 extern int frutree_debug
;
54 char bus_addr
[PICL_PROPNAMELEN_MAX
];
55 char path
[PICL_PROPNAMELEN_MAX
];
60 typedef struct p_info
{
61 frutree_port_type_t type
;
66 char devfs_path
[MAXPATHLEN
];
79 free_list(plist_t
*listptr
)
86 nextptr
= listptr
->first
;
87 while (nextptr
!= NULL
) {
89 nextptr
= nextptr
->next
;
94 /* (callback function for qsort) compare the bus_addr */
96 compare(const void *a
, const void *b
)
98 port_info_t
*pinfo1
, *pinfo2
;
99 port_info_t
**ptr2pinfo1
, **ptr2pinfo2
;
101 ptr2pinfo1
= (port_info_t
**)a
;
102 ptr2pinfo2
= (port_info_t
**)b
;
104 pinfo1
= (port_info_t
*)*ptr2pinfo1
;
105 pinfo2
= (port_info_t
*)*ptr2pinfo2
;
106 return (strcmp(pinfo1
->bus_addr
, pinfo2
->bus_addr
));
110 * assigns GeoAddr property for ports based on bus-addr
113 assign_geo_addr(plist_t
*list
, frutree_port_type_t type
)
117 port_info_t
**port_info
= NULL
;
118 port_info_t
*nextptr
= NULL
;
122 return (PICL_FAILURE
);
125 if (list
->first
== NULL
) {
126 return (PICL_SUCCESS
);
131 if (list
->n_serial
== 0) {
132 return (PICL_SUCCESS
);
134 num_ports
= list
->n_serial
;
138 if (list
->n_parallel
== 0) {
139 return (PICL_SUCCESS
);
141 num_ports
= list
->n_parallel
;
145 if (list
->n_network
== 0) {
146 return (PICL_SUCCESS
);
148 num_ports
= list
->n_network
;
153 port_info
= (port_info_t
**)malloc(
154 sizeof (port_info_t
*) * num_ports
);
155 if (port_info
== NULL
) {
156 return (PICL_NOSPACE
);
159 /* traverse thru list and look for ports of given type */
160 nextptr
= list
->first
;
161 while (nextptr
!= NULL
) {
162 if (nextptr
->type
!= type
) {
163 nextptr
= nextptr
->next
;
166 port_info
[i
] = nextptr
;
167 nextptr
= nextptr
->next
;
171 /* sort the nodes to assign geo_address */
172 (void) qsort((void *)port_info
, num_ports
,
173 sizeof (port_info_t
*), compare
);
174 for (i
= 0; i
< num_ports
; i
++) {
175 if (port_info
[i
] != NULL
) {
176 port_info
[i
]->geo_addr
= i
+ 1;
180 return (PICL_SUCCESS
);
184 create_port_config_info(plist_t
*list
, frutree_device_args_t
*devp
)
186 port_info_t
*port_info
= NULL
;
187 frutree_cache_t
*cachep
= NULL
;
188 char port_type
[PICL_PROPNAMELEN_MAX
];
189 char label
[PICL_PROPNAMELEN_MAX
];
192 return (PICL_FAILURE
);
195 port_info
= list
->first
;
196 while (port_info
!= NULL
) {
198 cachep
= (frutree_cache_t
*)malloc(sizeof (frutree_cache_t
));
199 if (cachep
== NULL
) {
200 return (PICL_NOSPACE
);
203 switch (port_info
->type
) {
205 (void) strncpy(label
, SANIBEL_NETWORK_LABEL
,
207 (void) strncpy(port_type
, SANIBEL_NETWORK_PORT
,
211 (void) strncpy(label
, SANIBEL_PARALLEL_PORT
,
213 (void) strncpy(port_type
, SANIBEL_PARALLEL_PORT
,
217 (void) strncpy(label
, SANIBEL_SERIAL_PORT
,
219 (void) strncpy(port_type
, SANIBEL_SERIAL_PORT
,
223 port_info
= port_info
->next
;
225 cachep
->buf
[0] = '\0';
227 (void) snprintf(cachep
->buf
,
228 sizeof (cachep
->buf
),
230 "\t%s %s %s %s 0 \"%s %d\"\n"
231 "\t%s %s %s %s 0 \"%s\"\n"
232 "\t%s %s %s %s 1 %d\n"
233 "\t%s %s %s %s 0 \"%s\"\n"
234 "\t%s %s %s %s 0 \"%s\"\n"
236 "NODE", port_info
->drv_name
, port_info
->instance
,
238 "PROP", PICL_PROP_LABEL
, "string", "r",
239 label
, (port_info
->geo_addr
-1),
240 "PROP", PICL_PROP_BUS_ADDR
, "string",
241 "r", port_info
->bus_addr
,
242 "PROP", PICL_PROP_GEO_ADDR
, "uint",
243 "r", port_info
->geo_addr
,
244 "PROP", PICL_PROP_PORT_TYPE
, "string",
246 "PROP", PICL_PROP_DEVFS_PATH
, "string",
247 "r", port_info
->devfs_path
,
250 /* add to the cache */
251 if (devp
->first
== NULL
) { /* 1st node */
252 devp
->first
= cachep
;
254 } else if (devp
->last
!= NULL
) { /* last node */
255 devp
->last
->next
= cachep
;
257 } else { /* 2nd node */
258 devp
->first
->next
= cachep
;
261 port_info
= port_info
->next
; /* advance to next node */
263 return (PICL_SUCCESS
);
268 load_driver(di_node_t node
, void *arg
)
270 char *drv_name
= NULL
;
271 char cmd
[MAXPATHLEN
];
273 if (di_node_state(node
) >= DS_ATTACHED
) {
274 return (DI_WALK_CONTINUE
);
276 drv_name
= di_driver_name(node
);
277 if (drv_name
== NULL
) {
278 return (DI_WALK_CONTINUE
);
281 (void) snprintf(cmd
, sizeof (cmd
), "%s %s",
282 DEVFSADM_CMD
, drv_name
);
283 (void) pclose(popen(cmd
, "r"));
284 return (DI_WALK_CONTINUE
);
288 load_drivers(char *path
)
292 return (PICL_INVALIDARG
);
295 rnode
= di_init(path
, DINFOSUBTREE
|DINFOMINOR
);
296 if (rnode
== DI_NODE_NIL
) {
297 return (PICL_FAILURE
);
300 if (di_walk_node(rnode
, DI_WALK_CLDFIRST
, NULL
, load_driver
) != 0) {
302 return (PICL_FAILURE
);
306 return (PICL_SUCCESS
);
310 * probe for port nodes
313 probe_tree(di_node_t node
, void *arg
)
315 char *nodetype
= NULL
;
316 char *devfs_path
= NULL
;
317 char *bus_addr
= NULL
;
318 char *drv_name
= NULL
;
319 plist_t
*listptr
= NULL
;
320 port_info_t
*port_info
= NULL
;
321 frutree_port_type_t port_type
= UNKNOWN_PORT
;
322 di_minor_t minor
= DI_MINOR_NIL
;
325 return (DI_WALK_TERMINATE
);
327 listptr
= (plist_t
*)arg
;
329 while ((minor
= di_minor_next(node
, minor
)) != DI_MINOR_NIL
) {
330 nodetype
= di_minor_nodetype(minor
);
331 if (nodetype
== NULL
) {
335 if (strcmp(nodetype
, DDI_NT_NET
) == 0) {
336 port_type
= NETWORK_PORT
;
337 } else if (strcmp(nodetype
, DDI_NT_PARALLEL
) == 0) {
338 port_type
= PARALLEL_PORT
;
339 } else if ((strcmp(nodetype
, DDI_NT_SERIAL
) == 0) ||
340 (strcmp(nodetype
, DDI_NT_SERIAL_MB
) == 0) ||
341 (strcmp(nodetype
, DDI_NT_SERIAL_DO
) == 0) ||
342 (strcmp(nodetype
, DDI_NT_SERIAL_MB_DO
) == 0)) {
343 port_type
= SERIAL_PORT
;
348 /* found port node */
349 devfs_path
= di_devfs_path(node
);
350 if (devfs_path
== NULL
) {
354 bus_addr
= di_bus_addr(node
);
355 drv_name
= di_driver_name(node
);
357 if ((bus_addr
== NULL
) || (drv_name
== NULL
)) {
358 di_devfs_path_free(devfs_path
);
362 port_info
= malloc(sizeof (port_info_t
));
363 if (port_info
== NULL
) {
364 di_devfs_path_free(devfs_path
);
365 return (PICL_NOSPACE
);
368 (void) strncpy(port_info
->devfs_path
, devfs_path
,
369 sizeof (port_info
->devfs_path
));
370 (void) strncpy(port_info
->bus_addr
, bus_addr
,
371 sizeof (port_info
->bus_addr
));
372 (void) strncpy(port_info
->drv_name
, drv_name
,
373 sizeof (port_info
->drv_name
));
374 port_info
->type
= port_type
;
375 port_info
->instance
= di_instance(node
);
376 port_info
->geo_addr
= -1;
377 port_info
->next
= NULL
;
381 listptr
->n_network
++;
387 listptr
->n_parallel
++;
391 /* add to the list */
392 if (listptr
->first
== NULL
) { /* 1st node */
393 listptr
->first
= port_info
;
394 listptr
->last
= NULL
;
395 } else if (listptr
->last
!= NULL
) { /* last node */
396 listptr
->last
->next
= port_info
;
397 listptr
->last
= port_info
;
398 } else { /* 2nd node */
399 listptr
->first
->next
= port_info
;
400 listptr
->last
= port_info
;
402 di_devfs_path_free(devfs_path
);
403 return (DI_WALK_CONTINUE
);
405 return (DI_WALK_CONTINUE
);
408 /* This routine probes libdevinfo for port nodes */
410 probe_libdevinfo(frutree_frunode_t
*frup
, frutree_device_args_t
** device
,
418 return (PICL_FAILURE
);
420 FRUTREE_DEBUG1(EVENTS
, "loading drivers for %s", frup
->name
);
422 if (load_drv
== B_TRUE
) {
423 if ((rc
= load_drivers(frup
->fru_path
)) != PICL_SUCCESS
) {
427 FRUTREE_DEBUG1(EVENTS
, "done with loading drivers for %s", frup
->name
);
429 rnode
= di_init(frup
->fru_path
, DINFOSUBTREE
|DINFOMINOR
);
430 if (rnode
== DI_NODE_NIL
) {
431 return (PICL_FAILURE
);
440 if (di_walk_node(rnode
, DI_WALK_CLDFIRST
, &list
, probe_tree
) != 0) {
443 return (PICL_FAILURE
);
446 if (list
.n_serial
> 0)
447 if ((rc
= assign_geo_addr(&list
, SERIAL_PORT
)) != PICL_SUCCESS
) {
453 if (list
.n_network
> 0)
454 if ((rc
= assign_geo_addr(&list
, NETWORK_PORT
)) != PICL_SUCCESS
) {
460 if (list
.n_parallel
> 0)
461 if ((rc
= assign_geo_addr(&list
, PARALLEL_PORT
)) != PICL_SUCCESS
) {
467 if ((rc
= create_port_config_info(&list
, *device
)) != PICL_SUCCESS
) {
475 FRUTREE_DEBUG1(EVENTS
, "done with probing %s", frup
->name
);
476 return (PICL_SUCCESS
);
480 get_reg_dev(di_node_t node
)
483 if (di_prop_lookup_ints(DDI_DEV_T_ANY
, node
, OBP_REG
, ®
) < 0) {
484 if (di_prom_prop_lookup_ints(prom_handle
, node
, OBP_REG
,
488 return (PCI_REG_DEV_G(reg
[0]));
490 return (PCI_REG_DEV_G(reg
[0]));
494 walk_tree(di_node_t node
, void *arg
)
497 char *bus_addr
= NULL
;
498 char *char_di_bus_addr
= NULL
;
501 char *node_name
= NULL
;
502 frutree_devinfo_t
*devinfo
;
503 frutree_frunode_t
*frup
= NULL
;
505 devinfo
= *(frutree_devinfo_t
**)arg
;
506 frup
= (frutree_frunode_t
*)devinfo
->arg
;
508 return (DI_WALK_TERMINATE
);
511 if (devinfo
->rnode
== node
) { /* skip the root node */
512 return (DI_WALK_CONTINUE
);
514 bus_addr
= devinfo
->bus_addr
;
516 char_di_bus_addr
= di_bus_addr(node
);
517 if (char_di_bus_addr
== NULL
) {
519 * look for reg property
520 * This applies to only cPCI devices
522 if (strstr(bus_addr
, ",") != NULL
) {
523 /* bus addr is of type 1,0 */
524 /* we dont handle this case yet */
525 return (DI_WALK_PRUNECHILD
);
527 di_busaddr
= get_reg_dev(node
);
528 if (di_busaddr
== -1) {
529 /* reg prop not found */
530 return (DI_WALK_PRUNECHILD
);
533 /* check if the bus addresses are same */
535 busaddr
= strtol(bus_addr
, (char **)NULL
, 16);
537 return (DI_WALK_TERMINATE
);
539 if (di_busaddr
!= busaddr
) {
540 return (DI_WALK_PRUNECHILD
);
543 /* build the fru path name */
544 /* parent_path/nodename@bus_addr */
545 node_name
= di_node_name(node
);
546 if (node_name
== NULL
) {
547 return (DI_WALK_TERMINATE
);
549 (void) snprintf(devinfo
->path
, sizeof (devinfo
->path
),
550 "%s/%s@%s", frup
->fru_path
, node_name
, bus_addr
);
551 return (DI_WALK_TERMINATE
);
554 if (strstr(bus_addr
, ",") != NULL
) { /* bus addr is of type 1,0 */
555 if (strcmp(bus_addr
, char_di_bus_addr
) != 0) {
556 return (DI_WALK_PRUNECHILD
);
558 } else { /* bus addr is of type 0x */
560 /* check if the values are same */
562 busaddr
= strtol(bus_addr
, (char **)NULL
, 16);
564 return (DI_WALK_TERMINATE
);
568 di_busaddr
= strtol(char_di_bus_addr
, (char **)NULL
, 16);
570 return (DI_WALK_TERMINATE
);
573 if (di_busaddr
!= busaddr
) {
574 return (DI_WALK_PRUNECHILD
);
579 path
= di_devfs_path(node
);
580 (void) strncpy(devinfo
->path
, path
, sizeof (devinfo
->path
));
581 di_devfs_path_free(path
);
582 return (DI_WALK_TERMINATE
);
586 get_fru_path(char *parent_path
, frutree_frunode_t
*frup
)
591 frutree_devinfo_t
*devinfo
= NULL
;
592 char slot_type
[PICL_PROPNAMELEN_MAX
];
593 char probe_path
[PICL_PROPNAMELEN_MAX
];
594 char bus_addr
[PICL_PROPNAMELEN_MAX
];
596 if ((rc
= ptree_get_propval_by_name(frup
->frunodeh
, PICL_PROP_PARENT
,
597 &loch
, sizeof (loch
))) != PICL_SUCCESS
) {
601 if ((rc
= ptree_get_propval_by_name(loch
, PICL_PROP_SLOT_TYPE
,
602 slot_type
, sizeof (slot_type
))) != PICL_SUCCESS
) {
606 if (strcmp(slot_type
, SANIBEL_SCSI_SLOT
) == 0 ||
607 strcmp(slot_type
, SANIBEL_IDE_SLOT
) == 0) {
608 if (ptree_get_propval_by_name(loch
, PICL_PROP_PROBE_PATH
,
609 probe_path
, sizeof (probe_path
)) != PICL_SUCCESS
) {
612 (void) strncpy(frup
->fru_path
, probe_path
,
613 sizeof (frup
->fru_path
));
614 return (PICL_SUCCESS
);
617 prom_handle
= di_prom_init();
618 rnode
= di_init(parent_path
, DINFOSUBTREE
|DINFOMINOR
);
619 if (rnode
== DI_NODE_NIL
) {
620 di_prom_fini(prom_handle
);
621 return (PICL_FAILURE
);
624 devinfo
= (frutree_devinfo_t
*)malloc(sizeof (frutree_devinfo_t
));
625 if (devinfo
== NULL
) {
627 di_prom_fini(prom_handle
);
628 return (PICL_NOSPACE
);
631 if (ptree_get_propval_by_name(loch
, PICL_PROP_BUS_ADDR
,
632 bus_addr
, sizeof (bus_addr
)) != PICL_SUCCESS
) {
635 di_prom_fini(prom_handle
);
639 devinfo
->rnode
= rnode
;
640 (void) strncpy(devinfo
->bus_addr
, bus_addr
, sizeof (devinfo
->bus_addr
));
641 devinfo
->path
[0] = '\0';
644 if (di_walk_node(rnode
, DI_WALK_SIBFIRST
, &devinfo
, walk_tree
) != 0) {
646 di_prom_fini(prom_handle
);
648 return (PICL_FAILURE
);
651 di_prom_fini(prom_handle
);
653 if (devinfo
->path
[0]) {
654 (void) strncpy(frup
->fru_path
, devinfo
->path
,
655 sizeof (frup
->fru_path
));
657 return (PICL_SUCCESS
);
660 return (PICL_NODENOTFOUND
);
665 find_fru_node(di_node_t node
, void *arg
)
667 frutree_locnode_t
*locp
= NULL
;
668 char *char_di_bus_addr
= NULL
;
671 char bus_addr
[PICL_PROPNAMELEN_MAX
];
672 frutree_devinfo_t
*devinfo
= NULL
;
674 devinfo
= *(frutree_devinfo_t
**)arg
;
675 locp
= *(frutree_locnode_t
**)devinfo
->arg
;
677 if (devinfo
->rnode
== node
) {
678 return (DI_WALK_CONTINUE
);
681 char_di_bus_addr
= di_bus_addr(node
);
682 if (char_di_bus_addr
== NULL
) {
683 return (DI_WALK_PRUNECHILD
);
686 if (ptree_get_propval_by_name(locp
->locnodeh
, PICL_PROP_BUS_ADDR
,
687 bus_addr
, sizeof (bus_addr
)) != PICL_SUCCESS
) {
688 return (DI_WALK_PRUNECHILD
);
691 if (strstr(bus_addr
, ",") != NULL
) {
692 /* bus addr is of type 1,0 */
693 if (strcmp(bus_addr
, char_di_bus_addr
) == 0) {
694 devinfo
->retval
= PICL_SUCCESS
;
695 return (DI_WALK_TERMINATE
);
697 return (DI_WALK_PRUNECHILD
);
699 } else { /* bus addr is of type 0x */
701 /* check if the values are same */
703 busaddr
= strtol(bus_addr
, (char **)NULL
, 16);
705 return (DI_WALK_PRUNECHILD
);
709 di_busaddr
= strtol(char_di_bus_addr
, (char **)NULL
, 16);
711 return (DI_WALK_PRUNECHILD
);
714 if (di_busaddr
== busaddr
) {
715 devinfo
->retval
= PICL_SUCCESS
;
716 return (DI_WALK_TERMINATE
);
718 return (DI_WALK_PRUNECHILD
);
724 * checks if a fru is present under location using pdev-path and busaddr
727 is_fru_present_under_location(frutree_locnode_t
*locp
)
730 frutree_devinfo_t
*devinfo
= NULL
;
731 char probe_path
[PICL_PROPNAMELEN_MAX
];
737 if (ptree_get_propval_by_name(locp
->locnodeh
, PICL_PROP_PROBE_PATH
,
738 probe_path
, sizeof (probe_path
)) != PICL_SUCCESS
) {
739 if (ptree_get_propval_by_name(locp
->locnodeh
,
740 PICL_PROP_DEVFS_PATH
, probe_path
,
741 sizeof (probe_path
)) != PICL_SUCCESS
) {
746 rnode
= di_init(probe_path
, DINFOSUBTREE
);
747 if (rnode
== DI_NODE_NIL
) {
752 devinfo
= (frutree_devinfo_t
*)malloc(sizeof (frutree_devinfo_t
));
753 if (devinfo
== NULL
) {
757 devinfo
->rnode
= rnode
;
758 devinfo
->arg
= (frutree_locnode_t
**)&locp
;
759 devinfo
->retval
= PICL_FAILURE
;
761 if (di_walk_node(rnode
, DI_WALK_SIBFIRST
, &devinfo
,
762 find_fru_node
) != 0) {
769 if (devinfo
->retval
== PICL_SUCCESS
) {
779 * initializes the port driver and instance fields based on libdevinfo
782 get_port_info(frutree_portnode_t
*portp
)
785 di_node_t rnode
, curr
, peer
;
786 char devfs_path
[PICL_PROPNAMELEN_MAX
];
787 char bus_addr
[PICL_PROPNAMELEN_MAX
];
788 char *di_busaddr
= NULL
, *di_drv
= NULL
;
789 int di_int_busaddr
, int_busaddr
;
791 if ((rc
= ptree_get_propval_by_name(portp
->portnodeh
,
792 PICL_PROP_DEVFS_PATH
, devfs_path
,
793 sizeof (devfs_path
))) != PICL_SUCCESS
) {
797 if (ptree_get_propval_by_name(portp
->portnodeh
, PICL_PROP_BUS_ADDR
,
798 bus_addr
, sizeof (bus_addr
)) != PICL_SUCCESS
) {
802 rnode
= di_init(devfs_path
, DINFOCPYALL
);
803 if (rnode
== DI_NODE_NIL
) {
804 return (PICL_FAILURE
);
807 peer
= di_child_node(rnode
);
808 while (peer
!= DI_NODE_NIL
) {
810 peer
= di_sibling_node(curr
);
812 di_busaddr
= di_bus_addr(curr
);
813 if (di_busaddr
== NULL
) {
817 /* compare the bus_addr */
818 if (strstr(bus_addr
, ",") != NULL
) {
819 /* bus addr is of type 1,0 */
820 if (strcmp(bus_addr
, di_busaddr
) != 0) {
823 } else { /* bus addr is of type 0x */
825 int_busaddr
= strtol(bus_addr
, (char **)NULL
, 16);
831 di_int_busaddr
= strtol(di_busaddr
, (char **)NULL
, 16);
836 if (di_int_busaddr
!= int_busaddr
) {
840 di_drv
= di_driver_name(curr
);
841 if (di_drv
== NULL
) {
843 return (PICL_FAILURE
);
845 /* initialize the driver name and instance number */
846 (void) strncpy(portp
->driver
, di_drv
, sizeof (portp
->driver
));
847 portp
->instance
= di_instance(curr
);
849 return (PICL_SUCCESS
);
852 return (PICL_NODENOTFOUND
);