2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <devid.h>
2N/A#include <strings.h>
2N/A#include <fcntl.h>
2N/A#include <unistd.h>
2N/A#include <stropts.h>
2N/A#include <strings.h>
2N/A#include <stddef.h>
2N/A#include <pthread.h>
2N/A#include <inttypes.h>
2N/A#include <fm/topo_mod.h>
2N/A#include <fm/libtopo.h>
2N/A#include <fm/topo_list.h>
2N/A#include <sys/scsi/scsi_address.h>
2N/A#include <sys/fm/protocol.h>
2N/A#include <sys/utsname.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <sys/pci.h>
2N/A#include <sys/pcie.h>
2N/A#include <sys/pci_tools.h>
2N/A#include <bay_impl.h>
2N/A
2N/Aextern int hba_node_cnt;
2N/A/*
2N/A * Routines used by the bay,so topo enumerator.
2N/A */
2N/A
2N/A/*
2N/A * Create "target-port-l0ids" property.
2N/A */
2N/Aint
2N/Acreate_l0ids(topo_mod_t *mod, tnode_t *bay_tn, bay_t *bp)
2N/A{
2N/A int err;
2N/A int rv;
2N/A tnode_t *disk_tn;
2N/A char *tgt_port = NULL;
2N/A di_node_t cnode = DI_NODE_NIL;
2N/A di_path_t pnode = DI_PATH_NIL;
2N/A
2N/A char *f = "create_l0ids";
2N/A
2N/A rv = find_child(mod, bp->hba_dnode, &cnode, &pnode, bp->phy);
2N/A if (rv != 0) {
2N/A topo_mod_dprintf(mod, "%s: failed to find child node\n", f);
2N/A goto out;
2N/A }
2N/A
2N/A tgt_port = topo_mod_alloc(mod, MAXNAMELEN);
2N/A if (tgt_port == NULL) {
2N/A topo_mod_dprintf(mod, "%s: failed to get memory for tgt-port\n",
2N/A f);
2N/A goto out;
2N/A }
2N/A rv = get_str_prop(cnode, pnode, SCSI_ADDR_PROP_TARGET_PORT, tgt_port);
2N/A if (rv != 0) {
2N/A /* look at the child */
2N/A rv = get_str_prop(di_child_node(cnode), DI_PATH_NIL,
2N/A SCSI_ADDR_PROP_TARGET_PORT, tgt_port);
2N/A if (rv != 0) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: failed to get \'target-port\'\n", f);
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A disk_tn = topo_node_lookup(bay_tn, DISK, 0);
2N/A if (disk_tn == NULL) {
2N/A topo_mod_dprintf(mod, "%s: failed to lookup disk tnode\n", f);
2N/A goto out;
2N/A }
2N/A
2N/A rv = topo_prop_set_string_array(disk_tn, TOPO_PGROUP_STORAGE,
2N/A TOPO_STORAGE_TARGET_PORT_L0IDS, TOPO_PROP_IMMUTABLE,
2N/A (const char **)&tgt_port, (uint_t)1, &err);
2N/A if (rv != 0) {
2N/A topo_mod_dprintf(mod, "%s: failed to add %s property: %s\n",
2N/A f, TOPO_STORAGE_TARGET_PORT_L0IDS, topo_strerror(err));
2N/A goto out;
2N/A }
2N/A
2N/A topo_mod_free(mod, tgt_port, MAXNAMELEN);
2N/A return (0);
2N/Aout:
2N/A if (tgt_port != NULL) {
2N/A topo_mod_free(mod, tgt_port, MAXNAMELEN);
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Return the minor attachment point node.
2N/A */
2N/A/* ARGSUSED */
2N/Adi_minor_t
2N/Afind_minor_ap(topo_mod_t *mod, di_node_t cnode)
2N/A{
2N/A di_minor_t minor = DI_MINOR_NIL;
2N/A
2N/A while ((minor = di_minor_next(cnode, minor)) != DI_MINOR_NIL) {
2N/A if (cmp_str(BAY_SCSI_AP, di_minor_nodetype(minor))) {
2N/A return (minor);
2N/A }
2N/A }
2N/A return (DI_MINOR_NIL);
2N/A}
2N/A
2N/A/*
2N/A * return the child node which matches the phy.
2N/A */
2N/A/* ARGSUSED */
2N/Aint
2N/Afind_child(topo_mod_t *mod, di_node_t dnode, di_node_t *cn, di_path_t *pn,
2N/A int phy)
2N/A{
2N/A di_node_t cnode = di_child_node(dnode);
2N/A di_path_t pnode = DI_PATH_NIL;
2N/A
2N/A while (cnode != DI_NODE_NIL) {
2N/A if (get_phy(cnode, DI_PATH_NIL) == phy) {
2N/A bcopy(&cnode, cn, sizeof (di_node_t));
2N/A return (0);
2N/A }
2N/A cnode = di_sibling_node(cnode);
2N/A }
2N/A
2N/A while ((pnode = di_path_phci_next_path(dnode, pnode)) != DI_PATH_NIL) {
2N/A if (get_phy(DI_NODE_NIL, pnode) == phy) {
2N/A bcopy(&pnode, pn, sizeof (di_path_t));
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A /* no PHY found */
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Create the attachment point path.
2N/A */
2N/Achar *
2N/Agen_ap(topo_mod_t *mod, di_minor_t minor)
2N/A{
2N/A char path[MAXPATHLEN];
2N/A char *devfs_path = di_devfs_minor_path(minor);
2N/A
2N/A /* ap_path = "/devices" + minor node path */
2N/A (void) snprintf(path, MAXPATHLEN, "%s%s", DEVICES, devfs_path);
2N/A di_devfs_path_free(devfs_path);
2N/A
2N/A return (topo_mod_strdup(mod, path));
2N/A}
2N/A
2N/A/*
2N/A * Return the generated generic slot label.
2N/A *
2N/A * The caller is responsible to free memory.
2N/A */
2N/Achar *
2N/Ageneric_label(topo_mod_t *mod, di_node_t hba_node, tnode_t *tnp, int n)
2N/A{
2N/A uint32_t ccode;
2N/A char g_label[MAXNAMELEN];
2N/A char *slotnm = NULL;
2N/A char *node_name = NULL;
2N/A char *gen_node_name = NULL;
2N/A
2N/A char *f = "generic_label";
2N/A
2N/A /* grab the topo pci label */
2N/A slotnm = get_pcilabel(mod, tnp, hba_node);
2N/A
2N/A /* get the generic name from the class code */
2N/A ccode = (uint32_t)get_int_prop(hba_node, DI_PATH_NIL, "class-code");
2N/A gen_node_name = di_pciclass2_node_name(ccode);
2N/A
2N/A topo_mod_dprintf(mod, "%s: generic name(%s):class code(0x%x)\n",
2N/A f, gen_node_name != NULL ? gen_node_name : "NULL", ccode);
2N/A
2N/A /* no generic name, use node name */
2N/A if (gen_node_name == NULL) {
2N/A node_name = topo_mod_strdup(mod, di_node_name(hba_node));
2N/A }
2N/A
2N/A /* create the label */
2N/A (void) snprintf(g_label, MAXNAMELEN, "%s/%s/PHY%d",
2N/A slotnm != NULL ? slotnm : "unknown-slot",
2N/A gen_node_name != NULL ? gen_node_name : node_name, n);
2N/A
2N/A /* clean up */
2N/A if (node_name != NULL) {
2N/A topo_mod_strfree(mod, node_name);
2N/A }
2N/A if (gen_node_name != NULL) {
2N/A di_pciclass2_node_name_free(gen_node_name);
2N/A }
2N/A if (slotnm != NULL) {
2N/A topo_mod_free(mod, slotnm, MAXNAMELEN);
2N/A }
2N/A
2N/A /* return label */
2N/A topo_mod_dprintf(mod, "%s: generic label(%s)\n", f, g_label);
2N/A return (topo_mod_strdup(mod, g_label));
2N/A}
2N/A
2N/A/*
2N/A * Return TRUE if chassis label is an 'internal' chassis.
2N/A */
2N/Aboolean_t
2N/Ainternal_ch(bay_t *bp)
2N/A{
2N/A if (cmp_str(bp->ch_label, INTERNAL)) {
2N/A return (B_TRUE);
2N/A }
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Generate the occupant path.
2N/A */
2N/Achar *
2N/Agen_oc(topo_mod_t *mod, di_node_t dnode, di_path_t pnode)
2N/A{
2N/A int rv;
2N/A int lun;
2N/A boolean_t got_w;
2N/A char path[MAXPATHLEN];
2N/A char *devfs_path = NULL;
2N/A char *target_port = NULL;
2N/A
2N/A char *f = "gen_oc";
2N/A
2N/A /* dev path */
2N/A if (pnode != DI_PATH_NIL) {
2N/A devfs_path = di_path_devfs_path(pnode);
2N/A bcopy(devfs_path, path, strlen(devfs_path) + 1);
2N/A di_devfs_path_free(devfs_path);
2N/A topo_mod_dprintf(mod, "%s: pathinfo path (%s)\n", f, path);
2N/A goto done;
2N/A }
2N/A
2N/A if (dnode != DI_NODE_NIL) {
2N/A devfs_path = di_devfs_path(dnode);
2N/A topo_mod_dprintf(mod, "%s: devinfo path (%s)\n", f,
2N/A devfs_path);
2N/A } else {
2N/A topo_mod_dprintf(mod, "%s: no path\n", f);
2N/A goto out;
2N/A }
2N/A
2N/A /* 'target-port' prop */
2N/A target_port = topo_mod_alloc(mod, MAXNAMELEN);
2N/A if (target_port == NULL) {
2N/A topo_mod_dprintf(mod, "%s: no memory for target-port\n", f);
2N/A goto out;
2N/A }
2N/A rv = get_str_prop(dnode, pnode, SCSI_ADDR_PROP_TARGET_PORT,
2N/A target_port);
2N/A if (rv != 0) {
2N/A topo_mod_dprintf(mod, "%s: failed to get target-port\n", f);
2N/A goto out;
2N/A }
2N/A
2N/A /* 'lun' prop */
2N/A lun = get_int_prop(dnode, pnode, "lun");
2N/A if (lun < 0 || lun > 255) {
2N/A topo_mod_dprintf(mod, "%s: invalid lun (%d)\n", f, lun);
2N/A goto out;
2N/A }
2N/A
2N/A /* leading 'w' is not always consistent */
2N/A got_w = target_port[0] == 'w' ? B_TRUE : B_FALSE;
2N/A
2N/A /* oc_path = devfs path + "/disk@w' + 'target-port' + "," + 'lun' */
2N/A (void) snprintf(path, MAXPATHLEN, "%s%s%s,%x", devfs_path,
2N/A (got_w ? "/disk@" : "/disk@w"), target_port, lun);
2N/A topo_mod_free(mod, target_port, MAXNAMELEN);
2N/A di_devfs_path_free(devfs_path);
2N/Adone:
2N/A return (topo_mod_strdup(mod, path));
2N/Aout:
2N/A if (target_port != NULL) {
2N/A topo_mod_free(mod, target_port, MAXNAMELEN);
2N/A }
2N/A if (devfs_path != NULL) {
2N/A di_devfs_path_free(devfs_path);
2N/A }
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Get the devid property from the disk node matching the path passed in.
2N/A */
2N/Achar *
2N/Aget_devid(topo_mod_t *mod, char *oc_path, bay_t *bp)
2N/A{
2N/A int rv;
2N/A di_path_t dpath = DI_PATH_NIL;
2N/A di_node_t dnode = DI_NODE_NIL;
2N/A di_node_t cnode = DI_NODE_NIL;
2N/A char *path = NULL;
2N/A char *devid = NULL;
2N/A
2N/A char *f = "get_devid";
2N/A
2N/A dnode = di_drv_first_node("sd", topo_mod_devinfo(mod));
2N/A while (dnode != DI_NODE_NIL) {
2N/A while ((dpath = di_path_client_next_path(dnode, dpath)) !=
2N/A NULL) {
2N/A path = di_path_devfs_path(dpath);
2N/A if (cmp_str(path, oc_path)) {
2N/A rv = di_prop_lookup_strings(DDI_DEV_T_ANY,
2N/A dnode, DEVID_PROP_NAME, &devid);
2N/A if (rv < 0) {
2N/A topo_mod_dprintf(mod, "%s: no devid "
2N/A "for path: %s\n", f, path);
2N/A di_devfs_path_free(path);
2N/A goto nopath;
2N/A }
2N/A di_devfs_path_free(path);
2N/A goto done;
2N/A }
2N/A di_devfs_path_free(path);
2N/A }
2N/A dnode = di_drv_next_node(dnode);
2N/A }
2N/Anopath:
2N/A /* find the child matching the phy and grab the devid prop */
2N/A dpath = DI_PATH_NIL;
2N/A rv = find_child(mod, bp->hba_dnode, &cnode, &dpath, bp->phy);
2N/A if (rv == 0) {
2N/A char *ndevid = topo_mod_alloc(mod, MAXNAMELEN);
2N/A char buf[MAXNAMELEN];
2N/A
2N/A rv = get_str_prop(cnode, dpath, DEVID_PROP_NAME, ndevid);
2N/A if (rv != 0) {
2N/A topo_mod_free(mod, ndevid, MAXNAMELEN);
2N/A return (NULL);
2N/A }
2N/A
2N/A bcopy(ndevid, buf, strlen(ndevid) + 1);
2N/A topo_mod_free(mod, ndevid, MAXNAMELEN);
2N/A topo_mod_dprintf(mod, "%s: ndevid for %s:%d: %s\n", f,
2N/A bp->hba_nm, bp->phy, buf);
2N/A return (topo_mod_strdup(mod, buf));
2N/A }
2N/Adone:
2N/A if (devid != NULL) {
2N/A topo_mod_dprintf(mod, "%s: devid for %s:%d: %s\n", f,
2N/A bp->hba_nm, bp->phy, devid);
2N/A return (topo_mod_strdup(mod, devid));
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "%s: failed to get devid for path: %s\n", f,
2N/A oc_path);
2N/A
2N/A return (NULL);
2N/A}
2N/A
2N/Aboolean_t
2N/Aget_generic(topo_mod_t *mod, di_node_t dnode)
2N/A{
2N/A int rv;
2N/A char *s = topo_mod_alloc(mod, MAXNAMELEN);
2N/A
2N/A char *f = "get_generic";
2N/A
2N/A if (s == NULL) {
2N/A topo_mod_dprintf(mod, "%s: no memory\n", f);
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A /* look for boot flag */
2N/A rv = get_str_prop(dnode, DI_PATH_NIL, "sas-das-generic", s);
2N/A
2N/A if (rv != 0 || !cmp_str(s, "true")) {
2N/A /* not set of not true */
2N/A topo_mod_free(mod, s, MAXNAMELEN);
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A topo_mod_free(mod, s, MAXNAMELEN);
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/*
2N/A * Get the total number of possible direct attached PHYs (bays). It's either
2N/A * the 'num-phys-hba' or 'num-phys' integer property of the HBA di_node.
2N/A */
2N/Aint
2N/Aget_num_phys(di_node_t dnode)
2N/A{
2N/A int num_phys;
2N/A
2N/A /* first look for 'num-phys' */
2N/A num_phys = get_int_prop(dnode, DI_PATH_NIL, NUM_PHYS);
2N/A if (num_phys == -1) {
2N/A /* next look for 'num-phys-hba' */
2N/A num_phys = get_int_prop(dnode, DI_PATH_NIL, NUM_PHYS_HBA);
2N/A if (num_phys == -1) {
2N/A num_phys = DFLT_NUM_PHYS;
2N/A }
2N/A }
2N/A
2N/A return (num_phys);
2N/A}
2N/A
2N/A/*
2N/A * Get the chassis name (held in FM_FMRI_AUTH_V1_CHASSIS_NM) and chassis S/N
2N/A * (FM_FMRI_AUTH_V1_CHASSIS_SN) from the authority fmri.
2N/A */
2N/Aint
2N/Aget_prod(topo_mod_t *mod, tnode_t *tparent, char *prod, char *ch_sn)
2N/A{
2N/A int rv;
2N/A char *val = NULL;
2N/A tnode_t *tnp = NULL;
2N/A nvlist_t *auth;
2N/A
2N/A if (cmp_str(topo_node_name(tparent), "chassis")) {
2N/A tnp = tparent;
2N/A } else {
2N/A /* everyone has either a chassis or a mother(board) */
2N/A tnp = topo_node_lookup(tparent, CHASSIS, 0);
2N/A if (tnp == NULL) {
2N/A tnp = topo_node_lookup(tparent, MOTHERBOARD, 0);
2N/A }
2N/A if (tnp == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "No chassis or motherboard node.\n");
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A /* get auth list from topo node */
2N/A auth = topo_mod_auth(mod, tnp);
2N/A
2N/A /* get chassis name */
2N/A rv = nvlist_lookup_string(auth, FM_FMRI_AUTH_V1_CHASSIS_NM, &val);
2N/A if (rv == 0 && val != NULL) {
2N/A bcopy(val, prod, strlen(val) + 1);
2N/A topo_mod_dprintf(mod, "get_prod: %s: %s\n",
2N/A FM_FMRI_AUTH_V1_CHASSIS_NM, prod);
2N/A } else {
2N/A bcopy(BAY_PROP_UNKNOWN, prod, strlen(BAY_PROP_UNKNOWN) + 1);
2N/A }
2N/A
2N/A /* chassis serial number */
2N/A val = NULL;
2N/A rv = nvlist_lookup_string(auth, FM_FMRI_AUTH_V1_CHASSIS_SN, &val);
2N/A if (rv == 0 && val != NULL) {
2N/A bcopy(val, ch_sn, strlen(val) + 1);
2N/A topo_mod_dprintf(mod, "get_prod: %s: %s\n",
2N/A FM_FMRI_AUTH_V1_CHASSIS_SN, ch_sn);
2N/A } else {
2N/A bcopy(BAY_PROP_UNKNOWN, ch_sn, strlen(BAY_PROP_UNKNOWN) + 1);
2N/A }
2N/A nvlist_free(auth);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Callback for mod walk routine; calls the shared routine to find the
2N/A * hba label.
2N/A */
2N/A/* ARGSUSED */
2N/Astatic int
2N/Aget_slotlabel_cb(topo_mod_t *mod, tnode_t *tnp, void *arg)
2N/A{
2N/A return (th_hba_l(NULL, tnp, arg));
2N/A}
2N/A
2N/A/*
2N/A * Get the pci slot label.
2N/A *
2N/A * The caller is responsible for freeing memory.
2N/A */
2N/Achar *
2N/Aget_pcilabel(topo_mod_t *mod, tnode_t *tnp, di_node_t dnode)
2N/A{
2N/A int rv;
2N/A int err;
2N/A tw_pcie_cbs_t cbs;
2N/A topo_walk_t *twp;
2N/A
2N/A char *f = "get_pcilabel";
2N/A
2N/A /* init walk */
2N/A cbs.devfs_path = di_devfs_path(dnode);
2N/A cbs.label = topo_mod_alloc(mod, MAXNAMELEN);
2N/A cbs.mod = mod;
2N/A cbs.hdl = NULL;
2N/A twp = topo_mod_walk_init(mod, tnp, get_slotlabel_cb, &cbs, &err);
2N/A if (twp == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "%s: topo_walk_init() failed for (%s)\n",
2N/A f, cbs.devfs_path);
2N/A goto bad;
2N/A }
2N/A
2N/A /* walk */
2N/A rv = topo_walk_step(twp, TOPO_WALK_CHILD);
2N/A if (rv == TOPO_WALK_ERR) {
2N/A topo_walk_fini(twp);
2N/A topo_mod_dprintf(mod,
2N/A "%s: topo_walk_step() failed for (%s)\n",
2N/A f, cbs.devfs_path);
2N/A goto bad;
2N/A }
2N/A topo_walk_fini(twp);
2N/A
2N/A /* no label */
2N/A if (strlen(cbs.label) == 0) {
2N/A topo_mod_dprintf(mod, "%s: no label for (%s)\n", f,
2N/A cbs.devfs_path);
2N/A goto bad;
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "%s: topo label for (%s):(%s)\n",
2N/A f, cbs.devfs_path, cbs.label);
2N/A
2N/A di_devfs_path_free(cbs.devfs_path);
2N/A return (cbs.label);
2N/Abad:
2N/A di_devfs_path_free(cbs.devfs_path);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Get the slot id by parsing the pci label for the slot, or return 0 if
2N/A * it's an on-board device (label == "MB").
2N/A */
2N/Aint
2N/Aget_slotid_from_pcilabel(topo_mod_t *mod, tnode_t *tnp, di_node_t dnode)
2N/A{
2N/A int i, j;
2N/A int id;
2N/A char n[3];
2N/A char *label = NULL;
2N/A
2N/A label = get_pcilabel(mod, tnp, dnode);
2N/A if (label == NULL) {
2N/A id = -1;
2N/A goto out;
2N/A }
2N/A
2N/A /* on-board - slot id is 0 by pcie spec */
2N/A if (cmp_str(label, "MB")) {
2N/A id = 0;
2N/A goto out;
2N/A }
2N/A
2N/A /* extract the slot id from the label */
2N/A for (i = (strlen(label) + 1), j = 2; i >= 0; i--, j--) {
2N/A if (label[i] < '0' && label[i] > '9' &&
2N/A label[i] != '\0')
2N/A continue;
2N/A n[j] = label[i];
2N/A }
2N/A
2N/A /* convert to an int */
2N/A id = atoi((const char *)n);
2N/A
2N/A /* 'PCIe 0' is really slot id 1 */
2N/A id++;
2N/Aout:
2N/A topo_mod_free(mod, label, MAXNAMELEN);
2N/A return (id);
2N/A}
2N/A
2N/A/*
2N/A * Parse a line of the bay config file to extract data.
2N/A */
2N/Astatic int
2N/Aparse_line(topo_mod_t *mod, di_node_t dnode, bay_t *bp, char *buf)
2N/A{
2N/A int i;
2N/A int phy;
2N/A int drv_inst;
2N/A char *prod = NULL;
2N/A char *drv_name = NULL;
2N/A char *ch_l = NULL;
2N/A char *ch_sn = NULL;
2N/A char *label = NULL;
2N/A char *token;
2N/A char *hba_nm = di_driver_name(dnode);
2N/A int hba_inst = di_instance(dnode);
2N/A
2N/A /* skip lines starting with '#' */
2N/A if (*buf == '#' || *buf == '\n') {
2N/A return (-1);
2N/A }
2N/A
2N/A /* parse the line e.g. */
2N/A /* Sans Digital TR4X:pmcs:0:JBOD 0:812BDA443-184:5:BAY2 */
2N/A if ((token = strtok(buf, ":")) != NULL) {
2N/A prod = token;
2N/A if ((token = strtok(NULL, ":")) != NULL) {
2N/A drv_name = token;
2N/A if ((token = strtok(NULL, ":")) != NULL) {
2N/A drv_inst = atoi(token);
2N/A if ((token = strtok(NULL, ":")) != NULL) {
2N/A ch_l = token;
2N/A if ((token = strtok(NULL, ":")) !=
2N/A NULL) {
2N/A ch_sn = token;
2N/A if ((token = strtok(NULL, ":"))
2N/A != NULL) {
2N/A phy = atoi(token);
2N/A if ((token =
2N/A strtok(NULL, ":"))
2N/A != NULL) {
2N/A label = token;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* check strings */
2N/A if (prod == NULL || drv_name == NULL || ch_l == NULL ||
2N/A ch_sn == NULL || label == NULL) {
2N/A topo_mod_dprintf(mod, "parse_line: bad format\n");
2N/A return (-1);
2N/A }
2N/A
2N/A /* check the hba driver name and instance */
2N/A if (!cmp_str(hba_nm, drv_name) || hba_inst != drv_inst) {
2N/A /* not what we're looking for */
2N/A return (-1);
2N/A }
2N/A
2N/A /* cut off the last newline */
2N/A for (i = 0; i < strlen(label); i++) {
2N/A if (label[i] == '\n') {
2N/A label[i] = '\0';
2N/A }
2N/A }
2N/A
2N/A topo_mod_dprintf(mod, "parse_line: product(%s) drv name(%s) inst(%d) "
2N/A "chassis label(%s) chassis S/N(%s) phy(%d) bay label(%s)\n",
2N/A prod, drv_name, drv_inst, ch_l, ch_sn, phy, label);
2N/A
2N/A /* fill in the bay struct */
2N/A bp->hba_dnode = dnode;
2N/A bp->hba_nm = topo_mod_strdup(mod, drv_name);
2N/A bp->hba_inst = drv_inst;
2N/A bp->ch_prod = topo_mod_strdup(mod, prod);
2N/A bp->ch_label = topo_mod_strdup(mod, ch_l);
2N/A bp->ch_serial = topo_mod_strdup(mod, ch_sn);
2N/A bp->phy = phy;
2N/A bp->label = topo_mod_strdup(mod, label);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Read the config file and fill in the bays array. Return the number of
2N/A * structs (bays) filled in.
2N/A */
2N/Aint
2N/Aread_config(topo_mod_t *mod, di_node_t dnode, char *f, bay_t *bp, int *n)
2N/A{
2N/A int rv;
2N/A int cnt = 0;
2N/A char s[MAXNAMELEN];
2N/A FILE *fp;
2N/A
2N/A /* open the config file and read how many bays for this hba */
2N/A fp = fopen(f, "r");
2N/A if (fp == NULL) {
2N/A topo_mod_dprintf(mod,
2N/A "read_config: failed to open config file (%s)\n", f);
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * config file format:
2N/A * "product:driver name:instance:product name:product s/n:PHY:label"
2N/A * "%s:%s:%d:%s:%d:%s"
2N/A */
2N/A while (fgets(s, MAXNAMELEN, fp) != NULL) {
2N/A rv = parse_line(mod, dnode, &bp[cnt], s);
2N/A if (rv == 0) {
2N/A cnt++;
2N/A }
2N/A }
2N/A
2N/A (void) fclose(fp);
2N/A *n = cnt;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Sort the hba_nodes[] array relative to their slot id. If the slot id is
2N/A * not obtainable; sort in the order seen.
2N/A *
2N/A * Return: 0 - successfully sorted
2N/A * -1 - failed to sort (usually due to lack of PCIe slotid)
2N/A */
2N/Aint
2N/Asort_hba_nodes(topo_mod_t *mod, tnode_t *tnp, di_node_t *hba_nodes)
2N/A{
2N/A int i;
2N/A int j;
2N/A int slot_id;
2N/A di_node_t tmp_d = DI_NODE_NIL;
2N/A
2N/A struct s {
2N/A di_node_t d;
2N/A int id;
2N/A } ss[MAX_HBAS];
2N/A
2N/A topo_mod_dprintf(mod, "sort_hba_nodes: hba_node_cnt = %d\n",
2N/A hba_node_cnt);
2N/A
2N/A /* fill in the sorting struct */
2N/A for (i = 0; i < hba_node_cnt; i++) {
2N/A /*
2N/A * Get the pci slot id from the HBA pcibus topo label.
2N/A * The pcibus enum has already done all the twizzle
2N/A * required to figure out the correct slot-id/label.
2N/A */
2N/A slot_id = get_slotid_from_pcilabel(mod, tnp, hba_nodes[i]);
2N/A if (slot_id == -1) {
2N/A return (-1);
2N/A }
2N/A topo_mod_dprintf(mod, "sort_hba_nodes: slotid = %d\n",
2N/A slot_id);
2N/A
2N/A ss[i].d = hba_nodes[i];
2N/A ss[i].id = slot_id;
2N/A }
2N/A
2N/A /* get ya sort on */
2N/A for (j = 0; j < hba_node_cnt; j++) {
2N/A for (i = 0; i < (hba_node_cnt - 1); i++) {
2N/A if (ss[i + 1].id < ss[i].id) {
2N/A tmp_d = ss[i].d;
2N/A ss[i].d = ss[i+1].d;
2N/A ss[i + 1].d = tmp_d;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* refill hba_nodes now in relative order */
2N/A for (i = 0; i < hba_node_cnt; i++) {
2N/A hba_nodes[i] = ss[i].d;
2N/A }
2N/A
2N/A return (0);
2N/A}