diff options
Diffstat (limited to 'usr/src/lib/libprtdiag/common/io.c')
| -rw-r--r-- | usr/src/lib/libprtdiag/common/io.c | 1067 |
1 files changed, 1067 insertions, 0 deletions
diff --git a/usr/src/lib/libprtdiag/common/io.c b/usr/src/lib/libprtdiag/common/io.c new file mode 100644 index 0000000000..f1bdb447cc --- /dev/null +++ b/usr/src/lib/libprtdiag/common/io.c @@ -0,0 +1,1067 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1999-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <kvm.h> +#include <varargs.h> +#include <errno.h> +#include <time.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/openpromio.h> +#include <sys/systeminfo.h> +#include <kstat.h> +#include <libintl.h> +#include <syslog.h> +#include <sys/dkio.h> +#include "pdevinfo.h" +#include "display.h" +#include "pdevinfo_sun4u.h" +#include "display_sun4u.h" +#include "libprtdiag.h" + +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + +Prom_node * +find_pci_bus(Prom_node *node, int id, int bus) +{ + Prom_node *pnode; + + /* find the first pci node */ + pnode = dev_find_node(node, "pci"); + + while (pnode != NULL) { + int tmp_id; + int tmp_bus; + + tmp_id = get_id(pnode); + tmp_bus = get_pci_bus(pnode); + + if ((tmp_id == id) && + (tmp_bus == bus)) { + break; + } + + pnode = dev_next_node(pnode, "pci"); + } + return (pnode); +} + +/* + * get_pci_bus + * + * Determines the PCI bus, either A (0) or B (1). If the function cannot + * find the bus-ranges property, it returns -1. + */ +int +get_pci_bus(Prom_node *pnode) +{ + int *value; + + /* look up the bus-range property */ + if ((value = (int *)get_prop_val(find_prop(pnode, "bus-range"))) == + NULL) { + return (-1); + } + + if (*value == 0) { + return (1); /* B bus has a bus-range value = 0 */ + } else { + return (0); + } +} + + + +/* + * Find the PCI device number of this PCI device. If no device number can + * be determined, then return -1. + */ +int +get_pci_device(Prom_node *pnode) +{ + void *value; + + if ((value = get_prop_val(find_prop(pnode, "assigned-addresses"))) != + NULL) { + return (PCI_DEVICE(*(int *)value)); + } else { + return (-1); + } +} + +/* + * Find the PCI device number of this PCI device. If no device number can + * be determined, then return -1. + */ +int +get_pci_to_pci_device(Prom_node *pnode) +{ + void *value; + + if ((value = get_prop_val(find_prop(pnode, "reg"))) != + NULL) { + return (PCI_DEVICE(*(int *)value)); + } else { + return (-1); + } +} + +/* + * free_io_cards + * Frees the memory allocated for an io card list. + */ +void +free_io_cards(struct io_card *card_list) +{ + /* Free the list */ + if (card_list != NULL) { + struct io_card *p, *q; + + for (p = card_list, q = NULL; p != NULL; p = q) { + q = p->next; + free(p); + } + } +} + + +/* + * insert_io_card + * Inserts an io_card structure into the list. The list is maintained + * in order based on board number and slot number. Also, the storage + * for the "card" argument is assumed to be handled by the caller, + * so we won't touch it. + */ +struct io_card * +insert_io_card(struct io_card *list, struct io_card *card) +{ + struct io_card *newcard; + struct io_card *p, *q; + + if (card == NULL) + return (list); + + /* Copy the card to be added into new storage */ + newcard = (struct io_card *)malloc(sizeof (struct io_card)); + if (newcard == NULL) { + perror("malloc"); + exit(2); + } + (void) memcpy(newcard, card, sizeof (struct io_card)); + newcard->next = NULL; + + if (list == NULL) + return (newcard); + + /* Find the proper place in the list for the new card */ + for (p = list, q = NULL; p != NULL; q = p, p = p->next) { + if (newcard->board < p->board) + break; + if ((newcard->board == p->board) && (newcard->slot < p->slot)) + break; + } + + /* Insert the new card into the list */ + if (q == NULL) { + newcard->next = p; + return (newcard); + } else { + newcard->next = p; + q->next = newcard; + return (list); + } +} + + +char * +fmt_manf_id(unsigned int encoded_id, char *outbuf) +{ + union manuf manuf; + + /* + * Format the manufacturer's info. Note a small inconsistency we + * have to work around - Brooktree has it's part number in decimal, + * while Mitsubishi has it's part number in hex. + */ + manuf.encoded_id = encoded_id; + switch (manuf.fld.manf) { + case MANF_BROOKTREE: + (void) sprintf(outbuf, "%s %d, version %d", "Brooktree", + manuf.fld.partno, manuf.fld.version); + break; + + case MANF_MITSUBISHI: + (void) sprintf(outbuf, "%s %x, version %d", "Mitsubishi", + manuf.fld.partno, manuf.fld.version); + break; + + default: + (void) sprintf(outbuf, "JED code %d, Part num 0x%x, version %d", + manuf.fld.manf, manuf.fld.partno, manuf.fld.version); + } + return (outbuf); +} + + +/* + * Find the sbus slot number of this Sbus device. If no slot number can + * be determined, then return -1. + */ +int +get_sbus_slot(Prom_node *pnode) +{ + void *value; + + if ((value = get_prop_val(find_prop(pnode, "reg"))) != NULL) { + return (*(int *)value); + } else { + return (-1); + } +} + + +/* + * This routine is the generic link into displaying system IO + * configuration. It displays the table header, then displays + * all the SBus cards, then displays all fo the PCI IO cards. + */ +void +display_io_devices(Sys_tree *tree) +{ + Board_node *bnode; + + /* + * TRANSLATION_NOTE + * Following string is used as a table header. + * Please maintain the current alignment in + * translation. + */ + log_printf("\n", 0); + log_printf("=========================", 0); + log_printf(dgettext(TEXT_DOMAIN, " IO Cards "), 0); + log_printf("=========================", 0); + log_printf("\n", 0); + log_printf("\n", 0); + bnode = tree->bd_list; + while (bnode != NULL) { + display_sbus(bnode); + display_pci(bnode); + display_ffb(bnode, 1); + bnode = bnode->next; + } +} + +void +display_pci(Board_node *bnode) +{ +#ifdef lint + bnode = bnode; +#endif + /* + * This function is intentionally empty + */ +} + + +/* + * Print out all the io cards in the list. Also print the column + * headers if told to do so. + */ +void +display_io_cards(struct io_card *list) +{ + static int banner = 0; /* Have we printed the column headings? */ + struct io_card *p; + + if (list == NULL) + return; + + if (banner == 0) { + log_printf(" Bus Freq\n", 0); + log_printf("Brd Type MHz Slot " + "Name " + "Model", 0); + log_printf("\n", 0); + log_printf("--- ---- ---- ---------- " + "---------------------------- " + "--------------------", 0); + log_printf("\n", 0); + banner = 1; + } + + for (p = list; p != NULL; p = p -> next) { + log_printf("%2d ", p->board, 0); + log_printf("%-4s ", p->bus_type, 0); + log_printf("%3d ", p->freq, 0); + /* + * We check to see if it's an int or + * a char string to display for slot. + */ + if (p->slot == PCI_SLOT_IS_STRING) + log_printf("%10s ", p->slot_str, 0); + else + log_printf("%10d ", p->slot, 0); + + log_printf("%-28.28s", p->name, 0); + if (strlen(p->name) > 28) + log_printf("+ ", 0); + else + log_printf(" ", 0); + log_printf("%-19.19s", p->model, 0); + if (strlen(p->model) > 19) + log_printf("+", 0); + log_printf("\n", 0); + } +} + +/* + * Display all FFBs on this board. It can either be in tabular format, + * or a more verbose format. + */ +void +display_ffb(Board_node *board, int table) +{ + Prom_node *fb; + void *value; + struct io_card *card_list = NULL; + struct io_card card; + char *type; + char *label; + + if (board == NULL) + return; + + /* Fill in common information */ + card.display = 1; + card.board = board->board_num; + (void) sprintf(card.bus_type, BUS_TYPE); + card.freq = sys_clk; + + for (fb = dev_find_node_by_type(board->nodes, "device_type", "display"); + fb != NULL; + fb = dev_next_node_by_type(fb, "device_type", "display")) { + value = get_prop_val(find_prop(fb, "name")); + if (value != NULL) { + if ((strcmp(FFB_NAME, value)) == 0) { + type = FFB_NAME; + label = "FFB"; + } else if ((strcmp(AFB_NAME, value)) == 0) { + type = AFB_NAME; + label = "AFB"; + } else + continue; + } else + continue; + if (table == 1) { + /* Print out in table format */ + + /* XXX - Get the slot number (hack) */ + card.slot = get_id(fb); + + /* Find out if it's single or double buffered */ + (void) sprintf(card.name, "%s", label); + value = get_prop_val(find_prop(fb, "board_type")); + if (value != NULL) + if ((*(int *)value) & FFB_B_BUFF) + (void) sprintf(card.name, + "%s, Double Buffered", label); + else + (void) sprintf(card.name, + "%s, Single Buffered", label); + + /* + * Print model number only if board_type bit 2 + * is not set and it is not SUNW,XXX-XXXX. + */ + card.model[0] = '\0'; + + if (strcmp(type, AFB_NAME) == 0) { + if (((*(int *)value) & 0x4) != 0x4) { + value = get_prop_val(find_prop(fb, + "model")); + if ((value != NULL) && + (strcmp(value, + "SUNW,XXX-XXXX") != 0)) { + (void) sprintf(card.model, "%s", + (char *)value); + } + } + } else { + value = get_prop_val(find_prop(fb, "model")); + if (value != NULL) + (void) sprintf(card.model, "%s", + (char *)value); + } + + card_list = insert_io_card(card_list, &card); + } else { + /* print in long format */ + char device[MAXSTRLEN]; + int fd = -1; + struct dirent *direntp; + DIR *dirp; + union strap_un strap; + struct ffb_sys_info fsi; + + /* Find the device node using upa-portid/portid */ + value = get_prop_val(find_prop(fb, "upa-portid")); + if (value == NULL) + value = get_prop_val(find_prop(fb, "portid")); + + if (value == NULL) + continue; + + (void) sprintf(device, "%s@%x", type, + *(int *)value); + if ((dirp = opendir("/devices")) == NULL) + continue; + + while ((direntp = readdir(dirp)) != NULL) { + if (strstr(direntp->d_name, device) != NULL) { + (void) sprintf(device, "/devices/%s", + direntp->d_name); + fd = open(device, O_RDWR, 0666); + break; + } + } + (void) closedir(dirp); + + if (fd == -1) + continue; + + if (ioctl(fd, FFB_SYS_INFO, &fsi) < 0) + continue; + + log_printf("%s Hardware Configuration:\n", label, 0); + log_printf("-----------------------------------\n", 0); + + strap.ffb_strap_bits = fsi.ffb_strap_bits; + log_printf("\tBoard rev: %d\n", + (int)strap.fld.board_rev, 0); + log_printf("\tFBC version: 0x%x\n", fsi.fbc_version, 0); + log_printf("\tDAC: %s\n", + fmt_manf_id(fsi.dac_version, device), 0); + log_printf("\t3DRAM: %s\n", + fmt_manf_id(fsi.fbram_version, device), 0); + log_printf("\n", 0); + } + + } + display_io_cards(card_list); + free_io_cards(card_list); +} + + +/* + * Display all the SBus IO cards on this board. + */ +void +display_sbus(Board_node *board) +{ + struct io_card card; + struct io_card *card_list = NULL; + int freq; + int card_num; + void *value; + Prom_node *sbus; + Prom_node *card_node; + + if (board == NULL) + return; + + for (sbus = dev_find_node(board->nodes, SBUS_NAME); sbus != NULL; + sbus = dev_next_node(sbus, SBUS_NAME)) { + + /* Skip failed nodes for now */ + if (node_failed(sbus)) + continue; + + /* Calculate SBus frequency in MHz */ + value = get_prop_val(find_prop(sbus, "clock-frequency")); + if (value != NULL) + freq = ((*(int *)value) + 500000) / 1000000; + else + freq = -1; + + for (card_node = sbus->child; card_node != NULL; + card_node = card_node->sibling) { + char *model; + char *name; + char *child_name; + + card_num = get_sbus_slot(card_node); + if (card_num == -1) + continue; + + /* Fill in card information */ + card.display = 1; + card.freq = freq; + card.board = board->board_num; + (void) sprintf(card.bus_type, "SBus"); + card.slot = card_num; + card.status[0] = '\0'; + + /* Try and get card status */ + value = get_prop_val(find_prop(card_node, "status")); + if (value != NULL) + (void) strncpy(card.status, (char *)value, + MAXSTRLEN); + + /* XXX - For now, don't display failed cards */ + if (strstr(card.status, "fail") != NULL) + continue; + + /* Now gather all of the node names for that card */ + model = (char *)get_prop_val(find_prop(card_node, + "model")); + name = get_node_name(card_node); + + if (name == NULL) + continue; + + card.name[0] = '\0'; + card.model[0] = '\0'; + + /* Figure out how we want to display the name */ + child_name = get_node_name(card_node->child); + if ((card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(card_node->child, + "device_type")); + if (value != NULL) + (void) sprintf(card.name, "%s/%s (%s)", + name, child_name, + (char *)value); + else + (void) sprintf(card.name, "%s/%s", name, + child_name); + } else { + (void) strncpy(card.name, name, MAXSTRLEN); + } + + if (model != NULL) + (void) strncpy(card.model, model, MAXSTRLEN); + + card_list = insert_io_card(card_list, &card); + } + } + + /* We're all done gathering card info, now print it out */ + display_io_cards(card_list); + free_io_cards(card_list); +} + + +/* + * Get slot-names properties from parent node and + * store them in an array. + */ +int +populate_slot_name_arr(Prom_node *pci, int *slot_name_bits, + char **slot_name_arr, int num_slots) +{ + int i, j, bit_mask; + char *value; + + value = (char *)get_prop_val(find_prop(pci, "slot-names")); + D_PRINTF("\n populate_slot_name_arr: value = [0x%x]\n", value); + + if (value != NULL) { + char *strings_arr[MAX_SLOTS_PER_IO_BD]; + bit_mask = *slot_name_bits = *(int *)value; + D_PRINTF("\nslot_names 1st integer = [0x%x]", *slot_name_bits); + + /* array starts after first int */ + strings_arr[0] = value + sizeof (int); + + /* + * break the array out into num_slots number of strings + */ + for (i = 1; i < num_slots; i++) { + strings_arr[i] = (char *)strings_arr[i - 1] + + strlen(strings_arr[i - 1]) + 1; + } + + /* + * process array of slot_names to remove blanks + */ + j = 0; + for (i = 0; i < num_slots; i++) { + if ((bit_mask >> i) & 0x1) + slot_name_arr[i] = strings_arr[j++]; + else + slot_name_arr[i] = ""; + + D_PRINTF("\nslot_name_arr[%d] = [%s]", i, + slot_name_arr[i]); + } + return (0); + } else { + D_PRINTF("\n populate_slot_name_arr: - psycho with no " + "slot-names\n"); + return (0); + } +} + +int +get_card_frequency(Prom_node *pci) +{ + char *value = get_prop_val(find_prop(pci, "clock-frequency")); + + if (value == NULL) + return (-1); + else + return (int)(((*(int *)value) + 500000) / 1000000); + +} + +void +get_dev_func_num(Prom_node *card_node, int *dev_no, int *func_no) +{ + + void *value = get_prop_val(find_prop(card_node, "reg")); + + if (value != NULL) { + int int_val = *(int *)value; + *dev_no = PCI_REG_TO_DEV(int_val); + *func_no = PCI_REG_TO_FUNC(int_val); + } else { + *dev_no = -1; + *func_no = -1; + } +} + +void +get_pci_class_codes(Prom_node *card_node, int *class_code, int *subclass_code) +{ + int class_code_reg = get_pci_class_code_reg(card_node); + + *class_code = CLASS_REG_TO_CLASS(class_code_reg); + *subclass_code = CLASS_REG_TO_SUBCLASS(class_code_reg); +} + +int +is_pci_bridge(Prom_node *card_node, char *name) +{ + int class_code, subclass_code; + + if (card_node == NULL) + return (FALSE); + + get_pci_class_codes(card_node, &class_code, &subclass_code); + + if ((strncmp(name, "pci", 3) == 0) && + (class_code == PCI_BRIDGE_CLASS) && + (subclass_code == PCI_PCI_BRIDGE_SUBCLASS)) + return (TRUE); + else + return (FALSE); +} + +int +is_pci_bridge_other(Prom_node *card_node, char *name) +{ + int class_code, subclass_code; + + if (card_node == NULL) + return (FALSE); + + get_pci_class_codes(card_node, &class_code, &subclass_code); + + if ((strncmp(name, "pci", 3) == 0) && + (class_code == PCI_BRIDGE_CLASS) && + (subclass_code == PCI_SUBCLASS_OTHER)) + return (TRUE); + else + return (FALSE); +} +void +get_pci_card_model(Prom_node *card_node, char *model) +{ + char *name = get_prop_val(find_prop(card_node, "name")); + char *value = get_prop_val(find_prop(card_node, "model")); + int pci_bridge = is_pci_bridge(card_node, name); + + if (value == NULL) + model[0] = '\0'; + else + (void) sprintf(model, "%s", + (char *)value); + + if (pci_bridge) { + if (strlen(model) == 0) + (void) sprintf(model, + "%s", "pci-bridge"); + else + (void) sprintf(model, + "%s/pci-bridge", model); + } +} + +void +create_io_card_name(Prom_node *card_node, char *name, char *card_name) +{ + char *value = get_prop_val(find_prop(card_node, "compatible")); + char *child_name; + char buf[MAXSTRLEN]; + + if (value != NULL) { + (void) sprintf(buf, "%s-%s", name, + (char *)value); + } else + (void) sprintf(buf, "%s", name); + + name = buf; + + child_name = (char *)get_node_name(card_node->child); + + if ((card_node->child != NULL) && + (child_name != NULL)) { + value = get_prop_val(find_prop(card_node->child, + "device_type")); + if (value != NULL) + (void) sprintf(card_name, "%s/%s (%s)", + name, child_name, + (char *)value); + else + (void) sprintf(card_name, "%s/%s", name, + child_name); + } else { + (void) sprintf(card_name, "%s", (char *)name); + } +} + + +/* + * Desktop display_psycho_pci + * Display all the psycho based PCI IO cards on this board. + */ + +/* ARGSUSED */ +void +display_psycho_pci(Board_node *board) +{ + struct io_card *card_list = NULL; + struct io_card card; + void *value; + + Prom_node *pci, *card_node, *pci_bridge_node = NULL; + char *name; + int slot_name_bits, pci_bridge_dev_no, + class_code, subclass_code, + pci_pci_bridge; + char *slot_name_arr[MAX_SLOTS_PER_IO_BD]; + + if (board == NULL) + return; + + /* Initialize all the common information */ + card.display = 1; + card.board = board->board_num; + (void) sprintf(card.bus_type, "PCI"); + + for (pci = dev_find_node_by_type(board->nodes, "model", "SUNW,psycho"); + pci != NULL; + pci = dev_next_node_by_type(pci, "model", "SUNW,psycho")) { + + /* + * If we have reached a pci-to-pci bridge node, + * we are one level below the 'pci' nodes level + * in the device tree. To get back to that level, + * the search should continue with the sibling of + * the parent or else the remaining 'pci' cards + * will not show up in the output. + */ + if (find_prop(pci, "upa-portid") == NULL) { + if ((pci->parent->sibling != NULL) && + (strcmp(get_prop_val( + find_prop(pci->parent->sibling, + "name")), PCI_NAME) == 0)) + pci = pci->parent->sibling; + else { + pci = pci->parent->sibling; + continue; + } + } + + D_PRINTF("\n\n------->Looking at device [%s][%d] - [%s]\n", + PCI_NAME, *((int *)get_prop_val(find_prop( + pci, "upa-portid"))), + get_prop_val(find_prop(pci, "model"))); + + /* Skip all failed nodes for now */ + if (node_failed(pci)) + continue; + + /* Fill in frequency */ + card.freq = get_card_frequency(pci); + + /* + * Each PSYCHO device has a slot-names property that can be + * used to determine the slot-name string for each IO + * device under this node. We get this array now and use + * it later when looking at the children of this PSYCHO. + */ + if ((populate_slot_name_arr(pci, &slot_name_bits, + (char **)&slot_name_arr, MAX_SLOTS_PER_IO_BD)) != 0) + goto next_card; + + /* Walk through the PSYCHO children */ + card_node = pci->child; + while (card_node != NULL) { + + pci_pci_bridge = FALSE; + + /* If it doesn't have a name, skip it */ + name = (char *)get_prop_val( + find_prop(card_node, "name")); + if (name == NULL) + goto next_card; + + /* get dev# and func# for this card. */ + get_dev_func_num(card_node, &card.dev_no, + &card.func_no); + + /* get class/subclass code for this card. */ + get_pci_class_codes(card_node, &class_code, + &subclass_code); + + D_PRINTF("\nName [%s] - ", name); + D_PRINTF("device no [%d] - ", card.dev_no); + D_PRINTF("class_code [%d] subclass_code [%d] - ", + class_code, subclass_code); + + /* + * Weed out PCI Bridge, subclass 'other' and + * ebus nodes. + */ + if (((class_code == PCI_BRIDGE_CLASS) && + (subclass_code == PCI_SUBCLASS_OTHER)) || + (strstr(name, "ebus"))) { + D_PRINTF("\nSkip ebus/class-other nodes [%s]", + name); + goto next_card; + } + + /* + * If this is a PCI bridge, then we store it's dev_no + * so that it's children can use it for getting at + * the slot_name. + */ + if (is_pci_bridge(card_node, name)) { + pci_bridge_dev_no = card.dev_no; + pci_bridge_node = card_node; + pci_pci_bridge = TRUE; + D_PRINTF("\nPCI Bridge detected\n"); + } + + /* + * If we are the child of a pci_bridge we use the + * dev# of the pci_bridge as an index to get + * the slot number. We know that we are a child of + * a pci-bridge if our parent is the same as the last + * pci_bridge node found above. + */ + if (card_node->parent == pci_bridge_node) + card.dev_no = pci_bridge_dev_no; + + /* Get slot-names property from slot_names_arr. */ + get_slot_number_str(&card, (char **)slot_name_arr, + slot_name_bits); + + if (slot_name_bits) + D_PRINTF("\nIO Card [%s] dev_no [%d] SlotStr " + "[%s] slot [%s]", name, card.dev_no, + slot_name_arr[card.dev_no], + card.slot_str); + + /* XXX - Don't know how to get status for PCI cards */ + card.status[0] = '\0'; + + /* Get the model of this card */ + get_pci_card_model(card_node, (char *)&card.model); + + /* + * If we haven't figured out the frequency yet, + * try and get it from the card. + */ + value = get_prop_val(find_prop(pci, "clock-frequency")); + if (value != NULL && card.freq == -1) + card.freq = ((*(int *)value) + 500000) + / 1000000; + + + /* Figure out how we want to display the name */ + create_io_card_name(card_node, name, + (char *)&card.name); + + if (card.freq != -1) + card_list = insert_io_card(card_list, &card); + +next_card: + /* + * If we are done with the children of the pci bridge, + * we must continue with the remaining siblings of + * the pci-to-pci bridge - otherwise we move onto our + * own sibling. + */ + if (pci_pci_bridge) { + if (card_node->child != NULL) + card_node = card_node->child; + else + card_node = card_node->sibling; + } else { + if ((card_node->parent == pci_bridge_node) && + (card_node->sibling == NULL)) + card_node = pci_bridge_node->sibling; + else + card_node = card_node->sibling; + } + } /* end-while */ + } /* end-for */ + + D_PRINTF("\n\n"); + + display_io_cards(card_list); + free_io_cards(card_list); +} + +void +get_slot_number_str(struct io_card *card, char **slot_name_arr, + int slot_name_bits) +{ + if (card->dev_no != -1) { + char *slot; + /* + * slot_name_bits is a mask of the plug-in slots so if our + * dev_no does not appear in this mask we must be an + * on_board device so set the slot to 'On-Board' + */ + if (slot_name_bits & (1 << card->dev_no)) { + /* we are a plug-in card */ + slot = slot_name_arr[card->dev_no]; + if (strlen(slot) != 0) { + (void) sprintf(card->slot_str, "%s", + slot); + } else + (void) sprintf(card->slot_str, "-"); + } else { + /* this is an on-board dev. */ + sprintf(card->slot_str, "On-Board"); + } + + } else { + (void) sprintf(card->slot_str, "%c", '-'); + } + + /* Informs display_io_cards to print slot_str instead of slot */ + card->slot = PCI_SLOT_IS_STRING; +} + + +/* + * The output of a number of I/O cards are identical so we need to + * differentiate between them. + * + * This function is called by the platform specific code and it decides + * if the card needs further processing. + * + * It can be extended in the future if card types other than QLC have + * the same problems. + */ +void +distinguish_identical_io_cards(char *name, Prom_node *node, + struct io_card *card) +{ + if ((name == NULL) || (node == NULL)) + return; + + if (strcmp(name, "SUNW,qlc") == 0) + decode_qlc_card_model_prop(node, card); +} + + +/* + * The name/model properties for a number of the QLC FCAL PCI cards are + * identical (*), so we need to distinguish them using the subsystem-id + * and modify the model string to be more informative. + * + * (*) Currently the problem cards are: + * Amber + * Crystal+ + */ +void +decode_qlc_card_model_prop(Prom_node *card_node, struct io_card *card) +{ + void *value = NULL; + + if (card_node == NULL) + return; + + value = get_prop_val(find_prop(card_node, "subsystem-id")); + if (value != NULL) { + int id = *(int *)value; + + switch (id) { + case AMBER_SUBSYSTEM_ID: + (void) snprintf(card->model, MAX_QLC_MODEL_LEN, "%s", + AMBER_CARD_NAME); + break; + + case CRYSTAL_SUBSYSTEM_ID: + (void) snprintf(card->model, MAX_QLC_MODEL_LEN, "%s", + CRYSTAL_CARD_NAME); + break; + + default: + /* + * If information has been saved into the model field + * before this function was called we will keep it as + * it probably will be more meaningful that the + * subsystem-id, otherwise we save the subsystem-id in + * the hope that it will distinguish the cards. + */ + if (strcmp(card->model, "") == 0) { + (void) snprintf(card->model, MAX_QLC_MODEL_LEN, + "0x%x", id); + } + break; + } + } +} |
