summaryrefslogtreecommitdiff
path: root/usr/src/cmd/luxadm
diff options
context:
space:
mode:
authorJohn Forte <John.Forte@Sun.COM>2008-10-14 15:09:13 -0700
committerJohn Forte <John.Forte@Sun.COM>2008-10-14 15:09:13 -0700
commitfcf3ce441efd61da9bb2884968af01cb7c1452cc (patch)
tree0e80d59ad41702571586195bf099ccc14222ce02 /usr/src/cmd/luxadm
parent247b82a1f1cb5ebd2d163bd9afdb1a3065611962 (diff)
downloadillumos-joyent-fcf3ce441efd61da9bb2884968af01cb7c1452cc.tar.gz
6745433 Merge NWS consolidation into OS/Net consolidation
Diffstat (limited to 'usr/src/cmd/luxadm')
-rw-r--r--usr/src/cmd/luxadm/Makefile107
-rw-r--r--usr/src/cmd/luxadm/adm.c1104
-rw-r--r--usr/src/cmd/luxadm/common.h495
-rw-r--r--usr/src/cmd/luxadm/diag.c262
-rw-r--r--usr/src/cmd/luxadm/errorcodes.h536
-rw-r--r--usr/src/cmd/luxadm/errormsgs.c1108
-rw-r--r--usr/src/cmd/luxadm/fabric_conf.c335
-rw-r--r--usr/src/cmd/luxadm/fcalupdate.c948
-rw-r--r--usr/src/cmd/luxadm/fchba.c2151
-rw-r--r--usr/src/cmd/luxadm/g_adm.c6673
-rw-r--r--usr/src/cmd/luxadm/hot.h78
-rw-r--r--usr/src/cmd/luxadm/hotplug.c3037
-rw-r--r--usr/src/cmd/luxadm/ibfirmwarebin0 -> 389496 bytes
-rw-r--r--usr/src/cmd/luxadm/lux_util.c1594
-rw-r--r--usr/src/cmd/luxadm/luxadm.h110
-rw-r--r--usr/src/cmd/luxadm/qlgcupdate.c1591
-rw-r--r--usr/src/cmd/luxadm/setboot.c257
-rw-r--r--usr/src/cmd/luxadm/x86_adm.c533
18 files changed, 20919 insertions, 0 deletions
diff --git a/usr/src/cmd/luxadm/Makefile b/usr/src/cmd/luxadm/Makefile
new file mode 100644
index 0000000000..dc58795386
--- /dev/null
+++ b/usr/src/cmd/luxadm/Makefile
@@ -0,0 +1,107 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+
+PROG = luxadm
+
+COBJS = adm.o fchba.o lux_util.o fabric_conf.o
+OBJS_sparc= g_adm.o diag.o fcalupdate.o hotplug.o qlgcupdate.o setboot.o
+OBJS_i386 = x86_adm.o errormsgs.o
+OBJS = $(OBJS_$(MACH)) $(COBJS)
+
+SRCS = $(OBJS:%.o=%.c)
+
+POFILES = $(OBJS:%.o=%.po)
+POFILE = luxadm.po
+
+include ../Makefile.cmd
+
+sparc_LDLIBS = -la5k -lg_fc -ldevid -lkstat -ldevinfo
+i386_LDLIBS =
+LDLIBS += $($(MACH)_LDLIBS)
+LDLIBS += -lnvpair
+LDLIBS += -ldevice
+LDLIBS += -lHBAAPI
+
+INCS += -I$(SRC)/lib/sun_fc/common
+
+CPPFLAGS += -D_LARGEFILE64_SOURCE=1 -D_REENTRANT $(INCS)
+
+LINTFLAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2
+LINTFLAGS += -erroff=E_SEC_PRINTF_VAR_FMT
+LINTFLAGS += -erroff=E_SEC_SPRINTF_UNBOUNDED_COPY
+LINTFLAGS += -erroff=E_NAME_USED_NOT_DEF2
+LINTFLAGS += -erroff=E_BAD_FORMAT_ARG_TYPE2
+LINTFLAGS += -erroff=E_SEC_FORBIDDEN_WARN_GETS
+LINTFLAGS += -erroff=E_SEC_SCANF_UNBOUNDED_COPY
+
+ROOTUSRSBINLINKS = $(PROG:%=$(ROOTUSRSBIN)/%)
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+GENCAT_PROG=a5k_g_fc_i18n_cat
+GENMSG_LUX= $(PROG).msg
+LUXFIRMWARED= $(ROOTLIB)/locale/C/LC_MESSAGES
+LUXIBFIRMWARE_sparc= $(LUXFIRMWARED)/ibfirmware
+LUXIBFIRMWARE= $(LUXIBFIRMWARE_$(MACH))
+LUXGENCAT_PROG_sparc= $(LUXFIRMWARED)/$(GENCAT_PROG)
+LUXGENCAT_PROG= $(LUXGENCAT_PROG_$(MACH))
+$(LUXIBFIRMWARE):= FILEMODE= 0644
+$(LUXGENCAT_PROG):= FILEMODE= 0644
+
+$(LUXFIRMWARED):
+ $(INS.dir)
+
+$(LUXIBFIRMWARE): $(LUXFIRMWARED) ibfirmware
+ $(INS.file) ibfirmware
+
+$(GENMSG_LUX): $(SRCS)
+ /usr/bin/genmsg -d -p '$(COMPILE.cpp)' -o $@ $(SRCS)
+
+$(GENCAT_PROG): $(GENMSG_LUX)
+ /usr/bin/gencat $@ $(GENMSG_LUX)
+
+$(LUXGENCAT_PROG): $(LUXFIRMWARED) $(GENCAT_PROG)
+ $(INS.file) $(GENCAT_PROG)
+
+install: all $(ROOTUSRSBINPROG) $(LUXIBFIRMWARE) $(LUXGENCAT_PROG)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+clean:
+ $(RM) $(OBJS) $(PROG) $(GENCAT_PROG) $(GENMSG_LUX)
+
+lint: lint_SRCS
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/luxadm/adm.c b/usr/src/cmd/luxadm/adm.c
new file mode 100644
index 0000000000..cb2ad51b43
--- /dev/null
+++ b/usr/src/cmd/luxadm/adm.c
@@ -0,0 +1,1104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+
+/*LINTLIBRARY*/
+
+
+/*
+ * Administration program for SENA
+ * subsystems and individual FC_AL devices.
+ */
+
+/*
+ * I18N message number ranges
+ * This file: 2000 - 2999
+ * Shared common messages: 1 - 1999
+ */
+
+/* #define _POSIX_SOURCE 1 */
+
+/*
+ * These defines are used to map instance number from sf minor node.
+ * They are copied from SF_INST_SHIFT4MINOR and SF_MINOR2INST in sfvar.h.
+ * sfvar.h is not clean for userland use.
+ * When it is cleaned up, these defines will be removed and sfvar.h
+ * will be included in luxadm.h header file.
+ */
+#define LUX_SF_INST_SHIFT4MINOR 6
+#define LUX_SF_MINOR2INST(x) (x >> LUX_SF_INST_SHIFT4MINOR)
+
+/* Includes */
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <termio.h> /* For password */
+#include <sys/scsi/scsi.h>
+
+#include "common.h"
+#include "luxadm.h"
+
+
+/* Global variables */
+char *dtype[16]; /* setting a global for later use. */
+char *whoami;
+int Options;
+const int OPTION_A = 0x00000001;
+const int OPTION_B = 0x00000002;
+const int OPTION_C = 0x00000004;
+const int OPTION_D = 0x00000008;
+const int OPTION_E = 0x00000010;
+const int OPTION_F = 0x00000020;
+const int OPTION_L = 0x00000040;
+const int OPTION_P = 0x00000080;
+const int OPTION_R = 0x00000100;
+const int OPTION_T = 0x00000200;
+const int OPTION_V = 0x00000400;
+const int OPTION_Z = 0x00001000;
+const int OPTION_Y = 0x00002000;
+const int OPTION_CAPF = 0x00004000;
+const int PVERBOSE = 0x00008000;
+const int SAVE = 0x00010000;
+const int EXPERT = 0x00020000;
+
+/*
+ * Given a pointer to a character array, print the character array.
+ * the character array will not necesarily be NULL terminated.
+ *
+ * Inputs:
+ * size - the max number of characters to print
+ * fill_flag - flag when set fills all NULL characters with spaces
+ * Returns:
+ * N/A
+ */
+void
+print_chars(uchar_t *buffer, int size, int fill_flag)
+{
+
+int i;
+
+ for (i = 0; i < size; i++) {
+ if (buffer[i])
+ (void) fprintf(stdout, "%c", buffer[i]);
+ else if (fill_flag)
+ (void) fprintf(stdout, " ");
+ else
+ return;
+ }
+}
+
+/*
+ * Name : memstrstr
+ * Input : pointer to buf1, pointer to buf2, size of buf1, size of buf2
+ * Returns :
+ * Pointer to start of contents-of-buf2 in buf1 if it is found
+ * NULL if buf1 does not contain contents of buf2
+ * Synopsis:
+ * This function works similar to strstr(). The difference is that null
+ * characters in the buffer are treated like any other character. So, buf1
+ * and buf2 can have embedded null characters in them.
+ */
+static char *
+memstrstr(char *s1, char *s2, int size1, int size2)
+{
+ int count1, count2;
+ char *s1_ptr, *s2_ptr;
+
+ count1 = size1; count2 = size2;
+ s1_ptr = s1; s2_ptr = s2;
+
+ if (size2 == 0)
+ return (s1);
+
+ while (count1--) {
+ if (*s1_ptr++ == *s2_ptr++) {
+ if (--count2 == 0)
+ return (s1_ptr - size2);
+ continue;
+ }
+ count2 = size2;
+ s2_ptr = s2;
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * Download host bus adapter FCode to all supported cards.
+ *
+ * Specify a directory that holds the FCode files, or
+ * it will use the default dir. Each file is dealt to
+ * the appropriate function.
+ *
+ * -p prints current versions only, -d specifies a directory to load
+ */
+static int
+adm_fcode(int verbose, char *dir)
+{
+ struct stat statbuf;
+ struct dirent *dirp;
+ DIR *dp;
+ int fp;
+ char fbuf[BUFSIZ];
+ char file[MAXPATHLEN];
+ int retval = 0, strfound = 0;
+ char manf[BUFSIZ];
+
+ /* Find all adapters and print the current FCode version */
+ if (Options & OPTION_P) {
+
+/* SOCAL (SBus) adapters are not supported on x86 */
+#ifndef __x86
+ if (verbose) {
+ (void) fprintf(stdout,
+ MSGSTR(2215, "\n Searching for FC100/S cards:\n"));
+ }
+ retval += fcal_update(Options & PVERBOSE, NULL);
+#endif
+
+ if (verbose) {
+ (void) fprintf(stdout,
+ MSGSTR(2216, "\n Searching for FC100/P, FC100/2P cards:\n"));
+ }
+ retval += q_qlgc_update(Options & PVERBOSE, NULL);
+ if (verbose) {
+ (void) fprintf(stdout,
+ MSGSTR(2503, "\n Searching for Emulex cards:\n"));
+ }
+ retval += emulex_update(NULL);
+
+ /* Send files to the correct function for loading to the HBA */
+ } else {
+
+ if (!dir) {
+ (void) fprintf(stdout, MSGSTR(2251,
+ " Location of Fcode not specified.\n"));
+ return (1);
+
+ } else if (verbose) {
+ (void) fprintf(stdout, MSGSTR(2217,
+ " Using directory %s"), dir);
+ }
+ if (lstat(dir, &statbuf) < 0) {
+ (void) fprintf(stderr, MSGSTR(134,
+ "%s: lstat() failed - %s\n"),
+ dir, strerror(errno));
+ return (1);
+ }
+ if (S_ISDIR(statbuf.st_mode) == 0) {
+ (void) fprintf(stderr,
+ MSGSTR(2218, "Error: %s is not a directory.\n"), dir);
+ return (1);
+ }
+ if ((dp = opendir(dir)) == NULL) {
+ (void) fprintf(stderr, MSGSTR(2219,
+ " Error Cannot open directory %s\n"), dir);
+ return (1);
+ }
+
+ while ((dirp = readdir(dp)) != NULL) {
+ if (strcmp(dirp->d_name, ".") == 0 ||
+ strcmp(dirp->d_name, "..") == 0) {
+ continue;
+ }
+ sprintf(file, "%s/%s", dir, dirp->d_name);
+
+ if ((fp = open(file, O_RDONLY)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(2220,
+ "Error: open() failed to open file "
+ "%s\n"), file);
+ /*
+ * We should just issue an error message and
+ * make an attempt on the next file,
+ * and the open error is still an error
+ * so the retval should be incremented
+ */
+ retval++;
+ continue;
+ }
+ while ((read(fp, fbuf, BUFSIZ)) > 0) {
+ if (memstrstr(fbuf, "SUNW,socal",
+ BUFSIZ, strlen("SUNW,socal"))
+ != NULL) {
+ (void) fprintf(stdout, MSGSTR(2221,
+ "\n Using file: %s\n"), file);
+ retval += fcal_update(
+ Options & PVERBOSE, file);
+ strfound++;
+ break;
+ } else if ((memstrstr(fbuf, "SUNW,ifp",
+ BUFSIZ, strlen("SUNW,ifp"))
+ != NULL) ||
+ (memstrstr(fbuf, "SUNW,qlc",
+ BUFSIZ, strlen("SUNW,qlc"))
+ != NULL)) {
+ (void) fprintf(stdout, MSGSTR(2221,
+ "\n Using file: %s\n"), file);
+ retval += q_qlgc_update(
+ Options & PVERBOSE, file);
+ strfound++;
+ break;
+ }
+ }
+ if (!strfound) {
+ /* check to see if this is an emulex fcode */
+ memset(manf, 0, sizeof (manf));
+ if ((emulex_fcode_reader(fp, "manufacturer",
+ manf,
+ sizeof (manf)) == 0) &&
+ (strncmp(manf, "Emulex", sizeof (manf))
+ == 0)) {
+ retval += emulex_update(file);
+ strfound = 0;
+ } else {
+ (void) fprintf(stderr, MSGSTR(2222,
+ "\nError: %s is not a valid Fcode "
+ "file.\n"), file);
+ retval++;
+ }
+ } else {
+ strfound = 0;
+ }
+ close(fp);
+ }
+ closedir(dp);
+ }
+ return (retval);
+}
+
+/*
+ * Definition of getaction() routine which does keyword parsing
+ *
+ * Operation: A character string containing the ascii cmd to be
+ * parsed is passed in along with an array of structures.
+ * The 1st struct element is a recognizable cmd string, the second
+ * is the minimum number of characters from the start of this string
+ * to succeed on a match. For example, { "offline", 3, ONLINE }
+ * will match "off", "offli", "offline", but not "of" nor "offlinebarf"
+ * The third element is the {usually but not necessarily unique}
+ * integer to return on a successful match. Note: compares are cAsE insensitive.
+ *
+ * To change, extend or use this utility, just add or remove appropriate
+ * lines in the structure initializer below and in the #define s for the
+ * return values.
+ *
+ * N O T E
+ * Do not change the minimum number of characters to produce
+ * a match as someone may be building scripts that use this
+ * feature.
+ */
+struct keyword {
+ char *match; /* Character String to match against */
+ int num_match; /* Minimum chars to produce a match */
+ int ret_code; /* Value to return on a match */
+};
+
+static struct keyword Keywords[] = {
+ {"display", 2, DISPLAY},
+ {"download", 3, DOWNLOAD},
+ {"enclosure_names", 2, ENCLOSURE_NAMES},
+ {"failover", 3, FAILOVER},
+ {"fcal_s_download", 4, FCAL_UPDATE},
+ {"fcode_download", 4, FCODE_UPDATE},
+ {"inquiry", 2, INQUIRY},
+ {"insert_device", 3, INSERT_DEVICE},
+ {"led", 3, LED},
+ {"led_on", 5, LED_ON},
+ {"led_off", 5, LED_OFF},
+ {"led_blink", 5, LED_BLINK},
+ {"password", 2, PASSWORD},
+ {"power_on", 8, POWER_ON},
+ {"power_off", 9, POWER_OFF},
+ {"probe", 2, PROBE},
+ {"qlgc_s_download", 4, QLGC_UPDATE},
+ {"remove_device", 3, REMOVE_DEVICE},
+ {"reserve", 5, RESERVE},
+ {"release", 3, RELEASE},
+ {"set_boot_dev", 5, SET_BOOT_DEV},
+ {"start", 3, START},
+ {"stop", 3, STOP},
+ {"rdls", 2, RDLS},
+ {"bypass", 3, BYPASS},
+ {"enable", 3, ENABLE},
+ {"p_offline", 4, LUX_P_OFFLINE},
+ {"p_online", 4, LUX_P_ONLINE},
+ {"forcelip", 2, FORCELIP},
+ {"dump", 2, DUMP},
+ {"check_file", 2, CHECK_FILE},
+ {"dump_map", 2, DUMP_MAP},
+ {"sysdump", 5, SYSDUMP},
+ {"port", 4, PORT},
+ {"external_loopback", 12, EXT_LOOPBACK},
+ {"internal_loopback", 12, INT_LOOPBACK},
+ {"no_loopback", 11, NO_LOOPBACK},
+ {"version", 2, VERSION},
+ {"create_fabric_device", 2, CREATE_FAB},
+ /* hotplugging device operations */
+ {"online", 2, DEV_ONLINE},
+ {"offline", 2, DEV_OFFLINE},
+ {"dev_getstate", 5, DEV_GETSTATE},
+ {"dev_reset", 5, DEV_RESET},
+ /* hotplugging bus operations */
+ {"bus_quiesce", 5, BUS_QUIESCE},
+ {"bus_unquiesce", 5, BUS_UNQUIESCE},
+ {"bus_getstate", 5, BUS_GETSTATE},
+ {"bus_reset", 9, BUS_RESET},
+ {"bus_resetall", 12, BUS_RESETALL},
+ /* hotplugging "helper" subcommands */
+ { NULL, 0, 0}
+};
+
+#ifndef EOK
+static const int EOK = 0; /* errno.h type success return code */
+#endif
+
+
+/*
+ * function getaction() takes a character string, cmd, and
+ * tries to match it against a passed structure of known cmd
+ * character strings. If a match is found, corresponding code
+ * is returned in retval. Status returns as follows:
+ * EOK = Match found, look for cmd's code in retval
+ * EFAULT = One of passed parameters was bad
+ * EINVAL = cmd did not match any in list
+ */
+static int
+getaction(char *cmd, struct keyword *matches, int *retval)
+{
+int actlen;
+
+ /* Idiot checking of pointers */
+ if (! cmd || ! matches || ! retval ||
+ ! (actlen = strlen(cmd))) /* Is there an cmd ? */
+ return (EFAULT);
+
+ /* Keep looping until NULL match string (end of list) */
+ while (matches->match) {
+ /*
+ * Precedence: Make sure target is no longer than
+ * current match string
+ * and target is at least as long as
+ * minimum # match chars,
+ * then do case insensitive match
+ * based on actual target size
+ */
+ if ((((int)strlen(matches->match)) >= actlen) &&
+ (actlen >= matches->num_match) &&
+ /* can't get strncasecmp to work on SCR4 */
+ /* (strncasecmp(matches->match, cmd, actlen) == 0) */
+ (strncmp(matches->match, cmd, actlen) == 0)) {
+ *retval = matches->ret_code; /* Found our match */
+ return (EOK);
+ } else {
+ matches++; /* Next match string/struct */
+ }
+ } /* End of matches loop */
+ return (EINVAL);
+
+} /* End of getaction() */
+
+/* main functions. */
+int
+main(int argc, char **argv)
+{
+register int c;
+/* getopt varbs */
+extern char *optarg;
+char *optstring = NULL;
+int path_index, err = 0;
+int cmd = 0; /* Cmd verb from cmd line */
+int exit_code = 0; /* exit code for program */
+int temp_fd; /* For -f option */
+char *file_name = NULL;
+int option_t_input;
+char *path_phys = NULL;
+int USE_FCHBA = 0;
+
+ whoami = argv[0];
+
+
+ /*
+ * Enable locale announcement
+ */
+ i18n_catopen();
+
+ while ((c = getopt(argc, argv, "ve"))
+ != EOF) {
+ switch (c) {
+ case 'v':
+ Options |= PVERBOSE;
+ break;
+ case 'e':
+ Options |= EXPERT;
+ break;
+ default:
+ /* Note: getopt prints an error if invalid option */
+ USEAGE()
+ exit(-1);
+ } /* End of switch(c) */
+ }
+ setbuf(stdout, NULL); /* set stdout unbuffered. */
+
+ /*
+ * Build any i18n global variables
+ */
+ dtype[0] = MSGSTR(2192, "Disk device");
+ dtype[1] = MSGSTR(2193, "Tape device");
+ dtype[2] = MSGSTR(2194, "Printer device");
+ dtype[3] = MSGSTR(2195, "Processor device");
+ dtype[4] = MSGSTR(2196, "WORM device");
+ dtype[5] = MSGSTR(2197, "CD-ROM device");
+ dtype[6] = MSGSTR(2198, "Scanner device");
+ dtype[7] = MSGSTR(2199, "Optical memory device");
+ dtype[8] = MSGSTR(2200, "Medium changer device");
+ dtype[9] = MSGSTR(2201, "Communications device");
+ dtype[10] = MSGSTR(107, "Graphic arts device");
+ dtype[11] = MSGSTR(107, "Graphic arts device");
+ dtype[12] = MSGSTR(2202, "Array controller device");
+ dtype[13] = MSGSTR(2203, "SES device");
+ dtype[14] = MSGSTR(71, "Reserved");
+ dtype[15] = MSGSTR(71, "Reserved");
+
+
+
+ /*
+ * Get subcommand.
+ */
+ if ((getaction(argv[optind], Keywords, &cmd)) == EOK) {
+ optind++;
+ if ((cmd != PROBE) && (cmd != FCAL_UPDATE) &&
+ (cmd != QLGC_UPDATE) && (cmd != FCODE_UPDATE) &&
+ (cmd != INSERT_DEVICE) && (cmd != SYSDUMP) && (cmd != AU) &&
+ (cmd != PORT) && (cmd != CREATE_FAB) && (optind >= argc)) {
+ (void) fprintf(stderr,
+ MSGSTR(2204,
+ "Error: enclosure or pathname not specified.\n"));
+ USEAGE();
+ exit(-1);
+ }
+ } else {
+ (void) fprintf(stderr,
+ MSGSTR(2205, "%s: subcommand not specified.\n"),
+ whoami);
+ USEAGE();
+ exit(-1);
+ }
+
+ /* Extract & Save subcommand options */
+ if ((cmd == ENABLE) || (cmd == BYPASS)) {
+ optstring = "Ffrab";
+ } else if (cmd == FCODE_UPDATE) {
+ optstring = "pd:";
+ } else if (cmd == REMOVE_DEVICE) {
+ optstring = "F";
+ } else if (cmd == CREATE_FAB) {
+ optstring = "f:";
+ } else {
+ optstring = "Fryszabepcdlvt:f:w:";
+ }
+ while ((c = getopt(argc, argv, optstring)) != EOF) {
+ switch (c) {
+ case 'a':
+ Options |= OPTION_A;
+ break;
+ case 'b':
+ Options |= OPTION_B;
+ break;
+ case 'c':
+ Options |= OPTION_C;
+ break;
+ case 'd':
+ Options |= OPTION_D;
+ if (cmd == FCODE_UPDATE) {
+ file_name = optarg;
+ }
+ break;
+ case 'e':
+ Options |= OPTION_E;
+ break;
+ case 'f':
+ Options |= OPTION_F;
+ if (!((cmd == ENABLE) || (cmd == BYPASS))) {
+ file_name = optarg;
+ }
+ break;
+ case 'F':
+ Options |= OPTION_CAPF;
+ break;
+ case 'l':
+ Options |= OPTION_L;
+ break;
+ case 'p':
+ Options |= OPTION_P;
+ break;
+ case 'r':
+ Options |= OPTION_R;
+ break;
+ case 's':
+ Options |= SAVE;
+ break;
+ case 't':
+ Options |= OPTION_T;
+ option_t_input = atoi(optarg);
+ break;
+ case 'v':
+ Options |= OPTION_V;
+ break;
+ case 'z':
+ Options |= OPTION_Z;
+ break;
+ case 'y':
+ Options |= OPTION_Y;
+ break;
+ default:
+ /* Note: getopt prints an error if invalid option */
+ USEAGE()
+ exit(-1);
+ } /* End of switch(c) */
+ }
+ if ((cmd != PROBE) && (cmd != FCAL_UPDATE) &&
+ (cmd != QLGC_UPDATE) && (cmd != FCODE_UPDATE) &&
+ (cmd != INSERT_DEVICE) && (cmd != SYSDUMP) &&
+ (cmd != AU) && (cmd != PORT) &&
+ (cmd != CREATE_FAB) && (optind >= argc)) {
+ (void) fprintf(stderr,
+ MSGSTR(2206,
+ "Error: enclosure or pathname not specified.\n"));
+ USEAGE();
+ exit(-1);
+ }
+ path_index = optind;
+
+ /*
+ * Check if the file supplied with the -f option is valid
+ * Some sub commands (bypass for example) use the -f option
+ * for other reasons. In such cases, "file_name" should be
+ * NULL.
+ */
+ if ((file_name != NULL) && (Options & OPTION_F)) {
+ if ((temp_fd = open(file_name, O_RDONLY)) == -1) {
+ perror(file_name);
+ exit(-1);
+ } else {
+ close(temp_fd);
+ }
+ }
+
+ /* Determine which mode to operate in (FC-HBA or original) */
+ USE_FCHBA = use_fchba();
+
+ switch (cmd) {
+ case DISPLAY:
+ if (Options &
+ ~(PVERBOSE | OPTION_A | OPTION_Z | OPTION_R |
+ OPTION_P | OPTION_V | OPTION_L | OPTION_E | OPTION_T)) {
+ USEAGE();
+ exit(-1);
+ }
+ /* Display object(s) */
+ if (USE_FCHBA) {
+ exit_code = fchba_display_config(&argv[path_index],
+ option_t_input, argc - path_index);
+ } else {
+ exit_code = adm_display_config(&argv[path_index]);
+ }
+ break;
+
+ case DOWNLOAD:
+ if (Options &
+ ~(PVERBOSE | OPTION_F | SAVE)) {
+ USEAGE();
+ exit(-1);
+ }
+ adm_download(&argv[path_index], file_name);
+ break;
+
+ case ENCLOSURE_NAMES:
+ if (Options & ~PVERBOSE) {
+ USEAGE();
+ exit(-1);
+ }
+ up_encl_name(&argv[path_index], argc);
+ break;
+
+ case FAILOVER:
+ if (Options & ~PVERBOSE) {
+ USEAGE();
+ exit(-1);
+ }
+ adm_failover(&argv[path_index]);
+ break;
+
+ case INQUIRY:
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ if (USE_FCHBA) {
+ exit_code = fchba_inquiry(&argv[path_index]);
+ } else {
+ exit_code = adm_inquiry(&argv[path_index]);
+ }
+ break;
+
+ case PROBE:
+ if (Options & ~(PVERBOSE | OPTION_P)) {
+ USEAGE();
+ exit(-1);
+ }
+ /*
+ * A special check just in case someone entered
+ * any characters after the -p or the probe.
+ *
+ * (I know, a nit.)
+ */
+ if (((Options & PVERBOSE) && (Options & OPTION_P) &&
+ (argc != 4)) ||
+ (!(Options & PVERBOSE) && (Options & OPTION_P) &&
+ (argc != 3)) ||
+ ((Options & PVERBOSE) && (!(Options & OPTION_P)) &&
+ (argc != 3)) ||
+ (!(Options & PVERBOSE) && (!(Options & OPTION_P)) &&
+ (argc != 2))) {
+ (void) fprintf(stderr,
+ MSGSTR(114, "Error: Incorrect number of arguments.\n"));
+ (void) fprintf(stderr, MSGSTR(2208,
+ "Usage: %s [-v] subcommand [option]\n"), whoami);
+ exit(-1);
+ }
+ if (USE_FCHBA) {
+ exit_code = fchba_non_encl_probe();
+ } else {
+ pho_probe();
+ non_encl_probe();
+ }
+ break;
+
+ case FCODE_UPDATE: /* Update Fcode in all cards */
+ if ((Options & ~(PVERBOSE)) &
+ ~(OPTION_P | OPTION_D) || argv[path_index]) {
+ USEAGE();
+ exit(-1);
+ }
+ if (!((Options & (OPTION_P | OPTION_D)) &&
+ !((Options & OPTION_P) && (Options & OPTION_D)))) {
+ USEAGE();
+ exit(-1);
+ }
+ if (adm_fcode(Options & PVERBOSE, file_name) != 0) {
+ exit(-1);
+ }
+ break;
+
+ case QLGC_UPDATE: /* Update Fcode in PCI HBA card(s) */
+ if ((Options & ~(PVERBOSE)) & ~(OPTION_F) ||
+ argv[path_index]) {
+ USEAGE();
+ exit(-1);
+ }
+ if (q_qlgc_update(Options & PVERBOSE, file_name) != 0) {
+ exit(-1);
+ }
+ break;
+
+ case FCAL_UPDATE: /* Update Fcode in Sbus soc+ card */
+ if ((Options & ~(PVERBOSE)) & ~(OPTION_F) ||
+ argv[path_index]) {
+ USEAGE();
+ exit(-1);
+ }
+ exit_code = fcal_update(Options & PVERBOSE, file_name);
+ break;
+
+ case SET_BOOT_DEV: /* Set boot-device variable in nvram */
+ exit_code = setboot(Options & OPTION_Y,
+ Options & PVERBOSE, argv[path_index]);
+ break;
+
+ case LED:
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ adm_led(&argv[path_index], L_LED_STATUS);
+ break;
+ case LED_ON:
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ adm_led(&argv[path_index], L_LED_ON);
+ break;
+ case LED_OFF:
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ adm_led(&argv[path_index], L_LED_OFF);
+ break;
+ case LED_BLINK:
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ adm_led(&argv[path_index], L_LED_RQST_IDENTIFY);
+ break;
+ case PASSWORD:
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ up_password(&argv[path_index]);
+ break;
+
+ case RESERVE:
+
+ if (Options & (~PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ VERBPRINT(MSGSTR(2209,
+ " Reserving: \n %s\n"), argv[path_index]);
+ if (USE_FCHBA) {
+ struct stat sbuf;
+ /* Just stat the argument and make sure it exists */
+ if (stat(argv[path_index], &sbuf) < 0) {
+ (void) fprintf(stderr, "%s: ", whoami);
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ exit(-1);
+ }
+ path_phys = argv[path_index];
+ if (err = scsi_reserve(path_phys)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ } else {
+ exit_code = adm_reserve(argv[path_index]);
+ }
+ break;
+
+ case RELEASE:
+ if (Options & (~PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ VERBPRINT(MSGSTR(2210, " Canceling Reservation for:\n %s\n"),
+ argv[path_index]);
+ if (USE_FCHBA) {
+ struct stat sbuf;
+ /* Just stat the argument and make sure it exists */
+ if (stat(argv[path_index], &sbuf) < 0) {
+ (void) fprintf(stderr, "%s: ", whoami);
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ exit(-1);
+ }
+ path_phys = argv[path_index];
+ if (err = scsi_release(path_phys)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ } else {
+ exit_code = adm_release(argv[path_index]);
+ }
+ break;
+
+ case START:
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ exit_code = adm_start(&argv[path_index]);
+ break;
+
+ case STOP:
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ exit_code = adm_stop(&argv[path_index]);
+ break;
+
+ case POWER_OFF:
+ if (Options & ~(PVERBOSE | OPTION_CAPF)) {
+ USEAGE();
+ exit(-1);
+ }
+ exit_code = adm_power_off(&argv[path_index], 1);
+ break;
+
+ case POWER_ON:
+ if (Options & (~PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ exit_code = adm_power_off(&argv[path_index], 0);
+ break;
+
+ /*
+ * EXPERT commands.
+ */
+
+ case FORCELIP:
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ E_USEAGE();
+ exit(-1);
+ }
+ exit_code = adm_forcelip(&argv[path_index]);
+ break;
+
+ case BYPASS:
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT |
+ OPTION_CAPF | OPTION_A | OPTION_B | OPTION_F |
+ OPTION_R)) || !(Options & (OPTION_A | OPTION_B)) ||
+ ((Options & OPTION_A) && (Options & OPTION_B))) {
+ E_USEAGE();
+ exit(-1);
+ }
+ adm_bypass_enable(&argv[path_index], 1);
+ break;
+
+ case ENABLE:
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT |
+ OPTION_CAPF | OPTION_A | OPTION_B | OPTION_F |
+ OPTION_R)) || !(Options & (OPTION_A | OPTION_B)) ||
+ ((Options & OPTION_A) && (Options & OPTION_B))) {
+ E_USEAGE();
+ exit(-1);
+ }
+ adm_bypass_enable(&argv[path_index], 0);
+ break;
+ case LUX_P_OFFLINE: /* Offline a port */
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ E_USEAGE();
+ exit(-1);
+ }
+ exit_code = adm_port_offline_online(&argv[path_index],
+ LUX_P_OFFLINE);
+ break;
+
+ case LUX_P_ONLINE: /* Online a port */
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ E_USEAGE();
+ exit(-1);
+ }
+ exit_code = adm_port_offline_online(&argv[path_index],
+ LUX_P_ONLINE);
+ break;
+
+ case RDLS:
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ E_USEAGE();
+ exit(-1);
+ }
+ if (USE_FCHBA) {
+ exit_code = fchba_display_link_status(&argv[path_index]);
+ } else {
+ display_link_status(&argv[path_index]);
+ }
+ break;
+
+ case CREATE_FAB:
+ if (!(Options & (EXPERT | OPTION_F)) ||
+ (Options & ~(PVERBOSE | EXPERT | OPTION_F))) {
+ E_USEAGE();
+ exit(-1);
+ }
+ if (read_repos_file(file_name) != 0) {
+ exit(-1);
+ }
+ break;
+
+ /*
+ * Undocumented commands.
+ */
+
+ case CHECK_FILE: /* Undocumented Cmd */
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ exit_code = adm_check_file(&argv[path_index],
+ (Options & PVERBOSE));
+ break;
+
+ case DUMP: /* Undocumented Cmd */
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ USEAGE();
+ exit(-1);
+ }
+ dump(&argv[path_index]);
+ break;
+
+ case DUMP_MAP: /* Undocumented Cmd */
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ USEAGE();
+ exit(-1);
+ }
+ if (USE_FCHBA) {
+ exit_code = fchba_dump_map(&argv[path_index]);
+ } else {
+ dump_map(&argv[path_index]);
+ }
+ break;
+
+ case SYSDUMP:
+ if (Options & ~(PVERBOSE)) {
+ USEAGE();
+ exit(-1);
+ }
+ if (err = sysdump(Options & PVERBOSE)) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+ break;
+
+ case PORT: /* Undocumented command */
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ USEAGE();
+ exit(-1);
+ }
+ if (USE_FCHBA) {
+ exit_code = fchba_display_port(Options & PVERBOSE);
+ } else {
+ exit_code = adm_display_port(Options & PVERBOSE);
+ }
+ break;
+
+ case EXT_LOOPBACK:
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ USEAGE();
+ exit(-1);
+ }
+ if (adm_port_loopback(argv[path_index], EXT_LOOPBACK) < 0) {
+ exit(-1);
+ }
+ break;
+
+ case INT_LOOPBACK:
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ USEAGE();
+ exit(-1);
+ }
+ if (adm_port_loopback(argv[path_index], INT_LOOPBACK) < 0) {
+ exit(-1);
+ }
+ break;
+
+ case NO_LOOPBACK:
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ USEAGE();
+ exit(-1);
+ }
+ if (adm_port_loopback(argv[path_index], NO_LOOPBACK) < 0) {
+ exit(-1);
+ }
+ break;
+
+ case VERSION:
+ break;
+
+
+ case INSERT_DEVICE:
+ if (argv[path_index] == NULL) {
+ if ((err = h_insertSena_fcdev()) != 0) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+ } else if ((err = hotplug(INSERT_DEVICE,
+ &argv[path_index],
+ Options & PVERBOSE,
+ Options & OPTION_CAPF)) != 0) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ break;
+ case REMOVE_DEVICE:
+ if (err = hotplug(REMOVE_DEVICE, &argv[path_index],
+ Options & PVERBOSE, Options & OPTION_CAPF)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ break;
+
+ /* for hotplug device operations */
+ case DEV_ONLINE:
+ case DEV_OFFLINE:
+ case DEV_GETSTATE:
+ case DEV_RESET:
+ case BUS_QUIESCE:
+ case BUS_UNQUIESCE:
+ case BUS_GETSTATE:
+ case BUS_RESET:
+ case BUS_RESETALL:
+ if (!(Options & EXPERT) || (Options & ~(PVERBOSE | EXPERT))) {
+ E_USEAGE();
+ exit(-1);
+ }
+ if (USE_FCHBA) {
+ if (fchba_hotplug_e(cmd, &argv[path_index],
+ Options & PVERBOSE, Options & OPTION_CAPF) != 0) {
+ exit(-1);
+ }
+ } else {
+ if (hotplug_e(cmd, &argv[path_index],
+ Options & PVERBOSE, Options & OPTION_CAPF) != 0) {
+ exit(-1);
+ }
+ }
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ MSGSTR(2213, "%s: subcommand decode failed.\n"),
+ whoami);
+ USEAGE();
+ exit(-1);
+ }
+ return (exit_code);
+}
diff --git a/usr/src/cmd/luxadm/common.h b/usr/src/cmd/luxadm/common.h
new file mode 100644
index 0000000000..1a96198d25
--- /dev/null
+++ b/usr/src/cmd/luxadm/common.h
@@ -0,0 +1,495 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * PHOTON CONFIGURATION MANAGER
+ * Common definitions
+ */
+
+/*
+ * I18N message number ranges
+ * This file: 12500 - 12999
+ * Shared common messages: 1 - 1999
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+
+
+
+/*
+ * Include any headers you depend on.
+ */
+#include <sys/types.h>
+#include <sys/scsi/adapters/scsi_vhci.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*#ifdef _BIG_ENDIAN
+#define htonll(x) (x)
+#define ntohll(x) (x)
+#else
+#define htonll(x) ((((unsigned long long)htonl(x)) << 32) + htonl(x >> 32))
+#define ntohll(x) ((((unsigned long long)ntohl(x)) << 32) + ntohl(x >> 32))
+#endif*/
+
+
+extern char *p_error_msg_ptr;
+
+#ifdef __x86
+#include <nl_types.h>
+extern nl_catd l_catd;
+#define L_SET1 1 /* catalog set number */
+#define MSGSTR(Num, Str) catgets(l_catd, L_SET1, Num, Str)
+#endif
+
+
+/* Defines */
+#define USEAGE() {(void) fprintf(stderr, MSGSTR(12500, \
+ "Usage: %s [-v] subcommand [option...]" \
+ " {enclosure[,dev]... | pathname...}\n"), \
+ whoami); \
+ (void) fflush(stderr); }
+
+#define E_USEAGE() {(void) fprintf(stderr, MSGSTR(12501, \
+ "Usage: %s [-v] -e subcommand [option...]" \
+ " {enclosure[,dev]... | pathname...}\n"), \
+ whoami); \
+ (void) fflush(stderr); }
+
+#define VERBPRINT if (Options & PVERBOSE) (void) printf
+
+#define L_ERR_PRINT \
+ if (p_error_msg_ptr == NULL) { \
+ perror(MSGSTR(12502, "Error")); \
+ } else { \
+ (void) fprintf(stderr, MSGSTR(12503, "Error: %s"), p_error_msg_ptr); \
+ } \
+ p_error_msg_ptr = NULL;
+
+#define P_ERR_PRINT if (p_error_msg_ptr == NULL) { \
+ perror(whoami); \
+ } else { \
+ (void) fprintf(stderr, MSGSTR(12504, "Error: %s"), p_error_msg_ptr); \
+ } \
+ p_error_msg_ptr = NULL;
+
+
+/* Display extended mode page information. */
+#ifndef MODEPAGE_CACHING
+#undef MODEPAGE_CACHING
+#define MODEPAGE_CACHING 0x08
+#endif
+
+
+/* Primary commands */
+#define ENCLOSURE_NAMES 100
+#define DISPLAY 101
+#define DOWNLOAD 102
+#define FAST_WRITE 400 /* SSA */
+#define FAILOVER 500
+#define FC_UPDATE 401 /* SSA */
+#define FCAL_UPDATE 103 /* Update the Fcode on Sbus soc card */
+#define FCODE_UPDATE 117 /* Update the Fcode on all cards */
+#define QLGC_UPDATE 116 /* Update the Fcode on PCI card(s) */
+#define INQUIRY 105
+#define LED 107
+#define LED_ON 108
+#define LED_OFF 109
+#define LED_BLINK 110
+#define NVRAM_DATA 402 /* SSA */
+#define POWER_OFF 403 /* SSA */
+#define POWER_ON 111
+#define PASSWORD 112
+#define PURGE 404 /* SSA */
+#define PERF_STATISTICS 405 /* SSA */
+#define PROBE 113
+#define RELEASE 210
+#define RESERVE 211
+#define START 213
+#define STOP 214
+#define SYNC_CACHE 406 /* SSA */
+#define SET_BOOT_DEV 115 /* Set the boot-device variable in nvram */
+#define INSERT_DEVICE 106 /* Hot plug */
+#define REMOVE_DEVICE 114 /* hot plug */
+
+/* Device hotplugging */
+#define REPLACE_DEVICE 150
+#define DEV_ONLINE 155
+#define DEV_OFFLINE 156
+#define DEV_GETSTATE 157
+#define DEV_RESET 158
+#define BUS_QUIESCE 160
+#define BUS_UNQUIESCE 161
+#define BUS_GETSTATE 162
+#define BUS_RESET 163
+#define BUS_RESETALL 164
+
+#define SKIP 111
+#define QUIT 222
+
+#define L_LED_STATUS 0x00
+#define L_LED_RQST_IDENTIFY 0x01
+#define L_LED_ON 0x02
+#define L_LED_OFF 0x04
+
+
+/* Enclosure Specific */
+#define ALARM 407 /* SSA */
+#define ALARM_OFF 408 /* SSA */
+#define ALARM_ON 409 /* SSA */
+#define ALARM_SET 410 /* SSA */
+#define ENV_DISPLAY 411 /* SSA */
+
+/* Expert commands */
+#define RDLS 215
+#define P_BYPASS 218
+#define P_ENABLE 219
+#define BYPASS 220
+#define ENABLE 221
+#define FORCELIP 222
+#define LUX_P_OFFLINE 223
+#define LUX_P_ONLINE 224
+#define EXT_LOOPBACK 225
+#define INT_LOOPBACK 226
+#define NO_LOOPBACK 227
+#define CREATE_FAB 228
+
+/* Undocumented commands */
+#define DUMP 300
+#define CHECK_FILE 301 /* Undocumented - Check download file */
+#define DUMP_MAP 302 /* Dump map of loop */
+#define VERSION 303 /* undocumented */
+#define AU 304 /* undocumented */
+#define PORT 305 /* undocumented */
+
+/* Undocumented diagnostic subcommands */
+#define SYSDUMP 350
+
+
+/* SSA - for adm_download */
+/* #define SSAFIRMWARE_FILE "/usr/lib/firmware/ssa/ssafirmware" */
+
+/* Global variables */
+extern char *whoami;
+extern int Options;
+extern const int OPTION_A;
+extern const int OPTION_B;
+extern const int OPTION_C;
+extern const int OPTION_D;
+extern const int OPTION_E;
+extern const int OPTION_F;
+extern const int OPTION_L;
+extern const int OPTION_P;
+extern const int OPTION_R;
+extern const int OPTION_T;
+extern const int OPTION_V;
+extern const int OPTION_Z;
+extern const int OPTION_Y;
+extern const int OPTION_CAPF;
+extern const int PVERBOSE;
+extern const int SAVE;
+extern const int EXPERT;
+
+#define TARGET_ID(box_id, f_r, slot) \
+ ((box_id | ((f_r == 'f' ? 0 : 1) << 4)) | (slot + 2))
+
+#define NEWER(time1, time2) (time1.tv_sec > time2.tv_sec)
+
+/* used to set the behavior of get_slash_devices_from_osDevName. */
+#define STANDARD_DEVNAME_HANDLING 1
+#define NOT_IGNORE_DANGLING_LINK 2
+
+#include <hbaapi.h>
+#ifndef __x86
+#include <sys/scsi/generic/mode.h>
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/impl/uscsi.h>
+#include <g_state.h>
+#include <stgcom.h>
+#include <l_common.h>
+#else
+typedef struct l_inquiry_inq_2 {
+#if defined(_BIT_FIELDS_HTOL)
+ uchar_t inq_2_reladdr : 1, /* relative addressing */
+ inq_wbus32 : 1, /* 32 bit wide data xfers */
+ inq_wbus16 : 1, /* 16 bit wide data xfers */
+ inq_sync : 1, /* synchronous data xfers */
+ inq_linked : 1, /* linked commands */
+ inq_res1 : 1, /* reserved */
+ inq_cmdque : 1, /* command queueing */
+ inq_sftre : 1; /* Soft Reset option */
+#else
+ uchar_t inq_sftre : 1, /* Soft Reset option */
+ inq_cmdque : 1, /* command queueing */
+ inq_res1 : 1, /* reserved */
+ inq_linked : 1, /* linked commands */
+ inq_sync : 1, /* synchronous data xfers */
+ inq_wbus16 : 1, /* 16 bit wide data xfers */
+ inq_wbus32 : 1, /* 32 bit wide data xfers */
+ inq_2_reladdr : 1; /* relative addressing */
+#endif /* _BIT_FIELDS_HTOL */
+} L_inq_2;
+
+typedef struct l_inquiry_inq_3 {
+#if defined(_BIT_FIELDS_HTOL)
+ uchar_t inq_3_reladdr : 1, /* relative addressing */
+ inq_SIP_2 : 3, /* Interlocked Protocol */
+ inq_3_linked : 1, /* linked commands */
+ inq_trandis : 1, /* Transfer Disable */
+ inq_3_cmdque : 1, /* command queueing */
+ inq_SIP_3 : 1; /* Interlocked Protocol */
+#else
+ uchar_t inq_SIP_3 : 1, /* Interlocked Protocol */
+ inq_3_cmdque : 1, /* command queueing */
+ inq_trandis : 1, /* Transfer Disable */
+ inq_3_linked : 1, /* linked commands */
+ inq_SIP_2 : 3, /* Interlocked Protocol */
+ inq_3_reladdr : 1; /* relative addressing */
+#endif /* _BIT_FIELDS_HTOL */
+} L_inq_3;
+
+typedef struct l_inquiry_struct {
+ /*
+ * byte 0
+ *
+ * Bits 7-5 are the Peripheral Device Qualifier
+ * Bits 4-0 are the Peripheral Device Type
+ *
+ */
+ uchar_t inq_dtype;
+ /* byte 1 */
+#if defined(_BIT_FIELDS_HTOL)
+ uchar_t inq_rmb : 1, /* removable media */
+ inq_qual : 7; /* device type qualifier */
+#else
+ uchar_t inq_qual : 7, /* device type qualifier */
+ inq_rmb : 1; /* removable media */
+#endif /* _BIT_FIELDS_HTOL */
+
+ /* byte 2 */
+#if defined(_BIT_FIELDS_HTOL)
+ uchar_t inq_iso : 2, /* ISO version */
+ inq_ecma : 3, /* ECMA version */
+ inq_ansi : 3; /* ANSI version */
+#else
+ uchar_t inq_ansi : 3, /* ANSI version */
+ inq_ecma : 3, /* ECMA version */
+ inq_iso : 2; /* ISO version */
+#endif /* _BIT_FIELDS_HTOL */
+
+ /* byte 3 */
+#define inq_aerc inq_aenc /* SCSI-3 */
+#if defined(_BIT_FIELDS_HTOL)
+ uchar_t inq_aenc : 1, /* async event notification cap. */
+ inq_trmiop : 1, /* supports TERMINATE I/O PROC msg */
+ inq_normaca : 1, /* Normal ACA Supported */
+ : 1, /* reserved */
+ inq_rdf : 4; /* response data format */
+#else
+ uchar_t inq_rdf : 4, /* response data format */
+ : 1, /* reserved */
+ inq_normaca : 1, /* Normal ACA Supported */
+ inq_trmiop : 1, /* supports TERMINATE I/O PROC msg */
+ inq_aenc : 1; /* async event notification cap. */
+#endif /* _BIT_FIELDS_HTOL */
+
+ /* bytes 4-7 */
+ uchar_t inq_len; /* additional length */
+ uchar_t : 8; /* reserved */
+#if defined(_BIT_FIELDS_HTOL)
+ uchar_t : 2, /* reserved */
+ inq_port : 1, /* Only defined when dual_p set */
+ inq_dual_p : 1, /* Dual Port */
+ inq_mchngr : 1, /* Medium Changer */
+ inq_SIP_1 : 3; /* Interlocked Protocol */
+#else
+ uchar_t inq_SIP_1 : 3, /* Interlocked Protocol */
+ inq_mchngr : 1, /* Medium Changer */
+ inq_dual_p : 1, /* Dual Port */
+ inq_port : 1, /* Only defined when dual_p set */
+ : 2; /* reserved */
+#endif /* _BIT_FIELDS_HTOL */
+
+ union {
+ L_inq_2 inq_2;
+ L_inq_3 inq_3;
+ } ui;
+
+
+ /* bytes 8-35 */
+
+ uchar_t inq_vid[8]; /* vendor ID */
+
+ uchar_t inq_pid[16]; /* product ID */
+
+ uchar_t inq_revision[4]; /* product revision level */
+
+ /*
+ * Bytes 36-55 are vendor-specific parameter bytes
+ */
+
+ /* SSA specific definitions */
+ /* bytes 36 - 39 */
+#define inq_ven_specific_1 inq_firmware_rev
+ uchar_t inq_firmware_rev[4]; /* firmware revision level */
+
+ /* bytes 40 - 51 */
+ uchar_t inq_serial[12]; /* serial number, not used any more */
+
+ /* bytes 52-53 */
+ uchar_t inq_res2[2];
+
+ /* byte 54, 55 */
+ uchar_t inq_ssa_ports; /* number of ports */
+ uchar_t inq_ssa_tgts; /* number of targets */
+
+ /*
+ * Bytes 56-95 are reserved.
+ */
+ uchar_t inq_res3[40];
+ /*
+ * 96 to 'n' are vendor-specific parameter bytes
+ */
+ uchar_t inq_box_name[32];
+ uchar_t inq_avu[256];
+} L_inquiry;
+#define HEX_ONLY 0 /* Print Hex only */
+#define HEX_ASCII 1 /* Print Hex and Ascii */
+#define WWN_SIZE 8 /* # of bytes to dump per line */
+
+/* NOTE: These command op codes are not defined in commands.h */
+#define SCMD_SYNC_CACHE 0x35
+#define SCMD_LOG_SENSE 0x4d
+#define SCMD_PERS_RESERV_IN 0x5e
+#define SCMD_PERS_RESERV_OUT 0x5f
+
+typedef struct rls_payload {
+ uint_t rls_portno;
+ uint_t rls_linkfail;
+ uint_t rls_syncfail;
+ uint_t rls_sigfail;
+ uint_t rls_primitiverr;
+ uint_t rls_invalidword;
+ uint_t rls_invalidcrc;
+} rls_payload_t;
+
+typedef struct l_inquiry00_struct {
+#if defined(_BIT_FIELDS_LTOH)
+uchar_t qual :3,
+ dtype :5;
+#else
+uchar_t dtype :5,
+ qual :3;
+#endif /* _BIT_FIELDS_LTOH */
+uchar_t page_code;
+uchar_t reserved;
+uchar_t len;
+uchar_t page_list[251];
+} L_inquiry00;
+
+#define MIN(a, b) (a < b ? a : b)
+#define ER_DPRINTF if (getenv("_LUX_ER_DEBUG") != NULL) (void) printf
+#define O_DPRINTF if (getenv("_LUX_O_DEBUG") != NULL) (void) printf
+#define P_DPRINTF if (getenv("_LUX_P_DEBUG") != NULL) (void) printf
+#define R_DPRINTF if (getenv("_LUX_R_DEBUG") != NULL) (void) printf
+#define I_DPRINTF if (getenv("_LUX_I_DEBUG") != NULL) (void) printf
+#define S_DPRINTF if (getenv("_LUX_S_DEBUG") != NULL) (void) printf
+#define RETRY_FCIO_IOCTL 360
+#define WAIT_FCIO_IOCTL 250000 /* 1/4 of a second */
+
+#endif /* __x86 */
+
+
+int adm_display_config(char **argv);
+void adm_download(char **argv, char *file_name);
+void up_encl_name(char **argv, int argc);
+void adm_failover(char **argv);
+void pho_probe();
+void non_encl_probe();
+void adm_led(char **argv, int led_action);
+void up_password(char **argv);
+int adm_start(char **argv);
+int adm_stop(char **argv);
+int adm_power_off(char **argv, int off_flag);
+int adm_forcelip(char **argv);
+void adm_bypass_enable(char **argv, int bypass_flag);
+int adm_port_offline_online(char *argv[], int flag);
+void display_link_status(char **argv);
+int read_repos_file(char *repos_filename);
+int adm_check_file(char **argv, int flag);
+void dump(char **argv);
+void dump_map(char **argv);
+int adm_port_loopback(char *portpath, int flag);
+int adm_inquiry(char **argv);
+int adm_display_port(int verbose);
+
+int adm_reserve(char *path);
+int adm_release(char *path);
+void i18n_catopen();
+void dump_hex_data(char *, uchar_t *, int, int);
+void print_errString(int, char *);
+void print_chars(uchar_t *, int, int);
+void print_inq_data(char *, char *, L_inquiry, uchar_t *, size_t);
+void print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
+ uchar_t dtype_prop);
+void print_private_loop_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
+ uchar_t dtype_prop);
+char *get_errString(int errornum);
+int cmp_raw_wwn(uchar_t *wwn_1, uchar_t *wwn_2);
+
+/* routines in fchba*.c files */
+int fchba_display_port(int verbose);
+int fchba_display_config(char **argv, int option_t_input, int argc);
+char *get_slash_devices_from_osDevName(char *osDevName, int flag);
+int get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc,
+ int *path_count);
+int get_mode_page(char *path, uchar_t **pg_buf);
+int scsi_mode_sense_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t pc,
+ uchar_t page_code);
+int scsi_release(char *path);
+int scsi_reserve(char *path);
+int is_path(char *arg);
+int is_wwn(char *arg);
+int loadLibrary();
+uint32_t getNumberOfAdapters();
+int getAdapterAttrs(HBA_HANDLE handle,
+ char *name, HBA_ADAPTERATTRIBUTES *attrs);
+int getAdapterPortAttrs(HBA_HANDLE handle, char *name, int portIndex,
+ HBA_PORTATTRIBUTES *attrs);
+HBA_STATUS fetch_mappings(HBA_HANDLE handle, HBA_WWN pwwn,
+ HBA_FCPTARGETMAPPINGV2 **map);
+int match_mappings(char *compare, HBA_FCPTARGETMAPPINGV2 *map);
+uint64_t wwnConversion(uchar_t *wwn);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMMON_H */
diff --git a/usr/src/cmd/luxadm/diag.c b/usr/src/cmd/luxadm/diag.c
new file mode 100644
index 0000000000..6336b5530d
--- /dev/null
+++ b/usr/src/cmd/luxadm/diag.c
@@ -0,0 +1,262 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*LINTLIBRARY*/
+
+
+/*
+ * This module is part of the photon library
+ */
+
+/*
+ * I18N message number ranges
+ * This file: 3500 - 3999
+ * Shared common messages: 1 - 1999
+ */
+
+/* Includes */
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/scsi/scsi.h>
+#include <nl_types.h>
+#include <strings.h>
+#include <sys/ddi.h> /* for max */
+#include <libdevice.h>
+#include <sys/fibre-channel/fcio.h>
+#define _SYS_FC4_FCAL_LINKAPP_H
+#include <sys/fc4/fcio.h>
+#include <sys/devctl.h>
+#include <sys/scsi/targets/sesio.h>
+#include <l_common.h>
+#include <l_error.h>
+#include <a_state.h>
+#include <a5k.h>
+#include <stgcom.h>
+#include "luxadm.h"
+
+
+/* Defines */
+#define VERBPRINT if (verbose) (void) printf
+
+
+
+static int
+print_ssd_in_box(char *ses_path, uchar_t *box_name, int verbose)
+{
+L_state l_state;
+int err, i;
+struct dlist *ml;
+WWN_list *wwn_list, *wwn_list_ptr;
+char *s;
+L_inquiry inq;
+int enc_type = 0;
+
+ wwn_list = wwn_list_ptr = NULL;
+ if (err = l_get_status(ses_path, &l_state, verbose)) {
+ return (err);
+ }
+
+ if (err = g_get_wwn_list(&wwn_list, verbose)) {
+ return (err);
+
+ }
+
+ /* Need to find out whether this device is a daktari */
+ if (g_get_inquiry(ses_path, &inq)) {
+ return (L_SCSI_ERROR);
+ }
+ if ((strncmp((char *)&inq.inq_pid[0], DAK_OFF_NAME,
+ strlen(DAK_OFF_NAME)) == 0) ||
+ (strncmp((char *)&inq.inq_pid[0], DAK_PROD_STR,
+ strlen(DAK_PROD_STR)) == 0)) {
+ enc_type = DAK_ENC_TYPE;
+ }
+ for (i = 0; i < (int)l_state.total_num_drv/2; i++) {
+ if (l_state.drv_front[i].ib_status.code != S_NOT_INSTALLED) {
+
+ ml = l_state.drv_front[i].g_disk_state.multipath_list;
+ while (ml) {
+ for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
+ wwn_list_ptr = wwn_list_ptr->wwn_next) {
+ s = wwn_list_ptr->physical_path;
+ if (strcmp((char *)s,
+ ml->dev_path) == 0) {
+ (void) fprintf(stdout, MSGSTR(3500,
+ "%-80.80s %-17.17s %-17.17s %-22.22s "),
+ wwn_list_ptr->physical_path,
+ wwn_list_ptr->node_wwn_s,
+ wwn_list_ptr->port_wwn_s,
+ wwn_list_ptr->logical_path);
+ if (enc_type == DAK_ENC_TYPE) {
+ (void) fprintf(stdout,
+ MSGSTR(3513, "%s,s%d\n"),
+ box_name, i);
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(3501, "%s,f%d\n"),
+ box_name, i);
+ }
+ }
+ }
+ ml = ml->next;
+ }
+
+ }
+ }
+ for (i = 0; i < (int)l_state.total_num_drv/2; i++) {
+ if (l_state.drv_rear[i].ib_status.code != S_NOT_INSTALLED) {
+
+ ml = l_state.drv_rear[i].g_disk_state.multipath_list;
+ while (ml) {
+ wwn_list_ptr = wwn_list;
+ for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
+ wwn_list_ptr = wwn_list_ptr->wwn_next) {
+ s = wwn_list_ptr->physical_path;
+ if (strcmp((char *)s,
+ ml->dev_path) == 0) {
+ (void) fprintf(stdout, MSGSTR(3502,
+ "%-80.80s %-17.17s %-17.17s %-22.22s "),
+ wwn_list_ptr->physical_path,
+ wwn_list_ptr->node_wwn_s,
+ wwn_list_ptr->port_wwn_s,
+ wwn_list_ptr->logical_path);
+ if (enc_type == DAK_ENC_TYPE) {
+ (void) fprintf(stdout,
+ MSGSTR(3513, "%s,s%d\n"),
+ box_name,
+ (int)l_state.total_num_drv/2+i);
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(3503, "%s,r%d\n"),
+ box_name, i);
+ }
+ }
+ }
+ ml = ml->next;
+ }
+
+ }
+ }
+ g_free_wwn_list(&wwn_list);
+ return (0);
+}
+
+
+
+int
+sysdump(int verbose)
+{
+int err;
+
+Box_list *b_list = NULL;
+Box_list *o_list = NULL;
+Box_list *c_list = NULL;
+int multi_print_flag;
+
+ if (err = l_get_box_list(&b_list, verbose)) {
+ return (err);
+ }
+ if (b_list == NULL) {
+ (void) fprintf(stdout,
+ MSGSTR(93, "No %s enclosures found "
+ "in /dev/es\n"), ENCLOSURE_PROD_NAME);
+ } else {
+ o_list = b_list;
+ while (b_list != NULL) {
+ /* Don't re-print multiple paths */
+ c_list = o_list;
+ multi_print_flag = 0;
+ while (c_list != b_list) {
+ if (strcmp(c_list->b_node_wwn_s,
+ b_list->b_node_wwn_s) == 0) {
+ multi_print_flag = 1;
+ break;
+ }
+ c_list = c_list->box_next;
+ }
+ if (multi_print_flag) {
+ b_list = b_list->box_next;
+ continue;
+ }
+ /* Found enclosure */
+
+ (void) fprintf(stdout,
+ MSGSTR(3504, "Enclosure name:%s Node WWN:%s\n"),
+ b_list->b_name, b_list->b_node_wwn_s);
+
+ (void) fprintf(stdout, MSGSTR(3505,
+ "%-80.80s %-17.17s %-17.17s %-22.22s %-20.20s \n"),
+ MSGSTR(3506, "Physical"),
+ MSGSTR(3507, "Node_WWN"),
+ MSGSTR(3508, "Port_WWN"),
+ MSGSTR(3509, "Logical"),
+ MSGSTR(3510, "Name"));
+
+ (void) fprintf(stdout, MSGSTR(3511,
+ "%-80.80s %-17.17s %-17.17s %-22.22s %-20.20s\n"),
+ b_list->b_physical_path,
+ b_list->b_node_wwn_s,
+ b_list->b_port_wwn_s,
+ b_list->logical_path,
+ b_list->b_name);
+
+ c_list = o_list;
+ while (c_list != NULL) {
+ if ((c_list != b_list) &&
+ (strcmp(c_list->b_node_wwn_s,
+ b_list->b_node_wwn_s) == 0)) {
+ (void) fprintf(stdout, MSGSTR(3512,
+ "%-80.80s %-17.17s %-17.17s %-22.22s %-20.20s\n"),
+ c_list->b_physical_path,
+ c_list->b_node_wwn_s,
+ c_list->b_port_wwn_s,
+ c_list->logical_path,
+ c_list->b_name);
+ }
+ c_list = c_list->box_next;
+ }
+ /*
+ * Print the individual disk information for each box.
+ */
+ if (err = print_ssd_in_box(b_list->b_physical_path,
+ b_list->b_name, verbose)) {
+ return (err);
+ }
+ b_list = b_list->box_next;
+ }
+ }
+ (void) l_free_box_list(&b_list);
+ return (0);
+}
diff --git a/usr/src/cmd/luxadm/errorcodes.h b/usr/src/cmd/luxadm/errorcodes.h
new file mode 100644
index 0000000000..09f80696e0
--- /dev/null
+++ b/usr/src/cmd/luxadm/errorcodes.h
@@ -0,0 +1,536 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ERRORCODES_H
+#define _ERRORCODES_H
+
+
+
+
+/*
+ * Include any headers you depend on.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* On the sparc platform, error codes come from stgcom.h */
+#ifdef __x86
+
+/*
+ * All error numbers below this base value
+ * are assumed to be UNIX error codes.
+ */
+
+#define L_BASE L_SCSI_ERROR
+
+/*
+ * SCSI Errors
+ *
+ */
+/* SCSI error */
+#define L_SCSI_ERROR 0x10000
+
+/* Receive Diagnostics: Transfer length is not word aligned */
+#define L_RD_INVLD_TRNSFR_LEN 0x11002
+
+/* No disk element found in Receive diag. page */
+#define L_RD_NO_DISK_ELEM 0x11003
+
+/* Illegal mode sense page length */
+#define L_ILLEGAL_MODE_SENSE_PAGE 0x11004
+
+/* Invalid number of ENV. SENSE pages */
+#define L_INVALID_NO_OF_ENVSEN_PAGES 0x11005
+
+/* Buffer is *too* small to hold more than 8 pages */
+#define L_INVALID_BUF_LEN 0x11006
+
+/* Scsi_vhci errors */
+#define L_SCSI_VHCI_ERROR 0x11007
+#define L_SCSI_VHCI_ALREADY_ACTIVE 0x11008
+#define L_SCSI_VHCI_NO_STANDBY 0x11009
+#define L_SCSI_VHCI_FAILOVER_NOTSUP 0x1100a
+#define L_SCSI_VHCI_FAILOVER_BUSY 0x1100b
+
+
+/*
+ * Error definitions
+ * for Format Errors.
+ */
+#define L_INVALID_PATH 0x20200
+
+/* Failed to open a given path */
+#define L_OPEN_PATH_FAIL 0x20001
+
+/* Invalid password length. */
+#define L_INVALID_PASSWORD_LEN 0x20002
+
+/* Given disk physical path is not valid. */
+#define L_INVLD_PHYS_PATH_TO_DISK 0x20004
+
+/* Invalid name id found in the physical path */
+#define L_INVLD_ID_FOUND 0x20005
+
+/* Invalid WWN format found */
+#define L_INVLD_WWN_FORMAT 0x20006
+
+/* No WWN found in the disk's physical path */
+#define L_NO_WWN_FOUND_IN_PATH 0x20007
+
+/* No Loop address found in the phys path */
+#define L_NO_LOOP_ADDRS_FOUND 0x20008
+
+/* Invalid port number found in the phys path */
+#define L_INVLD_PORT_IN_PATH 0x20009
+
+/* Invalid LED request */
+#define L_INVALID_LED_RQST 0x20010
+
+/* Invalid path format */
+#define L_INVALID_PATH_FORMAT 0x20011
+
+/* failed to get the physical path */
+#define L_NO_PHYS_PATH 0x20012
+
+/* failed to get the ses path */
+#define L_NO_SES_PATH 0x20015
+
+/* No "/" found in the physical path */
+#define L_INVLD_PATH_NO_SLASH_FND 0x20100
+
+/* No "@" found in the physical path */
+#define L_INVLD_PATH_NO_ATSIGN_FND 0x20101
+
+/* Invalid slot (slot < 0 or slot > 10). */
+#define L_INVALID_SLOT 0x20102
+
+/* No valid path to a device */
+#define L_NO_VALID_PATH 0x20103
+
+/* No disk devices found in /dev/rdsk directory */
+#define L_NO_DISK_DEV_FOUND 0x20104
+
+/* No tape devices found in /dev/rmt directory */
+#define L_NO_TAPE_DEV_FOUND 0x20105
+
+/* Device's Node WWN not found in the WWN list. */
+#define L_NO_NODE_WWN_IN_WWNLIST 0x20106
+
+/* Device's Node WWN not found in the Box list. */
+#define L_NO_NODE_WWN_IN_BOXLIST 0x20107
+
+/* Null WWN list found. */
+#define L_NULL_WWN_LIST 0x20108
+
+/* No devices found. */
+#define L_NO_DEVICES_FOUND 0x20109
+
+/* function arg error in wwn_list process */
+#define L_PROC_WWN_ARG_ERROR 0x20110
+
+/* WWN property not found */
+#define L_NO_WWN_PROP_FOUND 0x20111
+
+/* No driver nodes found for requested driver */
+#define L_NO_DRIVER_NODES_FOUND 0x20112
+
+/* ULP error on device(s) */
+#define L_GET_DEV_LIST_ULP_FAILURE 0x20150
+
+/*
+ * Error definitions
+ * for FC Loop (FC4 devices).
+ */
+/* Invalid loop map found */
+#define L_INVALID_LOOP_MAP 0x20202
+
+/* SFIOCGMAP ioctl failed */
+#define L_SFIOCGMAP_IOCTL_FAIL 0x20203
+
+/* FCIO_GETMAP ioctl failed */
+#define L_FCIO_GETMAP_IOCTL_FAIL 0x20204
+
+/* FCIO_LINKSTATUS ioctl failed */
+#define L_FCIO_LINKSTATUS_FAILED 0x20205
+
+/* FCIO_GETMAP: Invalid # of entries */
+#define L_FCIOGETMAP_INVLD_LEN 0x20206
+
+/* FCIO_FORCE_LIP ioctl failed. */
+#define L_FCIO_FORCE_LIP_FAIL 0x20207
+
+/* Error definitions for FC devices */
+/* FCIO_RESET_LINK ioctl failed */
+#define L_FCIO_RESET_LINK_FAIL 0x20208
+
+/* FCIO_GET_FCODE_REV_FAIL ioctl failed */
+#define L_FCIO_GET_FCODE_REV_FAIL 0x20209
+
+/* FCIO_GET_FW_REV_FAIL ioctl failed */
+#define L_FCIO_GET_FW_REV_FAIL 0x20210
+
+/* FCIO_GET_DEV_LIST returns invalid dev. counts */
+#define L_INVALID_DEVICE_COUNT 0x20211
+
+/* L_FCIO_GET_NUM_DEVS_FAIL ioctl failed */
+#define L_FCIO_GET_NUM_DEVS_FAIL 0x20212
+
+/* L_FCIO_GET_DEV_LIST_FAIL ioctl failed */
+#define L_FCIO_GET_DEV_LIST_FAIL 0x20213
+
+/* L_FCIO_GET_LINK_STATUS ioctl failed */
+#define L_FCIO_GET_LINK_STATUS_FAIL 0x20214
+
+/* L_FCIO_LOOPBACK_INTERNAL or FCIO_CMD/FCIO_LASER_OFF ioctl failed */
+#define L_PORT_OFFLINE_FAIL 0x20215
+
+/* Internal Loopback or laser off ioctls not supported */
+#define L_PORT_OFFLINE_UNSUPPORTED 0x20216
+
+/* L_FCIO_NO_LOOPBACK or FCIO_CMD/FCIO_LASER_ON ioctl failed */
+#define L_PORT_ONLINE_FAIL 0x20217
+
+/* No-Loopback or laser on ioctls not supported */
+#define L_PORT_ONLINE_UNSUPPORTED 0x20218
+
+/* L_FCIO_GET_HOST_PARAMS ioctl failed */
+#define L_FCIO_GET_HOST_PARAMS_FAIL 0x20219
+
+/* Loopback mode failure */
+#define L_LOOPBACK_FAILED 0x20220
+
+/* Loopback unsupported */
+#define L_LOOPBACK_UNSUPPORTED 0x20221
+
+/* FCIO_FORCE_LIP ioctl failed on one of the paths, say, of an MPXIO device */
+#define L_FCIO_FORCE_LIP_PARTIAL_FAIL 0x20222
+
+/*
+ * Error definitions
+ * for Fabric FC driver ioctls
+ */
+/* FCP_TGT_INQUIRY ioctl failed */
+#define L_FCP_TGT_INQUIRY_FAIL 0x20250
+
+/*
+ * Error definitions
+ * for 24-bit address handling
+ */
+/* Private loop address > 0xFF found */
+#define L_INVALID_PRIVATE_LOOP_ADDRESS 0x20401
+
+/* Encountered an unexpected fibre channel topology value */
+#define L_UNEXPECTED_FC_TOPOLOGY 0x20402
+
+/* Fabric address was not found */
+#define L_NO_FABRIC_ADDR_FOUND 0x20403
+
+/* The FCIO_GET_TOPOLOGY ioctl failed */
+#define L_FCIO_GET_TOPOLOGY_FAIL 0x20404
+
+/* Invalid fabric or public loop address */
+#define L_INVALID_FABRIC_ADDRESS 0x20405
+
+/* Point to Point fibre channel topology not supported */
+#define L_PT_PT_FC_TOP_NOT_SUPPORTED 0x20406
+
+/*
+ * Error definitions for Tapestry SAN support.
+ */
+/* The FCIO_DEV_LOGIN ioctl failed */
+#define L_FCIO_DEV_LOGIN_FAIL 0x20407
+
+/* The FCIO_DEV_LOGOUT ioctl failed */
+#define L_FCIO_DEV_LOGOUT_FAIL 0x20408
+
+/* Operation not supported on connected topology */
+#define L_OPNOSUPP_ON_TOPOLOGY 0x20409
+
+/* Operation not supported on the path */
+#define L_INVALID_PATH_TYPE 0x20410
+
+/* FCIO_GET_STATE ioctl failed */
+#define L_FCIO_GET_STATE_FAIL 0x20411
+
+/* input WWN not found in dev list */
+#define L_WWN_NOT_FOUND_IN_DEV_LIST 0x20412
+
+/*
+ * Error definitions for
+ * g_dev_map_init related routines.
+ */
+/* input addr invalid */
+#define L_INVALID_MAP_DEV_ADDR 0x20430
+
+/* input property invalid */
+#define L_INVALID_MAP_DEV_PROP_NAME 0x20431
+
+/* input property invalid */
+#define L_INVALID_MAP_DEV_PROP_TYPE 0x20432
+
+/* input property name invalid */
+#define L_INVALID_MAP_DEV_PROP 0x20433
+
+/* device not found */
+#define L_NO_SUCH_DEV_FOUND 0x20434
+
+/* prop not found */
+#define L_NO_SUCH_PROP_FOUND 0x20435
+
+/* invalid arg found */
+#define L_INVALID_ARG 0x20436
+
+/*
+ * Error definitions
+ * for Downloading IB FW.
+ */
+/* Invalid download file checksum */
+#define L_DWNLD_CHKSUM_FAILED 0x20301
+
+/* Unable to read download exec header */
+#define L_DWNLD_READ_HEADER_FAIL 0x20302
+
+/* Number of bytes read from download file is not correct */
+#define L_DWNLD_READ_INCORRECT_BYTES 0x20303
+
+/* Wrong text segment size */
+#define L_DWNLD_INVALID_TEXT_SIZE 0x20304
+
+/* Error reading the download file */
+#define L_DWNLD_READ_ERROR 0x20305
+
+/* Bad firmware magic found in the download file */
+#define L_DWNLD_BAD_FRMWARE 0x20306
+
+/* Timeout message for the IB to be available */
+#define L_DWNLD_TIMED_OUT 0x20307
+
+/* Error with Rec Diag page 1 */
+#define L_REC_DIAG_PG1 0x20600
+
+/* Invalid transfer Length */
+#define L_TRANSFER_LEN 0x20601
+
+/* A firmware file must be specified on the command line */
+#define L_REQUIRE_FILE 0x20602
+
+
+/*
+ * Error definitions
+ * for System Errors
+ */
+#define L_MALLOC_FAILED 0x30000
+
+#define L_MEMCPY_FAILED 0x30001
+
+/* Cannot get status for the given path */
+#define L_LSTAT_ERROR 0x30020
+
+/* Error reading the symbolic link */
+#define L_SYMLINK_ERROR 0x30021
+
+/* Could not convert std. time to hrs/min/sec */
+#define L_LOCALTIME_ERROR 0x30022
+
+/* select() system call failed to wait for specified time */
+#define L_SELECT_ERROR 0x30023
+
+/* uname() system call failed to get the system info. */
+#define L_UNAME_FAILED 0x30024
+
+/* Cannot get status for the given path */
+#define L_FSTAT_ERROR 0x30025
+
+/* Cannot get status for the given path */
+#define L_STAT_ERROR 0x30026
+
+/* di_init() failed to return snapshot of device tree */
+#define L_DEV_SNAPSHOT_FAILED 0x30027
+
+/* di_drv_first_node() failed to find a valid driver */
+#define L_PORT_DRIVER_NOT_FOUND 0x30029
+
+/* failed to find any device paths */
+#define L_PHYS_PATH_NOT_FOUND 0x30030
+
+/* No device identifier found */
+#define L_NO_DEVID 0x30031
+
+/* Driver not supported */
+#define L_DRIVER_NOTSUPP 0x30032
+
+/* di_prom_init failure */
+#define L_PROM_INIT_FAILED 0x30033
+
+/*
+ * Error definitions
+ * for individual
+ * devices.
+ */
+/* Device busy */
+#define L_DEV_BUSY 0x40000
+
+/* Disk reserved */
+#define L_DEVICE_RESERVED 0x40001
+
+/* One or more disks in enclosure are reserved */
+#define L_DISKS_RESERVED 0x40002
+
+/* Exclusive open to a device failed. May be busy */
+#define L_EXCL_OPEN_FAILED 0x40003
+
+/* Empty slot: Device not installed */
+#define L_SLOT_EMPTY 0x40100
+
+
+/*
+ * Error definitions
+ * for Devctl functions.
+ */
+/* Devctl acquire fails */
+#define L_ACQUIRE_FAIL 0x40200
+
+
+/* Power off fails. Device may be busy */
+#define L_POWER_OFF_FAIL_BUSY 0x40300
+
+
+/*
+ * Error definitions
+ * specific to Enclosure.
+ */
+/* Failed to change the enclosure name */
+#define L_ENCL_NAME_CHANGE_FAIL 0x40400
+
+/* Duplicate enclosure names found */
+#define L_DUPLICATE_ENCLOSURES 0x40401
+
+/* Invalid no. of dsks in SENA enclosure */
+#define L_INVALID_NUM_DISKS_ENCL 0x40402
+
+/* Path is not to a SENA ecnlosure. */
+#define L_ENCL_INVALID_PATH 0x40403
+
+/* Cannot get the box list */
+#define L_NO_ENCL_LIST_FOUND 0x40404
+
+
+/*
+ * Error definitions
+ * specific to IB.
+ */
+/* No element returned from the enclosure */
+#define L_IB_NO_ELEM_FOUND 0x40500
+
+/* Invalid page code found in Receive Diag. page. */
+#define L_RD_PG_INVLD_CODE 0x40501
+
+/* Reading Receive Diag. page failed: small buffer. */
+#define L_RD_PG_MIN_BUFF 0x40502
+
+/* Get status failed */
+#define L_GET_STATUS_FAILED 0x40600
+
+/* Warning define. */
+#define L_WARNING 0x90000
+
+/* Persistant Rservation: Invalid transfer length */
+#define L_PR_INVLD_TRNSFR_LEN 0x10001
+
+/*
+ * Error definitions
+ * for Format Errors.
+ */
+/* box name conflicts with the SSA name */
+#define L_SSA_CONFLICT 0x20013
+
+
+/*
+ * Error definitions
+ * for System Errors
+ */
+/* drvconfig fail */
+#define L_DRVCONFIG_ERROR 0x31001
+
+/* disks program failed */
+#define L_DISKS_ERROR 0x31002
+
+/* devlinks program failed */
+#define L_DEVLINKS_ERROR 0x31003
+
+/* fail to read /dev/rdsk directory. */
+#define L_READ_DEV_DIR_ERROR 0x31004
+
+/* Failed to open /dev/es/ directory. */
+#define L_OPEN_ES_DIR_FAILED 0x31005
+
+/* fail to get status from /dev/es directory. */
+#define L_LSTAT_ES_DIR_ERROR 0x31006
+
+/* disks program failed */
+#define L_TAPES_ERROR 0x31007
+
+/* fail to get status from /dev/rmt/directory. */
+#define L_STAT_RMT_DIR_ERROR 0x31008
+
+/* fail to get status from /dev/rmt/directory. */
+#define L_STAT_DEV_DIR_ERROR 0x31009
+
+
+/*
+ * Error definitions
+ * specific to Back plane.
+ */
+/* Backplane: Busy or reserved disks found */
+#define L_BP_BUSY_RESERVED 0x50000
+
+/* Backplane: one or more busy disks found */
+#define L_BP_BUSY 0x50001
+
+/* Backplane: one or more reserved disks found */
+#define L_BP_RESERVED 0x50002
+
+/* No BP element found in the enclosure */
+#define L_NO_BP_ELEM_FOUND 0x50003
+
+/*
+ * Thread errors.
+ */
+#define L_TH_CREATE 0x60000
+#define L_TH_JOIN 0x60001
+
+
+
+#endif /* __x86 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ERRORCODES_H */
diff --git a/usr/src/cmd/luxadm/errormsgs.c b/usr/src/cmd/luxadm/errormsgs.c
new file mode 100644
index 0000000000..542e0b5799
--- /dev/null
+++ b/usr/src/cmd/luxadm/errormsgs.c
@@ -0,0 +1,1108 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+
+/*
+ * I18N message number ranges
+ * This file: 10000 - 10499
+ * Shared common messages: 1 - 1999
+ */
+
+/* #define _POSIX_SOURCE 1 */
+
+
+/* Includes */
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <nl_types.h>
+#include <sys/scsi/scsi.h>
+#include <string.h>
+#include <errno.h>
+#include "common.h"
+#include "errorcodes.h"
+
+
+
+/* Defines */
+#define MAXLEN 1000
+
+/*
+ * Allocate space for and return a pointer to a string
+ * on the stack. If the string is null, create
+ * an empty string.
+ */
+char *
+alloc_string(char *s)
+{
+ char *ns;
+
+ if (s == (char *)NULL) {
+ ns = (char *)calloc(1, 1);
+ } else {
+ ns = (char *)calloc(1, strlen(s) + 1);
+ if (ns != NULL) {
+ (void) strncpy(ns, s, (strlen(s) + 1));
+ }
+ }
+ return (ns);
+}
+
+
+/*
+ * Decodes the SCSI sense byte to a string.
+ *
+ * RETURNS:
+ * character string
+ */
+static char *
+decode_sense_byte(uchar_t status)
+{
+ switch (status & STATUS_MASK) {
+ case STATUS_GOOD:
+ return (MSGSTR(10000, "Good status"));
+
+ case STATUS_CHECK:
+ return (MSGSTR(128, "Check condition"));
+
+ case STATUS_MET:
+ return (MSGSTR(124, "Condition met"));
+
+ case STATUS_BUSY:
+ return (MSGSTR(37, "Busy"));
+
+ case STATUS_INTERMEDIATE:
+ return (MSGSTR(10001, "Intermediate"));
+
+ case STATUS_INTERMEDIATE_MET:
+ return (MSGSTR(10002, "Intermediate - condition met"));
+
+ case STATUS_RESERVATION_CONFLICT:
+ return (MSGSTR(10003, "Reservation_conflict"));
+
+ case STATUS_TERMINATED:
+ return (MSGSTR(126, "Command terminated"));
+
+ case STATUS_QFULL:
+ return (MSGSTR(83, "Queue full"));
+
+ default:
+ return (MSGSTR(4, "Unknown status"));
+ }
+}
+
+
+/*
+ * This function finds a predefined error string to a given
+ * error number (errornum), allocates memory for the string
+ * and returns the corresponding error message to the caller.
+ *
+ * RETURNS
+ * error string if O.K.
+ * NULL otherwise
+ */
+char
+*get_errString(int errornum)
+{
+char err_msg[MAXLEN], *errStrg;
+
+ err_msg[0] = '\0'; /* Just in case */
+ if (errornum < L_BASE) {
+ /* Some sort of random system error most likely */
+ errStrg = strerror(errno);
+ if (errStrg != NULL) {
+ (void) strcpy(err_msg, errStrg);
+ } else { /* Something's _really_ messed up */
+ (void) sprintf(err_msg,
+ MSGSTR(10081,
+ " Error: could not decode the"
+ " error message.\n"
+ " The given error message is not"
+ " defined in the library.\n"
+ " Message number: %d.\n"), errornum);
+ }
+
+ /* Make sure ALL CASES set err_msg to something */
+ } else switch (errornum) {
+ case L_SCSI_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10096,
+ " Error: SCSI failure."));
+ break;
+
+ case L_PR_INVLD_TRNSFR_LEN:
+ (void) sprintf(err_msg,
+ MSGSTR(10005,
+ " Error: Persistant Reserve command"
+ " transfer length not word aligned."));
+ break;
+
+ case L_RD_NO_DISK_ELEM:
+ (void) sprintf(err_msg,
+ MSGSTR(10006,
+ " Error: Could not find the disk elements"
+ " in the Receive Diagnostic pages."));
+ break;
+
+ case L_RD_INVLD_TRNSFR_LEN:
+ (void) sprintf(err_msg,
+ MSGSTR(10007,
+ " Error: Receive Diagnostic command"
+ " transfer length not word aligned."));
+ break;
+
+ case L_ILLEGAL_MODE_SENSE_PAGE:
+ (void) sprintf(err_msg,
+ MSGSTR(10008,
+ " Error: Programming error - "
+ "illegal Mode Sense parameter."));
+ break;
+
+ case L_INVALID_NO_OF_ENVSEN_PAGES:
+ (void) sprintf(err_msg,
+ MSGSTR(10009,
+ " Error: Invalid no. of sense pages.\n"
+ " Could not get valid sense page"
+ " information from the device."));
+ break;
+
+ case L_INVALID_BUF_LEN:
+ (void) sprintf(err_msg,
+ MSGSTR(10010,
+ " Error: Invalid buffer length.\n"
+ " Could not get diagnostic "
+ " information from the device."));
+ break;
+
+ case L_INVALID_PATH:
+ (void) sprintf(err_msg,
+ MSGSTR(113,
+ " Error: Invalid pathname"));
+ break;
+
+ case L_NO_PHYS_PATH:
+ (void) sprintf(err_msg,
+ MSGSTR(10011,
+ " Error: Could not get"
+ " physical path to the device."));
+ break;
+
+ case L_NO_SES_PATH:
+ (void) sprintf(err_msg,
+ MSGSTR(10098,
+ " Error: No SES found"
+ " for the device path."));
+ break;
+
+ case L_INVLD_PATH_NO_SLASH_FND:
+ (void) sprintf(err_msg,
+ MSGSTR(10012,
+ "Error in the device physical path."));
+ break;
+
+ case L_INVLD_PATH_NO_ATSIGN_FND:
+ (void) sprintf(err_msg,
+ MSGSTR(10013,
+ " Error in the device physical path:"
+ " no @ found."));
+ break;
+
+ case L_INVALID_SLOT:
+ (void) sprintf(err_msg,
+ MSGSTR(10014,
+ " Error: Invalid path format."
+ " Invalid slot."));
+ break;
+
+ case L_INVALID_LED_RQST:
+ (void) sprintf(err_msg,
+ MSGSTR(10015,
+ " Error: Invalid LED request."));
+ break;
+
+ case L_INVALID_PATH_FORMAT:
+ (void) sprintf(err_msg,
+ MSGSTR(10016,
+ " Error: Invalid path format."));
+ break;
+
+ case L_OPEN_PATH_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10017,
+ " Error opening the path."));
+ break;
+
+ case L_INVALID_PASSWORD_LEN:
+ (void) sprintf(err_msg,
+ MSGSTR(10018,
+ "Error: Invalid password length."));
+ break;
+
+ case L_INVLD_PHYS_PATH_TO_DISK:
+ (void) sprintf(err_msg,
+ MSGSTR(10019,
+ " Error: Physical path not of a disk."));
+ break;
+
+ case L_INVLD_ID_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10020,
+ " Error in the device physical path:"
+ " Invalid ID found in the path."));
+ break;
+
+ case L_INVLD_WWN_FORMAT:
+ (void) sprintf(err_msg,
+ MSGSTR(10021,
+ " Error in the device physical path:"
+ " Invalid wwn format."));
+
+ break;
+
+ case L_NO_VALID_PATH:
+ (void) sprintf(err_msg,
+ MSGSTR(10022,
+ " Error: Could not find valid path to"
+ " the device."));
+ break;
+
+ case L_NO_WWN_FOUND_IN_PATH:
+ (void) sprintf(err_msg,
+ MSGSTR(10023,
+ " Error in the device physical path:"
+ " No WWN found."));
+
+ break;
+
+ case L_NO_NODE_WWN_IN_WWNLIST:
+ (void) sprintf(err_msg,
+ MSGSTR(10024,
+ " Error: Device's Node WWN is not"
+ " found in the WWN list.\n"));
+ break;
+
+ case L_NO_NODE_WWN_IN_BOXLIST:
+ (void) sprintf(err_msg,
+ MSGSTR(10025,
+ " Error: Device's Node WWN is not"
+ " found in the Box list.\n"));
+ break;
+
+ case L_NULL_WWN_LIST:
+ (void) sprintf(err_msg,
+ MSGSTR(10026,
+ " Error: Null WWN list found."));
+ break;
+
+ case L_NO_LOOP_ADDRS_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10027,
+ " Error: Could not find the loop address for "
+ " the device at physical path."));
+
+ break;
+
+ case L_INVLD_PORT_IN_PATH:
+ (void) sprintf(err_msg,
+ MSGSTR(10028,
+ "Error in the device physical path:"
+ " Invalid port number found."
+ " (Should be 0 or 1)."));
+
+ break;
+
+ case L_INVALID_LOOP_MAP:
+ (void) sprintf(err_msg,
+ MSGSTR(10029,
+ "Error: Invalid loop map found."));
+ break;
+
+ case L_SFIOCGMAP_IOCTL_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10030,
+ " Error: SFIOCGMAP ioctl failed."
+ " Cannot read loop map."));
+ break;
+
+ case L_FCIO_GETMAP_IOCTL_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10031,
+ " Error: FCIO_GETMAP ioctl failed."
+ " Cannot read loop map."));
+ break;
+
+ case L_FCIO_LINKSTATUS_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10032,
+ " Error: FCIO_LINKSTATUS ioctl failed."
+ " Cannot read loop map."));
+ break;
+
+ case L_FCIOGETMAP_INVLD_LEN:
+ (void) sprintf(err_msg,
+ MSGSTR(10033,
+ " Error: FCIO_GETMAP ioctl returned"
+ " an invalid parameter:"
+ " # entries to large."));
+ break;
+
+ case L_FCIO_FORCE_LIP_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10034,
+ " Error: FCIO_FORCE_LIP ioctl failed."));
+ break;
+
+ case L_FCIO_FORCE_LIP_PARTIAL_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10115,
+ " Error: FCIO_FORCE_LIP ioctl failed on one"
+ " or more (but not all) of the paths."));
+ break;
+
+ case L_DWNLD_CHKSUM_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10035,
+ "Error: Download file checksum failed."));
+
+ break;
+
+ case L_DWNLD_READ_HEADER_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10036,
+ " Error: Reading download file exec"
+ " header failed."));
+ break;
+
+ case L_DWNLD_READ_INCORRECT_BYTES:
+ (void) sprintf(err_msg,
+ MSGSTR(10037,
+ " Error: Incorrect number of bytes read."));
+ break;
+
+ case L_DWNLD_INVALID_TEXT_SIZE:
+ (void) sprintf(err_msg,
+ MSGSTR(10038,
+ " Error: Reading text segment: "
+ " Found wrong size."));
+ break;
+
+ case L_DWNLD_READ_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10039,
+ " Error: Failed to read download file."));
+ break;
+
+ case L_DWNLD_BAD_FRMWARE:
+ (void) sprintf(err_msg,
+ MSGSTR(10040,
+ " Error: Bad Firmware MAGIC."));
+ break;
+
+ case L_DWNLD_TIMED_OUT:
+ (void) sprintf(err_msg,
+ MSGSTR(10041,
+ " Error: Timed out in 5 minutes"
+ " waiting for the"
+ " IB to become available."));
+ break;
+
+ case L_REC_DIAG_PG1:
+ (void) sprintf(err_msg,
+ MSGSTR(10042,
+ " Error parsing the Receive"
+ " diagnostic page."));
+ break;
+
+ case L_TRANSFER_LEN:
+ (void) sprintf(err_msg,
+ MSGSTR(10043, " "));
+ break;
+
+ case L_REQUIRE_FILE:
+ (void) sprintf(err_msg,
+ MSGSTR(10109,
+ " Error: No default file. You must specify"
+ " the filename path."));
+ break;
+
+ case L_MALLOC_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10,
+ " Error: Unable to allocate memory."));
+ break;
+
+ case L_LOCALTIME_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10044,
+ " Error: Could not convert time"
+ " to broken-down time: Hrs/Mins/Secs."));
+ break;
+
+ case L_SELECT_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10045,
+ " select() error during retry:"
+ " Could not wait for"
+ " specified time."));
+ break;
+
+ case L_NO_DISK_DEV_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10046,
+ " Error: No disk devices found"
+ " in the /dev/rdsk"
+ " directory."));
+ break;
+
+ case L_NO_TAPE_DEV_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10047,
+ " Error: No tape devices found"
+ " in the /dev/rmt"
+ " directory."));
+ break;
+
+ case L_LSTAT_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10048,
+ " lstat() error: Cannot obtain status"
+ " for the device."));
+ break;
+
+ case L_SYMLINK_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10049,
+ " Error: Could not read the symbolic link."));
+ break;
+
+ case L_UNAME_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10050,
+ " uname() error: Could not obtain the"
+ " architeture of the host machine."));
+ break;
+
+ case L_DRVCONFIG_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10051,
+ " Error: Could not run drvconfig."));
+ break;
+
+ case L_DISKS_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10052,
+ " Error: Could not run disks."));
+ break;
+
+ case L_DEVLINKS_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10053,
+ " Error: Could not run devlinks."));
+ break;
+
+ case L_READ_DEV_DIR_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10054,
+ " Error: Could not read /dev/rdsk"
+ " directory."));
+ break;
+
+ case L_OPEN_ES_DIR_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10055,
+ " Error: Could not open /dev/es"
+ " directory."));
+ break;
+
+ case L_LSTAT_ES_DIR_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10056,
+ " lstat() error: Could not get status"
+ " for /dev/es directory."));
+ break;
+
+ case L_DEV_BUSY:
+ (void) sprintf(err_msg,
+ MSGSTR(10057,
+ " Error: Could not offline the device\n"
+ " May be Busy."));
+ break;
+
+ case L_EXCL_OPEN_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10058,
+ " Error: Could not open device in"
+ " exclusive mode."
+ " May already be open."));
+ break;
+
+ case L_DEVICE_RESERVED:
+ (void) sprintf(err_msg,
+ MSGSTR(10059,
+ " Error: Disk is reserved."));
+ break;
+
+ case L_DISKS_RESERVED:
+ (void) sprintf(err_msg,
+ MSGSTR(10060,
+ " Error: One or more disks in"
+ " SENA are reserved."));
+ break;
+
+ case L_SLOT_EMPTY:
+ (void) sprintf(err_msg,
+ MSGSTR(10061,
+ " Error: Slot is empty."));
+ break;
+
+ case L_ACQUIRE_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10062,
+ " Error: Could not acquire"
+ " the device."));
+ break;
+
+ case L_POWER_OFF_FAIL_BUSY:
+ (void) sprintf(err_msg,
+ MSGSTR(10063,
+ " Error: Could not power off the device.\n"
+ " May be Busy."));
+ break;
+
+ case L_ENCL_NAME_CHANGE_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10064,
+ " Error: The Enclosure name change failed."));
+ break;
+
+ case L_DUPLICATE_ENCLOSURES:
+ (void) sprintf(err_msg,
+ MSGSTR(10065,
+ " Error: There are two or more enclosures"
+ " with the same name."
+ " Please use a logical or physical"
+ " pathname."));
+ break;
+
+ case L_INVALID_NUM_DISKS_ENCL:
+ (void) sprintf(err_msg,
+ MSGSTR(10066,
+ " Error: The number of disks in the"
+ " front & rear of the enclosure are"
+ " different."
+ " This is not a supported configuration."));
+ break;
+
+ case L_ENCL_INVALID_PATH:
+ (void) sprintf(err_msg,
+ MSGSTR(10067,
+ " Error: Invalid path."
+ " Device is not a SENA subsystem."));
+ break;
+
+ case L_NO_ENCL_LIST_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10068,
+ " Error: Cannot get the Box list."));
+ break;
+
+ case L_IB_NO_ELEM_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10069,
+ " Error: No elements returned from"
+ " enclosure (IB)."));
+ break;
+
+ case L_GET_STATUS_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10070,
+ " Error: Get status failed."));
+ break;
+
+ case L_RD_PG_MIN_BUFF:
+ (void) sprintf(err_msg,
+ MSGSTR(10071,
+ " Error: Reading page from IB.\n"
+ " Buffer size too small."));
+ break;
+
+ case L_RD_PG_INVLD_CODE:
+ (void) sprintf(err_msg,
+ MSGSTR(10072,
+ " Error: Reading page from IB\n"
+ " Invalid page code or page len found."));
+ break;
+
+ case L_BP_BUSY_RESERVED:
+ (void) sprintf(err_msg,
+ MSGSTR(10073,
+ " Error: There is a busy or reserved disk"
+ " attached to this backplane.\n"
+ " You must close the disk,\n"
+ " or release the disk,\n"
+ " or resubmit the command using"
+ " the Force option."));
+ break;
+
+ case L_BP_BUSY:
+ (void) sprintf(err_msg,
+ MSGSTR(10074,
+ " Error: There is a busy disk"
+ " attached to this backplane.\n"
+ " You must close the disk,\n"
+ " or resubmit the command using"
+ " the Force option."));
+ break;
+
+ case L_BP_RESERVED:
+ (void) sprintf(err_msg,
+ MSGSTR(10075,
+ " Error: There is a reserved disk"
+ " attached to this backplane.\n"
+ " You must release the disk,\n"
+ " or resubmit the subcommand using"
+ " the Force option."));
+ break;
+
+ case L_NO_BP_ELEM_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10076,
+ " Error: No Back plane elements found"
+ " in the enclosure."));
+ break;
+
+ case L_SSA_CONFLICT:
+ (void) sprintf(err_msg,
+ MSGSTR(10077,
+ " There is a conflict between the "
+ "enclosure name and an SSA name of "
+ "same form, cN.\n"
+ " Please use a logical or physical "
+ "pathname."));
+ break;
+
+ case L_WARNING:
+ (void) sprintf(err_msg,
+ MSGSTR(10078, " Warning:"));
+
+ break;
+
+ case L_TH_JOIN:
+ (void) sprintf(err_msg,
+ MSGSTR(10079,
+ " Error: Thread join failed."));
+ break;
+
+ case L_FCIO_RESET_LINK_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10082,
+ " Error: FCIO_RESET_LINK ioctl failed.\n"
+ " Could not reset the loop."));
+ break;
+
+ case L_FCIO_GET_FCODE_REV_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10083,
+ " Error: FCIO_GET_FCODE_REV ioctl failed.\n"
+ " Could not get the fcode version."));
+ break;
+
+ case L_FCIO_GET_FW_REV_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10084,
+ " Error: FCIO_GET_FW_REV ioctl failed.\n"
+ " Could not get the firmware revision."));
+ break;
+
+ case L_NO_DEVICES_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10085,
+ " No FC devices found."));
+ break;
+
+ case L_INVALID_DEVICE_COUNT:
+ (void) sprintf(err_msg,
+ MSGSTR(10086,
+ " Error: FCIO_GET_DEV_LIST ioctl returned"
+ " an invalid device count."));
+ break;
+
+ case L_FCIO_GET_NUM_DEVS_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10087,
+ " Error: FCIO_GET_NUM_DEVS ioctl failed.\n"
+ " Could not get the number of devices."));
+ break;
+
+ case L_FCIO_GET_DEV_LIST_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10088,
+ " Error: FCIO_GET_DEV_LIST ioctl failed.\n"
+ " Could not get the device list."));
+ break;
+
+ case L_FCIO_GET_LINK_STATUS_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10089,
+ " Error: FCIO_GET_LINK_STATUS ioctl failed.\n"
+ " Could not get the link status."));
+ break;
+
+ case L_PORT_OFFLINE_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10090,
+ " Error: ioctl to offline the port failed."));
+ break;
+
+ case L_PORT_OFFLINE_UNSUPPORTED:
+ (void) sprintf(err_msg,
+ MSGSTR(10091,
+ " Error: The driver does not support ioctl to"
+ " disable the FCA port."));
+ break;
+
+ case L_PORT_ONLINE_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10092,
+ " Error: ioctl to online the port failed."));
+ break;
+
+ case L_PORT_ONLINE_UNSUPPORTED:
+ (void) sprintf(err_msg,
+ MSGSTR(10093,
+ " Error: The driver does not support ioctl to"
+ " enable the FCA port."));
+ break;
+
+ case L_FCP_TGT_INQUIRY_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10094,
+ " Error: FCP_TGT_INQUIRY ioctl failed.\n"
+ " Could not get the target inquiry data"
+ " from FCP."));
+ break;
+
+ case L_FSTAT_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10095,
+ " fstat() error: Cannot obtain status"
+ " for the device."));
+ break;
+
+ case L_FCIO_GET_HOST_PARAMS_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10097,
+ " Error: FCIO_GET_HOST_PARAMS ioctl failed.\n"
+ " Could not get the host parameters."));
+ break;
+
+ case L_STAT_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10099,
+ " stat() error: Cannot obtain status"
+ " for the device."));
+ break;
+
+ case L_DEV_SNAPSHOT_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10100,
+ " Error: Could not retrieve device tree"
+ " snapshot."));
+ break;
+
+ case L_LOOPBACK_UNSUPPORTED:
+ (void) sprintf(err_msg,
+ MSGSTR(10101,
+ " Error: Loopback mode is unsupported for this"
+ " device."));
+ break;
+
+ case L_LOOPBACK_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10102,
+ " Error: Error occurred during loopback mode"
+ " set."));
+ break;
+
+ case L_FCIO_GET_TOPOLOGY_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10103,
+ " Error: FCIO_GET_TOPOLOGY ioctl failed.\n"
+ " Could not get the fca port topology."));
+ break;
+
+ case L_UNEXPECTED_FC_TOPOLOGY:
+ (void) sprintf(err_msg,
+ MSGSTR(10104,
+ " Error: Unexpected Fibre Channel topology"
+ " found."));
+ break;
+
+ case L_INVALID_PRIVATE_LOOP_ADDRESS:
+ (void) sprintf(err_msg,
+ MSGSTR(10105,
+ " Error: AL_PA is not a valid private loop"
+ " address."));
+ break;
+
+ case L_NO_FABRIC_ADDR_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10106,
+ " Error: Could not find the fabric address"
+ " for the device at physical path."));
+ break;
+
+ case L_INVALID_FABRIC_ADDRESS:
+ (void) sprintf(err_msg,
+ MSGSTR(10107,
+ " Error: Device port address on the Fabric"
+ " topology is not valid."));
+ break;
+
+ case L_PT_PT_FC_TOP_NOT_SUPPORTED:
+ (void) sprintf(err_msg,
+ MSGSTR(10108,
+ " Error: Point to Point Fibre Channel "
+ "topology is currently not supported."));
+ break;
+
+ case L_FCIO_DEV_LOGIN_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10310,
+ " Error: FCIO_DEV_LOGIN ioctl failed."));
+ break;
+
+ case L_FCIO_DEV_LOGOUT_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10311,
+ " Error: FCIO_DEV_LOGOUT ioctl failed."));
+ break;
+
+ case L_OPNOSUPP_ON_TOPOLOGY:
+ (void) sprintf(err_msg,
+ MSGSTR(10312,
+ " Error: operation not supported "
+ "on connected topology."));
+ break;
+
+ case L_INVALID_PATH_TYPE:
+ (void) sprintf(err_msg,
+ MSGSTR(10313,
+ " Error: operation not supported "
+ "on the path."));
+ break;
+
+ case L_FCIO_GET_STATE_FAIL:
+ (void) sprintf(err_msg,
+ MSGSTR(10314,
+ " Error: FCIO_GET_STATE ioctl failed."));
+ break;
+
+ case L_WWN_NOT_FOUND_IN_DEV_LIST:
+ (void) sprintf(err_msg,
+ MSGSTR(10315,
+ " Error: device WWN not found in "
+ "device list."));
+ break;
+
+ case L_STAT_RMT_DIR_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10110,
+ " stat() error: Could not get status"
+ " for /dev/rmt directory."));
+ break;
+
+ case L_STAT_DEV_DIR_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10111,
+ " stat() error: Could not get status"
+ " for /dev/dsk directory."));
+ break;
+
+ case L_PROM_INIT_FAILED:
+ (void) sprintf(err_msg,
+ MSGSTR(10234,
+ " Error: di_prom_init failure"));
+ break;
+
+ case L_PORT_DRIVER_NOT_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10113,
+ " Error: requested port driver"
+ " does not exist"));
+ break;
+
+ case L_PHYS_PATH_NOT_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10114,
+ " Error: requested phys path does not exist"));
+ break;
+
+ case L_GET_DEV_LIST_ULP_FAILURE:
+ (void) sprintf(err_msg,
+ MSGSTR(10150,
+ " Error: g_get_dev_list failed on ULP "
+ "processing of target device(s)"));
+ break;
+
+ case L_SCSI_VHCI_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10230,
+ " Error: Unable to perform failover"));
+ break;
+
+ case L_SCSI_VHCI_ALREADY_ACTIVE:
+ (void) sprintf(err_msg,
+ MSGSTR(10231,
+ " Error: Pathclass already active"));
+ break;
+
+ case L_NO_DEVID:
+ (void) sprintf(err_msg,
+ MSGSTR(10232,
+ " Error: No device identifier found"));
+ break;
+
+ case L_DRIVER_NOTSUPP:
+ (void) sprintf(err_msg,
+ MSGSTR(10233,
+ " Error: Driver not supported"));
+ break;
+
+ case L_PROC_WWN_ARG_ERROR:
+ (void) sprintf(err_msg,
+ MSGSTR(10235,
+ " Error: process WWN argument"));
+ break;
+
+ case L_NO_WWN_PROP_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10236,
+ " Error: WWN prop not found"));
+ break;
+
+ case L_NO_DRIVER_NODES_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10237,
+ " Error: Requested driver nodes not found"));
+ break;
+
+ case L_INVALID_MAP_DEV_ADDR:
+ (void) sprintf(err_msg,
+ MSGSTR(10330,
+ " Error: Invalid map device handle found"));
+ break;
+
+ case L_INVALID_MAP_DEV_PROP_TYPE:
+ (void) sprintf(err_msg,
+ MSGSTR(10331,
+ " Error: Invalid device property type found"));
+ break;
+
+ case L_INVALID_MAP_DEV_PROP_NAME:
+ (void) sprintf(err_msg,
+ MSGSTR(10332,
+ " Error: Invalid device property name found"));
+ break;
+
+ case L_INVALID_MAP_DEV_PROP:
+ (void) sprintf(err_msg,
+ MSGSTR(10333,
+ " Error: Invalid device property handle "
+ "found"));
+ break;
+
+ case L_SCSI_VHCI_NO_STANDBY:
+ (void) sprintf(err_msg,
+ MSGSTR(10334,
+ " Error: Unable to perform failover, "
+ "standby path unavailable"));
+ break;
+
+ case L_SCSI_VHCI_FAILOVER_NOTSUP:
+ (void) sprintf(err_msg,
+ MSGSTR(10335,
+ " Error: Device does not support failover"));
+ break;
+
+ case L_SCSI_VHCI_FAILOVER_BUSY:
+ (void) sprintf(err_msg,
+ MSGSTR(10336,
+ " Error: Failover currently in progress"));
+ break;
+
+ case L_NO_SUCH_DEV_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10337,
+ " Error: No such device found"));
+ break;
+
+ case L_NO_SUCH_PROP_FOUND:
+ (void) sprintf(err_msg,
+ MSGSTR(10338,
+ " Error: No such property found"));
+ break;
+
+ case L_INVALID_ARG:
+ (void) sprintf(err_msg,
+ MSGSTR(10339,
+ " Error: Invalid argument found"));
+ break;
+
+ default:
+
+ if (((L_SCSI_ERROR ^ errornum) == STATUS_GOOD) ||
+ ((L_SCSI_ERROR ^ errornum) == STATUS_BUSY) ||
+ ((L_SCSI_ERROR ^ errornum) == STATUS_CHECK) ||
+ ((L_SCSI_ERROR ^ errornum) == STATUS_MET) ||
+ ((L_SCSI_ERROR ^ errornum) == STATUS_INTERMEDIATE) ||
+ ((L_SCSI_ERROR ^ errornum) == STATUS_INTERMEDIATE_MET) ||
+ ((L_SCSI_ERROR ^ errornum) == STATUS_RESERVATION_CONFLICT) ||
+ ((L_SCSI_ERROR ^ errornum) == STATUS_TERMINATED) ||
+ ((L_SCSI_ERROR ^ errornum) == STATUS_QFULL)) {
+ (void) sprintf(err_msg,
+ MSGSTR(10080,
+ " SCSI Error - Sense Byte:(0x%x) %s \n"
+ " Error: Retry failed."),
+ (L_SCSI_ERROR ^ errornum) & STATUS_MASK,
+ decode_sense_byte((uchar_t)L_SCSI_ERROR ^ errornum));
+ } else {
+ (void) sprintf(err_msg,
+ MSGSTR(10081,
+ " Error: could not decode the"
+ " error message.\n"
+ " The given error message is not"
+ " defined in the library.\n"
+ " Message number: %d.\n"), errornum);
+ }
+
+ } /* end of switch */
+
+ errStrg = alloc_string(err_msg);
+
+ return (errStrg);
+}
diff --git a/usr/src/cmd/luxadm/fabric_conf.c b/usr/src/cmd/luxadm/fabric_conf.c
new file mode 100644
index 0000000000..2a7611f319
--- /dev/null
+++ b/usr/src/cmd/luxadm/fabric_conf.c
@@ -0,0 +1,335 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <libdevice.h>
+#include <sys/fibre-channel/fcio.h>
+#include "common.h"
+
+static int parse_line(char *line, char *path, char *wwn, char *filename);
+static int create_ap_instance(char *ap_id, char *wwn_string,
+ char *filename, char *line);
+static void log_error(char *msg_id, char *input_tmplt, ...);
+static char ctoi(char c);
+
+/*
+ * Simple wrapper for syslog error messages.
+ * Allows easy addition of syserr output if desired.
+ */
+static void
+log_error(char *msg_id, char *input_tmplt, ...)
+{
+ va_list ap;
+ char input_merged_msg[200];
+ char *msg_template = "ID[luxadm.create_fabric_device.%s] %s";
+ /*
+ * First %s for msg_id in merged msg.
+ * Second %s is for input merged_msg
+ */
+ char *merged_msg;
+
+ va_start(ap, input_tmplt);
+ /* insert caller's args */
+ (void) vsprintf(input_merged_msg, input_tmplt, ap);
+ va_end(ap);
+
+ merged_msg = (char *)malloc(strlen(msg_template) +
+ strlen(input_merged_msg) +
+ strlen(msg_id) + 1);
+ if (merged_msg == NULL) {
+ syslog(LOG_ERR,
+ "ID[luxadm.create_fabric_device.2317] "
+ "malloc failure, %s", strerror(errno));
+ } else {
+ sprintf(merged_msg, msg_template, msg_id, input_merged_msg);
+ /* first insert msg_id */
+ syslog(LOG_ERR, merged_msg, "");
+ (void) puts(merged_msg); /* also print message */
+ free(merged_msg);
+ }
+}
+
+/*
+ * Routines for reading tapestry repository file
+ */
+
+#define COMMENT_CHAR '#'
+int
+read_repos_file(char *repos_filename)
+{
+ int fd;
+ char *line;
+ char *tmp_ptr, *mmap_ptr;
+ char path[MAXPATHLEN];
+ int ret;
+ char wwn[FC_WWN_SIZE*2+1];
+ struct stat stbuf;
+ unsigned int filesize;
+ unsigned int bytes_read;
+
+ if (repos_filename == NULL || *repos_filename == NULL) {
+ log_error("2310",
+ "filename missing for -f option of "
+ "luxadm -e create_fabric_device");
+ return (-1);
+ }
+
+ fd = open(repos_filename, O_RDONLY);
+
+ if (fd == -1) {
+ log_error("2311",
+ "fopen failed: cannot open repository file %s. %d",
+ repos_filename, strerror(errno));
+ return (-1);
+ }
+
+ if (fstat(fd, &stbuf) == -1) {
+ close(fd);
+ log_error("2312",
+ "stat failed on file %s. %s",
+ repos_filename, strerror(errno));
+ return (-1);
+ }
+ filesize = stbuf.st_size;
+ tmp_ptr = mmap_ptr = mmap((caddr_t)0, filesize,
+ (PROT_READ | PROT_WRITE), MAP_PRIVATE, fd, 0);
+
+ if (mmap_ptr == MAP_FAILED) {
+ log_error("2315",
+ "Failed to mmap file %s. %s",
+ repos_filename, strerror(errno));
+ return (-1);
+ }
+
+ bytes_read = 0;
+ while (bytes_read < filesize) {
+ line = tmp_ptr;
+ while (bytes_read < filesize && *tmp_ptr != '\n') {
+ bytes_read++;
+ tmp_ptr++;
+ }
+ if (*tmp_ptr == '\n') {
+ *tmp_ptr = NULL;
+ tmp_ptr++;
+ bytes_read++;
+ }
+
+ /* If the line is a comment, read another line */
+ if (*line == COMMENT_CHAR) {
+ continue;
+ }
+ ret = parse_line(line, path, wwn, repos_filename);
+ if (ret == 0) {
+ ret = create_ap_instance(path,
+ wwn, repos_filename, line);
+ }
+ }
+
+ ret = close(fd);
+ ret = munmap(mmap_ptr, filesize);
+ return (ret);
+}
+
+/*
+ * Input is paramater 1 - a line from repository
+ * Output is other parameters, the path to the attachment point,
+ * and the port wwn are parsed from the repository
+ * Format is
+ * "/devices/pci..../fp@1,0:fc::wwn"
+ * If controller name is missing, that's okay. Other fields
+ * must be present
+ *
+ * Return 0 on success or -1 on failure; all failures logged to syslog.
+ */
+#define WWN_DELIM "::"
+static int
+parse_line(char *line, char *path, char *wwn, char *filename)
+{
+ char *p_path, *p_wwn, *p_delim;
+ char *line_copy;
+
+ line_copy = strdup(line);
+ if (line_copy == NULL) {
+ log_error("2317",
+ "malloc failure, %s", strerror(errno));
+ }
+ p_path = line_copy;
+ p_delim = strstr(p_path, WWN_DELIM);
+ if (p_delim == NULL) {
+ log_error("2313",
+ "Invalid line (%s) in file %s.", line, filename);
+ free(line_copy);
+ return (-1);
+ }
+ *p_delim = NULL; /* NULL terminate path */
+
+ if (strlcpy(path, p_path, MAXPATHLEN) >= MAXPATHLEN) {
+ log_error("2318",
+ "Path too long (%s) in file %s.", p_path, filename);
+ free(line_copy);
+ return (-1);
+ }
+
+ p_wwn = p_delim + strlen(WWN_DELIM);
+ /*
+ * Now look for the blank delimiter before the controller
+ *
+ * This is just the case when there may be a controller #
+ * after the attachment point and WWN. For example -
+ * /devices/pci@b,2000/pci@2/SUNW,qlc@4/fp@0,0:fc::220000203707f4f1 c4
+ */
+ p_delim = strchr(p_wwn, ' ');
+ if (p_delim != NULL) {
+ /* now p_delim points to blank */
+ *p_delim = NULL; /* terminate wwn at delim */
+ } else {
+ char *p_last_char;
+ p_last_char = p_wwn+strlen(p_wwn)-1;
+ if (*p_last_char == '\n') {
+ *p_last_char = NULL;
+ }
+ }
+ strcpy(wwn, p_wwn);
+ free(line_copy);
+ return (0);
+}
+
+static char
+ctoi(char c)
+{
+ if ((c >= '0') && (c <= '9'))
+ c -= '0';
+ else if ((c >= 'A') && (c <= 'F'))
+ c = c - 'A' + 10;
+ else if ((c >= 'a') && (c <= 'f'))
+ c = c - 'a' + 10;
+ else
+ c = -1;
+ return (c);
+}
+
+/*
+ * "string" is Input and "port_wwn" has the output
+ *
+ * This function converts a string to WWN.
+ * For example a string like
+ * "220000203707F4F1" gets converted to 0x220000203707F4F1 ...
+ * where
+ * port_wwn[0] = 0x22,
+ * port_wwn[1] = 0x00,
+ * port_wwn[2] = 0x00,
+ * port_wwn[3] = 0x20,
+ * port_wwn[4] = 0x37,
+ * port_wwn[5] = 0x07,
+ * port_wwn[6] = 0xF4, and
+ * port_wwn[7] = 0xF1
+ */
+static int
+string_to_wwn(const uchar_t *string, uchar_t *port_wwn)
+{
+ int i;
+ char c, c1;
+ uchar_t *wwnp;
+
+ wwnp = port_wwn;
+ for (i = 0; i < WWN_SIZE; i++, wwnp++) {
+
+ c = ctoi(*string++);
+ c1 = ctoi(*string++);
+ if (c == -1 || c1 == -1)
+ return (-1);
+ *wwnp = ((c << 4) + c1);
+ }
+
+ return (0);
+}
+
+static int
+create_ap_instance(char *ap_id, char *wwn_string,
+ char *filename, char *line)
+{
+ devctl_hdl_t bus_handle, dev_handle;
+ devctl_ddef_t ddef_handle;
+ int ret;
+ uchar_t wwn_array[FC_WWN_SIZE];
+
+ ddef_handle = devctl_ddef_alloc("dummy", 0);
+ if (ddef_handle == NULL) {
+ log_error("2314",
+ "Internal error to process line (%s) "
+ "in file: %s. %s",
+ line, filename, strerror(errno));
+ return (-1);
+ }
+ /*
+ * g_string_to_wwn() has not been used here because it
+ * prepends 2 NULLs.
+ */
+ if (string_to_wwn((uchar_t *)wwn_string, wwn_array) != 0) {
+ log_error("2314",
+ "Internal error to process line (%s) "
+ "in file: %s. %s",
+ line, filename, strerror(errno));
+ devctl_ddef_free(ddef_handle);
+ return (-1);
+ }
+ (void) devctl_ddef_byte_array(ddef_handle,
+ "port-wwn", FC_WWN_SIZE, wwn_array);
+
+ if ((bus_handle = devctl_bus_acquire(ap_id, 0)) == NULL) {
+ devctl_ddef_free(ddef_handle);
+ log_error("2314",
+ "Internal error to process line (%s) "
+ "in file: %s. %s",
+ line, filename, strerror(errno));
+ return (-1);
+ }
+ if (ret =
+ devctl_bus_dev_create(bus_handle, ddef_handle, 0, &dev_handle)) {
+ devctl_ddef_free(ddef_handle);
+ devctl_release(bus_handle);
+ log_error("2316",
+ "configuration failed for line (%s) "
+ "in file: %s. %s",
+ line, filename, strerror(errno));
+ return (-1);
+ }
+ devctl_release(dev_handle);
+ devctl_ddef_free(ddef_handle);
+ devctl_release(bus_handle);
+ return (ret);
+}
diff --git a/usr/src/cmd/luxadm/fcalupdate.c b/usr/src/cmd/luxadm/fcalupdate.c
new file mode 100644
index 0000000000..c9a8e158fa
--- /dev/null
+++ b/usr/src/cmd/luxadm/fcalupdate.c
@@ -0,0 +1,948 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * I18N message number ranges
+ * This file: 4500 - 4999
+ * Shared common messages: 1 - 1999
+ */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <siginfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/exec.h>
+#include <sys/exechdr.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/fibre-channel/fcio.h>
+#include <sys/socalreg.h>
+/*
+ * The following define is not to
+ * include sys/fc4/fcal_linkapp.h
+ * file from sys/socalio.h, since it
+ * has same structure defines as in
+ * sys/fibre-channel/fcio.h.
+ */
+#define _SYS_FC4_FCAL_LINKAPP_H
+#include <sys/socalio.h>
+#include <sys/time.h>
+#include <nl_types.h>
+#include <errno.h>
+#include <stgcom.h>
+#include <gfc.h>
+#include <l_common.h>
+#include "luxadm.h"
+
+/* Defines */
+#define FEPROM_SIZE 256*1024
+#define FEPROM_MAX_PROGRAM 25
+#define FEPROM_MAX_ERASE 1000
+#define FEPROM_READ_MEMORY 0x00
+#define FEPROM_ERASE 0x20
+#define FEPROM_ERASE_VERIFY 0xa0
+#define FEPROM_PROGRAM 0x40
+#define FEPROM_PROGRAM_VERIFY 0xc0
+#define FEPROM_RESET 0xff
+#define HBA_MAX 128
+
+#define FOUND 0
+#define NOT_FOUND 1
+#define PROM_SIZ 0x20010
+
+#define MAX_RETRIES 3
+#define MAX_WAIT_TIME 30
+
+/*
+ * The next define is to work around a problem with sbusmem driver not
+ * able to round up mmap() requests that are not around page boundaries.
+ */
+#define PROM_SIZ_ROUNDED 0x22000
+#define SAMPLE_SIZ 0x100
+
+#define REG_OFFSET 0x20000
+
+#define FEPROM_WWN_OFFSET 0x3fe00
+
+#define FEPROM_SUN_WWN 0x50200200
+
+/*
+ * We'll leave this on leadville, as the onboard
+ * isn't allowed to be zapped by luxadm
+ */
+
+#define ONBOARD_SOCAL "SUNW,socal@d"
+
+#define SOCAL_STR "SUNW,socal"
+#define SOCAL_STR_LEN 10
+
+
+static uchar_t buffer[FEPROM_SIZE];
+
+static char sbus_list[HBA_MAX][PATH_MAX];
+static char sbussoc_list[HBA_MAX][PATH_MAX];
+static char bootpath[PATH_MAX];
+static char version[MAXNAMELEN];
+
+static uint_t getsbuslist(void);
+static int load_file(char *, caddr_t, volatile socal_reg_t *);
+static void usec_delay(int);
+static void getbootdev(unsigned int);
+static void getsocpath(char *, int *);
+static int loadsocpath(const char *, int *);
+static int warn(void);
+static int findversion(int, uchar_t *);
+static int write_feprom(uchar_t *, uchar_t *, volatile socal_reg_t *);
+static int feprom_erase(volatile uchar_t *, volatile socal_reg_t *);
+
+static struct exec exec;
+
+int
+fcal_update(unsigned int verbose, char *file)
+/*ARGSUSED*/
+{
+int fd, strfound = 0, retval = 0;
+int fbuf_idx, fd1, bytes_read;
+caddr_t addr;
+uint_t i;
+uint_t fflag = 0;
+uint_t vflag = 0;
+uint_t numslots;
+volatile socal_reg_t *regs;
+char *slotname, socal[MAXNAMELEN];
+char fbuf[BUFSIZ];
+
+ if (!file)
+ vflag++;
+ else {
+ fflag++;
+ if ((fd1 = open(file, O_RDONLY)) == -1) {
+ (void) fprintf(stderr,
+ MSGSTR(4500,
+ "Error: open() failed on file "
+ "%s\n"), file);
+ return (1);
+ }
+ /*
+ * We will just make a check to see if it the file
+ * has the "SUNW,socal" strings in it
+ * We cannot use strstr() here because we are operating on
+ * binary data and so is very likely to have embedded NULLs
+ */
+ while (!strfound && ((bytes_read = read(fd1,
+ fbuf, BUFSIZ)) > 0)) {
+ for (fbuf_idx = 0; fbuf_idx < bytes_read;
+ fbuf_idx++) {
+ /* First check for the SUNW,socal string */
+ if (strncmp((fbuf + fbuf_idx), SOCAL_STR,
+ SOCAL_STR_LEN) == 0) {
+ strfound = 1;
+ break;
+ }
+
+ }
+ }
+ (void) close(fd1);
+
+ if (!strfound) {
+ (void) fprintf(stderr,
+ MSGSTR(4501,
+ "Error: %s is not a "
+ "FC100/S Fcode file\n"), file);
+ return (1);
+ }
+ }
+
+ /*
+ * Get count of, and names of SBus slots using the SBus memory
+ * interface.
+ */
+ (void) getbootdev(verbose);
+ if (getenv("_LUX_D_DEBUG") != NULL) {
+ (void) fprintf(stdout, " Bootpath: %s\n", bootpath);
+ }
+
+ numslots = getsbuslist();
+ (void) fprintf(stdout,
+ MSGSTR(4503, "\n Found Path to %d FC100/S Cards\n"), numslots);
+
+ for (i = 0; i < numslots; i++) {
+
+ /*
+ * Open SBus memory for this slot.
+ */
+ slotname = &sbus_list[i][0];
+ if (fflag && (strcmp(slotname, bootpath) == 0)) {
+ (void) fprintf(stderr,
+ MSGSTR(4504, " Ignoring %s (bootpath)\n"), slotname);
+ continue;
+ }
+
+ (void) sprintf(socal, "%s:0", &sbussoc_list[i][0]);
+
+ if ((fd = open(socal, O_RDWR)) < 0) {
+ (void) sprintf(socal, "%s:1", &sbussoc_list[i][0]);
+ if ((fd = open(socal, O_RDWR)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(4505, "Could not open %s\n"),
+ &sbussoc_list[i][0]);
+ (void) fprintf(stderr,
+ MSGSTR(4506, "Ignoring %s\n"),
+ &sbussoc_list[i][0]);
+ retval++;
+ continue;
+ }
+ }
+
+ (void) close(fd);
+
+ if (verbose) {
+ (void) fprintf(stdout, "\n ");
+ (void) fprintf(stdout,
+ MSGSTR(85, "Opening %s\n"), slotname);
+ }
+
+ fd = open(slotname, O_RDWR);
+
+ if (fd < 0) {
+ perror(MSGSTR(4507, "open of slotname"));
+ retval++;
+ continue;
+ }
+
+ /*
+ * Mmap that SBus memory into my memory space.
+ */
+ addr = mmap((caddr_t)0, PROM_SIZ_ROUNDED, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+
+ if (addr == MAP_FAILED) {
+ perror(MSGSTR(46, "mmap"));
+ (void) close(fd);
+ retval++;
+ continue;
+ }
+
+ if ((int)addr == -1) {
+ perror(MSGSTR(46, "mmap"));
+ (void) close(fd);
+ retval++;
+ continue;
+ }
+
+ regs = (socal_reg_t *)((int)addr + REG_OFFSET);
+
+ (void) fprintf(stdout,
+ MSGSTR(4508, "\n Device: %s\n"),
+ &sbussoc_list[i][0]);
+ /*
+ * Load the New FCode
+ */
+ if (fflag) {
+ if (!warn())
+ retval += load_file(file, addr, regs);
+ } else if (vflag) {
+ if (findversion(i, (uchar_t *)&version[0]) == FOUND) {
+ (void) fprintf(stdout,
+ MSGSTR(4509,
+ " Detected FC100/S Version: %s\n"), version);
+ }
+ }
+
+ if (munmap(addr, PROM_SIZ) == -1) {
+ perror(MSGSTR(4510, "munmap"));
+ retval++;
+ }
+
+ (void) close(fd);
+
+ }
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(125, "Complete\n"));
+ return (retval);
+}
+
+static int
+findversion(int index, uchar_t *version)
+/*ARGSUSED*/
+{
+int fd, ntries;
+struct socal_fm_version *buffer;
+char socal[MAXNAMELEN];
+char fp[MAXNAMELEN];
+char prom_ver[100];
+char mcode_ver[100];
+uint_t dev_type;
+fcio_t fcio;
+char fw_rev[FC_FW_REV_SIZE + 1];
+
+
+ if ((dev_type = g_get_path_type(&sbussoc_list[index][0])) == 0) {
+ return (L_INVALID_PATH);
+ }
+
+
+ if (dev_type & FC4_FCA_MASK) {
+ P_DPRINTF("findversion: found an FC4 path\n");
+ (void) sprintf(socal, "%s:0", &sbussoc_list[index][0]);
+ if ((fd = open(socal, O_RDWR)) < 0) {
+ (void) sprintf(socal, "%s:1", &sbussoc_list[index][0]);
+ if ((fd = open(socal, O_RDWR)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(4511, "Could not open %s\n"),
+ &sbussoc_list[index][0]);
+ (void) close(fd);
+ return (NOT_FOUND);
+ }
+ }
+ if ((buffer = (struct socal_fm_version *)malloc(
+ sizeof (struct socal_fm_version))) == NULL) {
+ (void) fprintf(stderr, MSGSTR(10,
+ " Error: Unable to allocate memory."));
+ (void) fprintf(stderr, "\n");
+ (void) close(fd);
+ return (NOT_FOUND);
+ }
+
+ buffer->fcode_ver = (char *)version;
+ buffer->mcode_ver = mcode_ver;
+ buffer->prom_ver = prom_ver;
+ buffer->fcode_ver_len = MAXNAMELEN - 1;
+ buffer->mcode_ver_len = 100;
+ buffer->prom_ver_len = 100;
+
+ if (ioctl(fd, FCIO_FCODE_MCODE_VERSION, buffer) < 0) {
+ (void) fprintf(stderr, MSGSTR(4512,
+ "fcal_s_download: could not get"
+ " fcode version.\n"));
+ (void) close(fd);
+ (void) free(buffer);
+ return (NOT_FOUND);
+ }
+ version[buffer->fcode_ver_len] = '\0';
+ free(buffer);
+
+ } else if (dev_type & FC_FCA_MASK) {
+ /*
+ * Get the fcode and prom's fw version
+ * using new ioctls. Currently, we pass
+ * only the fcode version to the calling function
+ * and ignore the FW version (using the existing
+ * implementation). The function definition
+ * might be changed in future to pass both the
+ * fcode and FW revisions to the calling function, if
+ * needed by the calling function.
+ */
+ P_DPRINTF("findversion: found an FC path\n");
+ (void) sprintf(fp, "%s/fp@0,0:devctl",
+ &sbussoc_list[index][0]);
+ if ((fd = open(fp, O_RDWR)) < 0) {
+ (void) sprintf(fp, "%s/fp@1,0:devctl",
+ &sbussoc_list[index][0]);
+ if ((fd = open(fp, O_RDWR)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(4511, "Could not open %s\n"),
+ &sbussoc_list[index][0]);
+ (void) close(fd);
+ return (NOT_FOUND);
+ }
+ }
+ /* Get the fcode version */
+ bzero(version, sizeof (version));
+ fcio.fcio_cmd = FCIO_GET_FCODE_REV;
+ /* Information read operation */
+ fcio.fcio_xfer = FCIO_XFER_READ;
+ fcio.fcio_obuf = (caddr_t)version;
+ fcio.fcio_olen = MAXNAMELEN;
+
+ for (ntries = 0; ntries < MAX_RETRIES; ntries++) {
+ if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
+ if ((errno == EAGAIN) &&
+ (ntries+1 < MAX_RETRIES)) {
+ /* wait 30 secs */
+ (void) sleep(MAX_WAIT_TIME);
+ continue;
+ }
+ I_DPRINTF("ioctl FCIO_GET_FCODE_REV failed.\n"
+ "Error: %s\n", strerror(errno));
+ (void) close(fd);
+ return (L_FCIO_GET_FCODE_REV_FAIL);
+ }
+ break;
+ }
+ version[MAXNAMELEN-1] = '\0';
+
+ /* Get the FW revision */
+ bzero(fw_rev, sizeof (fw_rev));
+ fcio.fcio_cmd = FCIO_GET_FW_REV;
+ /* Information read operation */
+ fcio.fcio_xfer = FCIO_XFER_READ;
+ fcio.fcio_obuf = (caddr_t)fw_rev;
+ fcio.fcio_olen = FC_FW_REV_SIZE;
+ for (ntries = 0; ntries < MAX_RETRIES; ntries++) {
+ if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
+ if ((errno == EAGAIN) &&
+ (ntries+1 < MAX_RETRIES)) {
+ /* wait 30 secs */
+ (void) sleep(MAX_WAIT_TIME);
+ continue;
+ }
+ I_DPRINTF(" FCIO_GET_FW_REV ioctl failed.\n"
+ " Error: %s\n", strerror(errno));
+ (void) close(fd);
+ return (L_FCIO_GET_FW_REV_FAIL);
+ }
+ break;
+ }
+ }
+
+ (void) close(fd);
+ return (FOUND);
+}
+/*
+ * program an FEprom with data from 'source_address'.
+ * program the FEprom with zeroes,
+ * erase it,
+ * program it with the real data.
+ */
+static int
+feprom_program(uchar_t *source_address, uchar_t *dest_address,
+ volatile socal_reg_t *regs)
+{
+ int i;
+
+ (void) fprintf(stdout, MSGSTR(4513, "Filling with zeroes...\n"));
+ if (!write_feprom((uchar_t *)0, dest_address, regs)) {
+ (void) fprintf(stderr,
+ MSGSTR(4514, "FEprom at 0x%x: zero fill failed\n"),
+ (int)dest_address);
+ return (0);
+ }
+
+ (void) fprintf(stdout, MSGSTR(4515, "Erasing...\n"));
+ for (i = 0; i < FEPROM_MAX_ERASE; i++) {
+ if (feprom_erase(dest_address, regs))
+ break;
+ }
+
+ if (i >= FEPROM_MAX_ERASE) {
+ (void) fprintf(stderr,
+ MSGSTR(4516, "FEprom at 0x%x: failed to erase\n"),
+ (int)dest_address);
+ return (0);
+ } else if (i > 0) {
+ if (i == 1) {
+ (void) fprintf(stderr, MSGSTR(4517,
+ "FEprom erased after %d attempt\n"), i);
+ } else {
+ (void) fprintf(stderr, MSGSTR(4518,
+ "FEprom erased after %d attempts\n"), i);
+ }
+ }
+
+ (void) fprintf(stdout, MSGSTR(4519, "Programming...\n"));
+ if (!(write_feprom(source_address, dest_address, regs))) {
+ (void) fprintf(stderr,
+ MSGSTR(4520, "FEprom at 0x%x: write failed\n"),
+ (int)dest_address);
+ return (0);
+ }
+
+ /* select the zeroth bank at end so we can read it */
+ regs->socal_cr.w &= ~(0x30000);
+ (void) fprintf(stdout, MSGSTR(4521, "Programming done\n"));
+ return (1);
+}
+
+/*
+ * program an FEprom one byte at a time using hot electron injection.
+ */
+static int
+write_feprom(uchar_t *source_address, uchar_t *dest_address,
+ volatile socal_reg_t *regs)
+{
+ int pulse, i;
+ uchar_t *s = source_address;
+ volatile uchar_t *d;
+
+ for (i = 0; i < FEPROM_SIZE; i++, s++) {
+
+ if ((i & 0xffff) == 0) {
+ (void) fprintf(stdout,
+ MSGSTR(4522, "selecting bank %d\n"), i>>16);
+ regs->socal_cr.w &= ~(0x30000);
+ regs->socal_cr.w |= i & 0x30000;
+ }
+
+ d = dest_address + (i & 0xffff);
+
+ for (pulse = 0; pulse < FEPROM_MAX_PROGRAM; pulse++) {
+ *d = FEPROM_PROGRAM;
+ *d = source_address ? *s : 0;
+ usec_delay(50);
+ *d = FEPROM_PROGRAM_VERIFY;
+ usec_delay(30);
+ if (*d == (source_address ? *s : 0))
+ break;
+ }
+
+ if (pulse >= FEPROM_MAX_PROGRAM) {
+ *dest_address = FEPROM_RESET;
+ return (0);
+ }
+ }
+
+ *dest_address = FEPROM_RESET;
+ return (1);
+}
+
+/*
+ * erase an FEprom using Fowler-Nordheim tunneling.
+ */
+static int
+feprom_erase(volatile uchar_t *dest_address, volatile socal_reg_t *regs)
+{
+ int i;
+ volatile uchar_t *d = dest_address;
+
+ *d = FEPROM_ERASE;
+ usec_delay(50);
+ *d = FEPROM_ERASE;
+
+ usec_delay(10000); /* wait 10ms while FEprom erases */
+
+ for (i = 0; i < FEPROM_SIZE; i++) {
+
+ if ((i & 0xffff) == 0) {
+ regs->socal_cr.w &= ~(0x30000);
+ regs->socal_cr.w |= i & 0x30000;
+ }
+
+ d = dest_address + (i & 0xffff);
+
+ *d = FEPROM_ERASE_VERIFY;
+ usec_delay(50);
+ if (*d != 0xff) {
+ *dest_address = FEPROM_RESET;
+ return (0);
+ }
+ }
+ *dest_address = FEPROM_RESET;
+ return (1);
+}
+
+static void
+usec_delay(int s)
+{
+ hrtime_t now, then;
+
+ now = gethrtime();
+ then = now + s*1000;
+ do {
+ now = gethrtime();
+ } while (now < then);
+}
+
+static uint_t
+getsbuslist(void)
+{
+ int devcnt = 0;
+ char devpath[PATH_MAX];
+
+ /* We're searching the /devices directory, so... */
+ (void) strcpy(devpath, "/devices");
+
+ /* get the directory entries under /devices for socal sbusmem */
+ (void) getsocpath(devpath, (int *)&devcnt);
+
+ return (devcnt);
+}
+
+static void
+getbootdev(unsigned int verbose)
+{
+ char *df = "df /";
+ FILE *ptr;
+ char *p, *p1;
+ char bootdev[PATH_MAX];
+ char buf[BUFSIZ];
+ int foundroot = 0;
+
+
+ if ((ptr = popen(df, "r")) != NULL) {
+ while (fgets(buf, BUFSIZ, ptr) != NULL) {
+ if (p = strstr(buf, "/dev/dsk/")) {
+ (void) memset((char *)&bootdev[0], 0,
+ PATH_MAX);
+ p1 = p;
+ while (*p1 != '\0') {
+ if (!isalnum(*p1) && (*p1 != '/'))
+ *p1 = ' ';
+ p1++;
+ }
+ (void) sscanf(p, "%s", bootdev);
+ foundroot = 1;
+ }
+ }
+ if (!foundroot) {
+ if (verbose)
+ (void) fprintf(stderr, MSGSTR(44,
+ "root is not on a local disk!\n"));
+ (void) memset((char *)&bootpath[0], 0, PATH_MAX);
+ return;
+ }
+ (void) pclose(ptr);
+ if (bootdev[0]) {
+ char *ls;
+ char *p1;
+ char *p2 = NULL;
+ char *sbusmem = "/sbusmem@";
+ char *slot = ",0:slot";
+
+ ls = (char *)malloc(PATH_MAX);
+ (void) memset((char *)ls, NULL, PATH_MAX);
+ (void) strcpy(ls, "ls -l ");
+ (void) strcat(ls, bootdev);
+ if ((ptr = popen(ls, "r")) != NULL) {
+ while (fgets(buf, BUFSIZ, ptr) != NULL) {
+ if (p = strstr(buf, "/devices")) {
+ if (p1 = strstr(buf, "sbus")) {
+ while (*p1 != '/')
+ p1++;
+ p2 = strstr(p1, "@");
+ ++p2;
+ *p1 = '\0';
+ } else {
+ if (p1 = strstr(buf,
+ SOCAL_STR)) {
+ p2 = strstr(p1, "@");
+ ++p2;
+ --p1;
+ *p1 = '\0';
+ }
+ }
+ }
+ }
+ (void) pclose(ptr);
+ }
+ (void) memset((char *)&bootdev[0], 0, PATH_MAX);
+ (void) sscanf(p, "%s", bootdev);
+ (void) memset((char *)&bootpath[0], 0, PATH_MAX);
+ (void) strcat(bootpath, bootdev);
+ (void) strcat(bootpath, sbusmem);
+ if (p2) {
+ (void) strncat(bootpath, p2, 1);
+ (void) strcat(bootpath, slot);
+ (void) strncat(bootpath, p2, 1);
+ }
+ }
+ }
+}
+
+/*
+ * This function reads "size" bytes from the FC100/S PROM.
+ * source_address: PROM address
+ * dest_address: local memeory
+ * offset: Location in PROM to start reading from.
+ */
+static void
+feprom_read(uchar_t *source_address, uchar_t *dest_address,
+ int offset, int size, volatile socal_reg_t *regs)
+{
+uchar_t *s = source_address;
+uchar_t *d = dest_address;
+int i = offset;
+
+ if (getenv("_LUX_D_DEBUG") != NULL) {
+ (void) fprintf(stdout,
+ " feprom_read: selecting bank %d\n",
+ (i&0xf0000)>>16);
+ if (size <= 8) {
+ (void) fprintf(stdout, " Data read: ");
+ }
+ }
+ regs->socal_cr.w = i & 0xf0000;
+ s = source_address + (i & 0xffff);
+ *s = FEPROM_READ_MEMORY;
+ usec_delay(6);
+ for (; s < source_address + (i & 0xffff) + size; d++, s++) {
+ *d = *s;
+ if ((getenv("_LUX_D_DEBUG") != NULL) &&
+ (size <= 8)) {
+ (void) fprintf(stdout, "0x%x ", *d);
+ }
+ }
+ if ((getenv("_LUX_D_DEBUG") != NULL) &&
+ (size <= 8)) {
+ (void) fprintf(stdout, "\n From offset: 0x%x\n",
+ offset);
+ }
+}
+
+
+static int
+load_file(char *file, caddr_t prom, volatile socal_reg_t *regs)
+{
+uint_t wwn_d8, wwn_lo;
+uint_t wwn_hi;
+int ffd = open(file, 0);
+
+ wwn_hi = FEPROM_SUN_WWN;
+
+ if (ffd < 0) {
+ perror(MSGSTR(4524, "open of file"));
+ exit(1);
+ }
+ (void) fprintf(stdout, MSGSTR(4525, "Loading FCode: %s\n"), file);
+
+ if (read(ffd, &exec, sizeof (exec)) != sizeof (exec)) {
+ perror(MSGSTR(4526, "read exec"));
+ exit(1);
+ }
+
+ if (exec.a_trsize || exec.a_drsize) {
+ (void) fprintf(stderr,
+ MSGSTR(4527, "%s: is relocatable\n"), file);
+ exit(1);
+ }
+
+ if (exec.a_data || exec.a_bss) {
+ (void) fprintf(stderr,
+ MSGSTR(4528, "%s: has data or bss\n"), file);
+ exit(1);
+ }
+
+ if (exec.a_machtype != M_SPARC) {
+ (void) fprintf(stderr,
+ MSGSTR(4529, "%s: not for SPARC\n"), file);
+ exit(1);
+ }
+
+ (void) fprintf(stdout, MSGSTR(4530,
+ "Loading 0x%x bytes from %s at offset 0x%x\n"),
+ (int)exec.a_text, file, 0);
+
+ if (read(ffd, &buffer, exec.a_text) != exec.a_text) {
+ perror(MSGSTR(4531, "read"));
+ exit(1);
+ }
+
+ (void) close(ffd);
+
+ feprom_read((uchar_t *)prom, (uchar_t *)&wwn_d8,
+ FEPROM_WWN_OFFSET, 4, regs);
+ feprom_read((uchar_t *)prom, (uchar_t *)&wwn_lo,
+ FEPROM_WWN_OFFSET + 4, 4, regs);
+ wwn_hi |= wwn_d8 & 0x0f; /* only last digit is interesting */
+ if (getenv("_LUX_D_DEBUG") != NULL) {
+ (void) fprintf(stdout,
+ " load_file: Writing WWN hi:0x%x lo:0x%x "
+ "to the FC100/S PROM\n", wwn_hi, wwn_lo);
+ }
+ /* put wwn into buffer location */
+ bcopy((const void *)&wwn_hi,
+ (void *)&buffer[FEPROM_WWN_OFFSET],
+ sizeof (wwn_hi));
+ bcopy((const void *)&wwn_lo,
+ (void *)&buffer[FEPROM_WWN_OFFSET + 4],
+ sizeof (wwn_lo));
+ bcopy((const void *)&wwn_hi,
+ (void *)&buffer[FEPROM_WWN_OFFSET + 8],
+ sizeof (wwn_hi));
+ bcopy((const void *)&wwn_lo,
+ (void *)&buffer[FEPROM_WWN_OFFSET + 0xc],
+ sizeof (wwn_lo));
+
+ if (feprom_program((uchar_t *)buffer, (uchar_t *)prom, regs) == 0) {
+ /* here 0 means failure */
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+warn(void)
+{
+ char input[1024];
+
+ input[0] = '\0';
+
+ (void) fprintf(stderr, MSGSTR(4532,
+"\nWARNING!! This program will update the FCode in this FC100/S Sbus Card.\n"));
+ (void) fprintf(stderr, MSGSTR(4533,
+"This may take a few (5) minutes. Please be patient.\n"));
+
+loop1:
+ (void) fprintf(stderr, MSGSTR(4534,
+ "Do you wish to continue ? (y/n) "));
+
+ (void) gets(input);
+
+ if ((strcmp(input, MSGSTR(4535, "y")) == 0) ||
+ (strcmp(input, MSGSTR(40, "yes")) == 0)) {
+ return (FOUND);
+ } else if ((strcmp(input, MSGSTR(4536, "n")) == 0) ||
+ (strcmp(input, MSGSTR(45, "no")) == 0)) {
+ (void) fprintf(stderr, MSGSTR(4537, "Not Downloading FCode\n"));
+ return (NOT_FOUND);
+ } else {
+ (void) fprintf(stderr, MSGSTR(4538, "Invalid input\n"));
+ goto loop1;
+ }
+}
+
+
+/*
+ * getsocpath():
+ * Searches the /devices directory recursively returning all soc_name
+ * entries in sbussoc_list (global). This excludes port entries and
+ * onboard socal (which leaves only directory entries with
+ * soc_name included). devcnt is updated to reflect number of soc_name
+ * devices found.
+ */
+
+static void
+getsocpath(char *devpath, int *devcnt)
+{
+ struct stat statbuf;
+ struct dirent *dirp;
+ DIR *dp;
+ char *ptr;
+
+ if (lstat(devpath, &statbuf) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(4539, "Error: %s lstat() error\n"), devpath);
+ exit(1);
+ }
+
+ if (S_ISDIR(statbuf.st_mode) == 0)
+ /*
+ * not a directory so
+ * we don't care about it - return
+ */
+ return;
+
+ else {
+ if (strstr(devpath, ONBOARD_SOCAL))
+ return;
+
+ if (strstr(devpath, SOCAL_STR)) {
+ /* It's a keeper - call the load function */
+ if ((loadsocpath(devpath, devcnt)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(4540, "Error: Cannot set device list\n"),
+ devpath);
+ exit(1);
+ }
+ /*
+ * if socal directory - return,
+ * nothing else to see here
+ */
+ return;
+ }
+ }
+
+ /*
+ * It's a directory. Call ourself to
+ * traverse the path(s)
+ */
+
+ ptr = devpath + strlen(devpath);
+ *ptr++ = '/';
+ *ptr = 0;
+
+ /* Forget the /devices/pseudo/ directory */
+ if (strcmp(devpath, "/devices/pseudo/") == 0)
+ return;
+
+ if ((dp = opendir(devpath)) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(4541, "Error: %s Can't read directory\n"), devpath);
+ exit(1);
+ }
+
+ while ((dirp = readdir(dp)) != NULL) {
+
+ if (strcmp(dirp->d_name, ".") == 0 ||
+ strcmp(dirp->d_name, "..") == 0)
+ continue;
+
+ (void) strcpy(ptr, dirp->d_name); /* append name */
+ getsocpath(devpath, devcnt);
+ }
+
+ if (closedir(dp) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(4542, "Error: %s Can't close directory\n"), devpath);
+ exit(1);
+ }
+}
+
+static int
+loadsocpath(const char *pathname, int *devcnt)
+{
+ int ret = 0;
+ int len;
+ int len_tmp;
+ char *sp;
+ char *sp_tmp;
+ char buffer[PATH_MAX];
+
+
+ /*
+ * Okay we found a device, now let's load it in to sbussoc_list
+ * and load the sbusmem file into sbus_list
+ */
+
+ if (pathname != NULL && *devcnt < HBA_MAX) {
+ (void) strcpy(sbussoc_list[*devcnt], pathname);
+ if (sp_tmp = strstr(sbussoc_list[*devcnt], SOCAL_STR)) {
+ sp = sp_tmp;
+ /* len_tmp will be len of "SUNW,socal@" */
+ len_tmp = SOCAL_STR_LEN + 1;
+ }
+ len = strlen(sbussoc_list[*devcnt]) - strlen(sp);
+ (void) strncpy(buffer, sbussoc_list[*devcnt], len);
+ buffer[len] = '\0';
+ sp += len_tmp;
+ (void) sprintf(sbus_list[*devcnt], "%ssbusmem@%c,0:slot%c",
+ buffer, sp[0], sp[0]);
+ *devcnt += 1;
+ }
+ else
+ ret = -1;
+ return (ret);
+}
diff --git a/usr/src/cmd/luxadm/fchba.c b/usr/src/cmd/luxadm/fchba.c
new file mode 100644
index 0000000000..b353532d10
--- /dev/null
+++ b/usr/src/cmd/luxadm/fchba.c
@@ -0,0 +1,2151 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <hbaapi.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/mode.h>
+#include <sys/scsi/generic/inquiry.h>
+#include <errno.h>
+#include <libdevice.h>
+#include <config_admin.h>
+#include <sys/byteorder.h>
+#include <sys/fibre-channel/fcio.h>
+#include "common.h"
+#include "sun_fc_version.h"
+
+#define DEFAULT_LUN_COUNT 1024
+#define LUN_SIZE 8
+#define LUN_HEADER_SIZE 8
+#define DEFAULT_LUN_LENGTH DEFAULT_LUN_COUNT * \
+ LUN_SIZE + \
+ LUN_HEADER_SIZE
+struct lun_val {
+ uchar_t val[8];
+};
+struct rep_luns_rsp {
+ uint32_t length;
+ uint32_t rsrvd;
+ struct lun_val lun[1];
+};
+
+/* Extracted from the old scsi.h file */
+struct capacity_data_struct {
+ uint_t last_block_addr;
+ uint_t block_size;
+};
+
+
+/* Structure to handle the inq. page 0x80 serial number */
+struct page80 {
+ uchar_t inq_dtype;
+ uchar_t inq_page_code;
+ uchar_t reserved;
+ uchar_t inq_page_len;
+ uchar_t inq_serial[251];
+};
+
+extern char *dtype[];
+extern int Options;
+extern const int OPTION_P;
+
+int skip_hba(int i);
+int find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn,
+ uint64_t lun, int page_num);
+/*
+ * The routines within this file operate against the T11
+ * HBA API interface. In some cases, proprietary Sun driver
+ * interface are also called to add additional information
+ * above what the standard library supports.
+ */
+
+uint64_t
+wwnConversion(uchar_t *wwn) {
+ uint64_t tmp;
+ (void) memcpy(&tmp, wwn, sizeof (uint64_t));
+ return (ntohll(tmp));
+}
+
+void printStatus(HBA_STATUS status) {
+ switch (status) {
+ case HBA_STATUS_OK:
+ printf(MSGSTR(2410, "OK"));
+ return;
+ case HBA_STATUS_ERROR:
+ printf(MSGSTR(2411, "ERROR"));
+ return;
+ case HBA_STATUS_ERROR_NOT_SUPPORTED:
+ printf(MSGSTR(2412, "NOT SUPPORTED"));
+ return;
+ case HBA_STATUS_ERROR_INVALID_HANDLE:
+ printf(MSGSTR(2413, "INVALID HANDLE"));
+ return;
+ case HBA_STATUS_ERROR_ARG:
+ printf(MSGSTR(2414, "ERROR ARG"));
+ return;
+ case HBA_STATUS_ERROR_ILLEGAL_WWN:
+ printf(MSGSTR(2415, "ILLEGAL WWN"));
+ return;
+ case HBA_STATUS_ERROR_ILLEGAL_INDEX:
+ printf(MSGSTR(2416, "ILLEGAL INDEX"));
+ return;
+ case HBA_STATUS_ERROR_MORE_DATA:
+ printf(MSGSTR(2417, "MORE DATA"));
+ return;
+ case HBA_STATUS_ERROR_STALE_DATA:
+ printf(MSGSTR(2418, "STALE DATA"));
+ return;
+ case HBA_STATUS_SCSI_CHECK_CONDITION:
+ printf(MSGSTR(2419, "SCSI CHECK CONDITION"));
+ return;
+ case HBA_STATUS_ERROR_BUSY:
+ printf(MSGSTR(2420, "BUSY"));
+ return;
+ case HBA_STATUS_ERROR_TRY_AGAIN:
+ printf(MSGSTR(2421, "TRY AGAIN"));
+ return;
+ case HBA_STATUS_ERROR_UNAVAILABLE:
+ printf(MSGSTR(2422, "UNAVAILABLE"));
+ return;
+ default:
+ printf(MSGSTR(2423, "UNKNOWN ERROR TYPE %d"), status);
+ return;
+ }
+}
+
+uint32_t
+getNumberOfAdapters() {
+ uint32_t count = HBA_GetNumberOfAdapters();
+ if (count == 0) {
+ fprintf(stderr, MSGSTR(2405,
+ "\nERROR: No Fibre Channel Adapters found.\n"));
+ }
+ return (count);
+}
+
+#define MAX_RETRIES 10
+
+/*
+ * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
+ * Will handle retries if applicable.
+ */
+int
+getAdapterAttrs(HBA_HANDLE handle, char *name, HBA_ADAPTERATTRIBUTES *attrs) {
+ int count = 0;
+ HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
+
+ /* Loop as long as we have a retryable error */
+ while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
+ status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) {
+ status = HBA_GetAdapterAttributes(handle, attrs);
+ if (status == HBA_STATUS_OK) {
+ break;
+ }
+ (void) sleep(1);
+ }
+ if (status != HBA_STATUS_OK) {
+ /* We encountered a non-retryable error */
+ fprintf(stderr, MSGSTR(2501,
+ "\nERROR: Unable to retrieve adapter port details (%s)"),
+ name);
+ printStatus(status);
+ fprintf(stderr, "\n");
+ }
+ return (status);
+}
+
+/*
+ * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
+ * Will handle retries if applicable.
+ */
+int
+getAdapterPortAttrs(HBA_HANDLE handle, char *name, int portIndex,
+ HBA_PORTATTRIBUTES *attrs) {
+ int count = 0;
+ HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
+
+ /* Loop as long as we have a retryable error */
+ while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
+ status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) {
+ status = HBA_GetAdapterPortAttributes(handle, portIndex, attrs);
+ if (status == HBA_STATUS_OK) {
+ break;
+ }
+
+ /* The odds of this occuring are very slim, but possible. */
+ if (status == HBA_STATUS_ERROR_STALE_DATA) {
+ /*
+ * If we hit a stale data scenario,
+ * we'll just tell the user to try again.
+ */
+ status = HBA_STATUS_ERROR_TRY_AGAIN;
+ break;
+ }
+ sleep(1);
+ }
+ if (status != HBA_STATUS_OK) {
+ /* We encountered a non-retryable error */
+ fprintf(stderr, MSGSTR(2501,
+ "\nERROR: Unable to retrieve adapter port details (%s)"),
+ name);
+ printStatus(status);
+ fprintf(stderr, "\n");
+ }
+ return (status);
+}
+
+/*
+ * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
+ * Will handle retries if applicable.
+ */
+int
+getDiscPortAttrs(HBA_HANDLE handle, char *name, int portIndex, int discIndex,
+ HBA_PORTATTRIBUTES *attrs) {
+ int count = 0;
+ HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
+
+ /* Loop as long as we have a retryable error */
+ while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
+ status == HBA_STATUS_ERROR_BUSY) && count++ < MAX_RETRIES) {
+ status = HBA_GetDiscoveredPortAttributes(handle, portIndex,
+ discIndex, attrs);
+ if (status == HBA_STATUS_OK) {
+ break;
+ }
+
+ /* The odds of this occuring are very slim, but possible. */
+ if (status == HBA_STATUS_ERROR_STALE_DATA) {
+ /*
+ * If we hit a stale data scenario, we'll just tell the
+ * user to try again.
+ */
+ status = HBA_STATUS_ERROR_TRY_AGAIN;
+ break;
+ }
+ sleep(1);
+ }
+ if (status != HBA_STATUS_OK) {
+ /* We encountered a non-retryable error */
+ fprintf(stderr, MSGSTR(2504,
+ "\nERROR: Unable to retrieve target port details (%s)"),
+ name);
+ printStatus(status);
+ fprintf(stderr, "\n");
+ }
+ return (status);
+}
+
+
+/*ARGSUSED*/
+int
+fchba_display_port(int verbose)
+{
+ int retval = 0;
+ HBA_HANDLE handle;
+ HBA_ADAPTERATTRIBUTES hbaAttrs;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_STATUS status;
+ int count, adapterIndex, portIndex;
+ char name[256];
+ char *physical = NULL;
+ char path[MAXPATHLEN];
+
+ if ((retval = loadLibrary())) {
+ return (retval);
+ }
+
+ count = getNumberOfAdapters();
+
+ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
+ if (skip_hba(adapterIndex)) {
+ continue;
+ }
+ status = HBA_GetAdapterName(adapterIndex, (char *)&name);
+ if (status != HBA_STATUS_OK) {
+ /* Just skip it, maybe it was DR'd */
+ continue;
+ }
+ handle = HBA_OpenAdapter(name);
+ if (handle == 0) {
+ /* Just skip it, maybe it was DR'd */
+ continue;
+ }
+
+ if (getAdapterAttrs(handle, name, &hbaAttrs)) {
+ /* This should never happen, we'll just skip the adapter */
+ HBA_CloseAdapter(handle);
+ continue;
+ }
+
+ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
+ portIndex++) {
+ if (getAdapterPortAttrs(handle, name, portIndex,
+ &portAttrs)) {
+ continue;
+ }
+ physical = get_slash_devices_from_osDevName(
+ portAttrs.OSDeviceName,
+ STANDARD_DEVNAME_HANDLING);
+ if (physical) {
+ char *tmp = strstr(physical, ":fc");
+ if (tmp) {
+ *tmp = '\0';
+ (void) snprintf(path, MAXPATHLEN, "%s:devctl",
+ physical);
+ } else {
+ (void) snprintf(path, MAXPATHLEN, "%s",
+ physical);
+ }
+ free(physical);
+ physical = NULL;
+ (void) printf("%-65s ", path);
+ } else {
+ (void) printf("%-65s ", portAttrs.OSDeviceName);
+ }
+ if (portAttrs.NumberofDiscoveredPorts > 0) {
+ printf(MSGSTR(2233, "CONNECTED\n"));
+ } else {
+ printf(MSGSTR(2234, "NOT CONNECTED\n"));
+ }
+ }
+ }
+ (void) HBA_FreeLibrary();
+ return (retval);
+}
+
+/*
+ * Internal routines/structure to deal with a path list
+ * so we can ensure uniqueness
+ */
+struct path_entry {
+ char path[MAXPATHLEN];
+ HBA_UINT8 wwn[8];
+ uchar_t dtype;
+ struct path_entry *next;
+};
+void add_path(struct path_entry *head, struct path_entry *cur) {
+ struct path_entry *tmp;
+ for (tmp = head; tmp->next != NULL; tmp = tmp->next) { }
+ tmp->next = cur;
+}
+struct path_entry *is_duplicate_path(struct path_entry *head, char *path) {
+ struct path_entry *tmp;
+ for (tmp = head; tmp != NULL; tmp = tmp->next) {
+ if (strncmp(tmp->path, path, sizeof (tmp->path)) == 0) {
+ return (tmp);
+ }
+ }
+ return (NULL);
+}
+void free_path_list(struct path_entry *head) {
+ struct path_entry *tmp;
+ struct path_entry *tmp2;
+ for (tmp = head; tmp != NULL; ) {
+ tmp2 = tmp->next;
+ free(tmp);
+ tmp = tmp2;
+ }
+}
+
+
+int
+is_wwn(char *arg) {
+ int i;
+ if (strlen(arg) == 16) {
+ for (i = 0; i < 16; i++) {
+ if (!isxdigit(arg[i])) {
+ return (0);
+ }
+ }
+ return (1);
+ }
+ return (0);
+}
+
+int
+is_path(char *arg) {
+ struct stat buf;
+ if (stat(arg, &buf)) {
+ return (0);
+ }
+ return (1);
+}
+
+/* We take a wild guess for our first get target mappings call */
+#define MAP_GUESS 50
+
+HBA_STATUS
+fetch_mappings(HBA_HANDLE handle, HBA_WWN pwwn, HBA_FCPTARGETMAPPINGV2 **map) {
+ int loop = 0;
+ int count = 0;
+ HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
+ *map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1,
+ (sizeof (HBA_FCPSCSIENTRYV2)* (MAP_GUESS-1)) +
+ sizeof (HBA_FCPTARGETMAPPINGV2));
+
+ /* Loop as long as we have a retryable error */
+ while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
+ status == HBA_STATUS_ERROR_BUSY ||
+ status == HBA_STATUS_ERROR_MORE_DATA) && loop++ < MAX_RETRIES) {
+ status = HBA_GetFcpTargetMappingV2(handle, pwwn, *map);
+ if (status == HBA_STATUS_OK) {
+ break;
+ } else if (status == HBA_STATUS_ERROR_MORE_DATA) {
+ count = (*map)->NumberOfEntries;
+ free(*map);
+ *map = (HBA_FCPTARGETMAPPINGV2 *) calloc(1,
+ (sizeof (HBA_FCPSCSIENTRYV2)* (count-1)) +
+ sizeof (HBA_FCPTARGETMAPPINGV2));
+ (*map)->NumberOfEntries = count;
+ continue;
+ }
+ sleep(1);
+ }
+ if (status != HBA_STATUS_OK) {
+ /* We encountered a non-retryable error */
+ fprintf(stderr, MSGSTR(2502,
+ "\nERROR: Unable to retrieve SCSI device paths "
+ "(HBA Port WWN %016llx)"),
+ wwnConversion(pwwn.wwn));
+ printStatus(status);
+ fprintf(stderr, "\n");
+ }
+ return (status);
+}
+
+/*
+ * Returns the index of the first match, or -1 if no match
+ */
+int
+match_mappings(char *compare, HBA_FCPTARGETMAPPINGV2 *map) {
+ int mapIndex;
+ char *physical = NULL;
+ char *tmp;
+ int wwnCompare = 0;
+ uint64_t wwn;
+
+ if (map == NULL || compare == NULL) {
+ return (-1);
+ }
+
+ if (is_wwn(compare)) {
+ wwnCompare = 1;
+ (void) sscanf(compare, "%016llx", &wwn);
+ } else {
+ /* Convert the paths to phsyical paths */
+ physical = get_slash_devices_from_osDevName(compare,
+ STANDARD_DEVNAME_HANDLING);
+ }
+
+ for (mapIndex = 0; mapIndex < map->NumberOfEntries; mapIndex ++) {
+ if (wwnCompare) {
+ if (wwn == wwnConversion(
+ map->entry[mapIndex].FcpId.NodeWWN.wwn) ||
+ wwn == wwnConversion(
+ map->entry[mapIndex].FcpId.PortWWN.wwn)) {
+ return (mapIndex);
+ }
+ } else {
+ if (physical != NULL) {
+ tmp = get_slash_devices_from_osDevName(
+ map->entry[mapIndex].ScsiId.OSDeviceName,
+ STANDARD_DEVNAME_HANDLING);
+ if ((tmp != NULL) &&
+ strncmp(physical, tmp, MAXPATHLEN) == 0) {
+ free(physical);
+ return (mapIndex);
+ }
+ }
+ }
+ }
+ if (physical) {
+ free(physical);
+ }
+ return (-1);
+}
+
+
+/*
+ * returns non-zero on failure (aka HBA_STATUS_ERROR_*
+ */
+int
+loadLibrary() {
+ int status = HBA_LoadLibrary();
+ if (status != HBA_STATUS_OK) {
+ fprintf(stderr, MSGSTR(2505,
+ "ERROR: Unable to load HBA API library: "));
+ printStatus(status);
+ fprintf(stderr, "\n");
+ }
+ return (status);
+}
+
+int
+fchba_non_encl_probe() {
+ HBA_HANDLE handle;
+ HBA_ADAPTERATTRIBUTES hbaAttrs;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_FCPTARGETMAPPINGV2 *map;
+ HBA_STATUS status;
+ int count, adapterIndex, portIndex, mapIndex;
+ char name[256];
+ struct path_entry *head = NULL;
+ uint64_t lun = 0;
+ L_inquiry inq;
+ struct scsi_extended_sense sense;
+ HBA_UINT8 scsiStatus;
+ uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense);
+
+ if (loadLibrary()) {
+ return (-1);
+ }
+
+ count = getNumberOfAdapters();
+
+ /* Loop over all HBAs */
+ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
+ if (skip_hba(adapterIndex)) {
+ continue;
+ }
+ status = HBA_GetAdapterName(adapterIndex, (char *)&name);
+ if (status != HBA_STATUS_OK) {
+ /* May have been DR'd */
+ continue;
+ }
+ handle = HBA_OpenAdapter(name);
+ if (handle == 0) {
+ /* May have been DR'd */
+ continue;
+ }
+
+ if (getAdapterAttrs(handle, name, &hbaAttrs)) {
+ /* Should not happen, just skip it */
+ HBA_CloseAdapter(handle);
+ continue;
+ }
+
+
+ /* Loop over all HBA Ports */
+ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
+ portIndex++) {
+ if (getAdapterPortAttrs(handle, name, portIndex,
+ &portAttrs)) {
+ continue;
+ }
+
+ if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
+ continue;
+ }
+
+ /* Loop over all target Mapping entries */
+ for (mapIndex = 0; mapIndex < map->NumberOfEntries;
+ mapIndex ++) {
+ struct path_entry *tmpPath = NULL;
+ int doInquiry = 0;
+ if (!head) {
+ head = (struct path_entry *)calloc(1,
+ sizeof (struct path_entry));
+ tmpPath = head;
+ strncpy(head->path,
+ map->entry[mapIndex].ScsiId.OSDeviceName,
+ sizeof (map->entry[mapIndex].ScsiId.OSDeviceName));
+ (void) memcpy(tmpPath->wwn,
+ map->entry[mapIndex].FcpId.NodeWWN.wwn,
+ sizeof (HBA_UINT8) * 8);
+ doInquiry = 1;
+ } else if (tmpPath = is_duplicate_path(head,
+ map->entry[mapIndex].ScsiId.OSDeviceName)) {
+ if (tmpPath->dtype != 0x1f) {
+ doInquiry = 0;
+ } else {
+ doInquiry = 1;
+ }
+ } else {
+ tmpPath = (struct path_entry *)
+ calloc(1, sizeof (struct path_entry));
+ strncpy(tmpPath->path,
+ map->entry[mapIndex].ScsiId.OSDeviceName,
+ sizeof (map->entry[mapIndex].ScsiId.OSDeviceName));
+ (void) memcpy(tmpPath->wwn,
+ map->entry[mapIndex].FcpId.NodeWWN.wwn,
+ sizeof (HBA_UINT8) * 8);
+ add_path(head, tmpPath);
+ doInquiry = 1;
+ }
+
+ if (doInquiry) {
+ lun = map->entry[mapIndex].FcpId.FcpLun;
+ memset(&inq, 0, sizeof (inq));
+ memset(&sense, 0, sizeof (sense));
+ status = HBA_ScsiInquiryV2(handle,
+ portAttrs.PortWWN,
+ map->entry[mapIndex].FcpId.PortWWN,
+ lun, 0, 0,
+ &inq, &inquirySize,
+ &scsiStatus,
+ &sense, &senseSize);
+ if (status != HBA_STATUS_OK) {
+ inq.inq_dtype = 0x1f;
+ }
+ tmpPath->dtype = inq.inq_dtype;
+ }
+ }
+ }
+ }
+ if (head) {
+ struct path_entry *tmp;
+ printf(MSGSTR(2098, "\nFound Fibre Channel device(s):\n"));
+ for (tmp = head; tmp != NULL; tmp = tmp->next) {
+ printf(" ");
+ printf(MSGSTR(90, "Node WWN:"));
+ printf("%016llx ", wwnConversion(tmp->wwn));
+ fprintf(stdout, MSGSTR(35, "Device Type:"));
+ (void) fflush(stdout);
+
+ if ((tmp->dtype & DTYPE_MASK) < 0x10) {
+ fprintf(stdout, "%s",
+ dtype[tmp->dtype & DTYPE_MASK]);
+ } else if ((tmp->dtype & DTYPE_MASK) < 0x1f) {
+ fprintf(stdout, MSGSTR(2406,
+ "Reserved"));
+ } else {
+ fprintf(stdout, MSGSTR(2407,
+ "Unknown"));
+ }
+
+ printf("\n ");
+ printf(MSGSTR(31, "Logical Path:%s"), tmp->path);
+ printf("\n");
+
+ /* We probably shouldn't be using a g_fc interface here */
+ if (Options & OPTION_P) {
+ char *phys_path =
+ get_slash_devices_from_osDevName(
+ tmp->path,
+ STANDARD_DEVNAME_HANDLING);
+ if (phys_path != NULL) {
+ fprintf(stdout, " ");
+ fprintf(stdout, MSGSTR(5, "Physical Path:"));
+ fprintf(stdout, "\n %s\n", phys_path);
+ free(phys_path);
+ }
+ }
+ }
+ free_path_list(head);
+ }
+ HBA_FreeLibrary();
+ return (0);
+}
+
+
+int
+fchba_inquiry(char **argv)
+{
+ int path_index = 0, found = 0;
+ uint64_t wwn;
+ uint64_t lun = 0;
+ HBA_HANDLE handle;
+ HBA_ADAPTERATTRIBUTES hbaAttrs;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_FCPTARGETMAPPINGV2 *map;
+ HBA_STATUS status;
+ int count, adapterIndex, portIndex, mapIndex;
+ char name[256];
+ L_inquiry inq;
+ struct page80 serial;
+ uint32_t serialSize = sizeof (serial);
+ struct scsi_extended_sense sense;
+ HBA_UINT8 scsiStatus;
+ uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense);
+ boolean_t goodPath = B_FALSE;
+ int matched = 0, wwnCompare = 0;
+ char *tmp, *physical = NULL;
+ int ret = 0;
+
+ if (loadLibrary()) {
+ return (-1);
+ }
+ for (path_index = 0; argv[path_index] != NULL; path_index++) {
+ goodPath = B_FALSE;
+ found = 0;
+
+ if (is_wwn(argv[path_index])) {
+ (void) sscanf(argv[path_index], "%016llx", &wwn);
+ wwnCompare = 1;
+ } else if (!is_path(argv[path_index])) {
+ fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ continue;
+ }
+ if (!wwnCompare) {
+ /* Convert the paths to phsyical paths */
+ physical = get_slash_devices_from_osDevName(argv[path_index],
+ STANDARD_DEVNAME_HANDLING);
+ if (!physical) {
+ fprintf(stderr, MSGSTR(112,
+ "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ continue;
+ }
+ }
+
+ count = getNumberOfAdapters();
+
+ /* Loop over all HBAs */
+ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
+ if (skip_hba(adapterIndex)) {
+ continue;
+ }
+ status = HBA_GetAdapterName(adapterIndex, (char *)&name);
+ if (status != HBA_STATUS_OK) {
+ /* May have been DR'd */
+ continue;
+ }
+ handle = HBA_OpenAdapter(name);
+ if (handle == 0) {
+ /* May have been DR'd */
+ continue;
+ }
+
+ if (getAdapterAttrs(handle, name, &hbaAttrs)) {
+ /* Should never happen */
+ HBA_CloseAdapter(handle);
+ continue;
+ }
+
+
+ /* Loop over all HBA Ports */
+ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
+ portIndex++) {
+ if (getAdapterPortAttrs(handle, name, portIndex,
+ &portAttrs)) {
+ continue;
+ }
+
+ if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
+ continue;
+ }
+
+ for (mapIndex = 0; mapIndex < map->NumberOfEntries;
+ mapIndex ++) {
+ matched = 0;
+ if (wwnCompare) {
+ if (wwn == wwnConversion(
+ map->entry[mapIndex].FcpId.NodeWWN.wwn) ||
+ wwn == wwnConversion(
+ map->entry[mapIndex].FcpId.PortWWN.wwn)) {
+ lun = map->entry[mapIndex].FcpId.FcpLun;
+ matched = 1;
+ }
+ } else {
+ tmp = get_slash_devices_from_osDevName(
+ map->entry[mapIndex].ScsiId.OSDeviceName,
+ STANDARD_DEVNAME_HANDLING);
+ if ((tmp != NULL) && (strncmp(physical, tmp,
+ MAXPATHLEN) == 0)) {
+ lun = map->entry[mapIndex].FcpId.FcpLun;
+ matched = 1;
+ free(tmp);
+ }
+ }
+
+ if (matched) {
+ memset(&inq, 0, sizeof (inq));
+ memset(&sense, 0, sizeof (sense));
+ status = HBA_ScsiInquiryV2(handle,
+ portAttrs.PortWWN,
+ map->entry[mapIndex].FcpId.PortWWN,
+ lun, 0, 0,
+ &inq, &inquirySize,
+ &scsiStatus,
+ &sense, &senseSize);
+ if (status == HBA_STATUS_OK) {
+ goodPath = B_TRUE;
+ /*
+ * Call the inquiry cmd on page 0x80 only if
+ * the vendor supports page 0x80
+ */
+ memset(&serial, 0, sizeof (serial));
+ if ((find_supported_inq_page(handle,
+ portAttrs.PortWWN,
+ map->entry[mapIndex].FcpId.PortWWN,
+ lun, 0x80))) {
+ status = HBA_ScsiInquiryV2(handle,
+ portAttrs.PortWWN,
+ map->entry[mapIndex].FcpId.PortWWN,
+ lun, 1, 0x80,
+ &serial, &serialSize,
+ &scsiStatus,
+ &sense, &senseSize);
+ if (status != HBA_STATUS_OK) {
+ strncpy(
+ (char *)serial.inq_serial,
+ "Unavailable",
+ sizeof (serial.inq_serial));
+ }
+ } else {
+ strncpy((char *)serial.inq_serial,
+ "Unsupported",
+ sizeof (serial.inq_serial));
+ }
+ /*
+ * we are adding serial number information
+ * from 0x80. If length is less than 39,
+ * then we want to increase length to 52 to
+ * reflect the fact that we have serial number
+ * information
+ */
+ if (inq.inq_len < 39) {
+ inq.inq_len = 52;
+ }
+ print_inq_data(argv[path_index],
+ map->entry[mapIndex].ScsiId.OSDeviceName,
+ inq, serial.inq_serial,
+ sizeof (serial.inq_serial));
+ if (! wwnCompare) {
+ found = 1;
+ break;
+ }
+ } else {
+ fprintf(stderr, MSGSTR(2430,
+ "Error: I/O failure communicating with %s "),
+ map->entry[mapIndex].ScsiId.OSDeviceName);
+ printStatus(status);
+ fprintf(stderr, "\n");
+ }
+ }
+ }
+ if (found == 1) {
+ break;
+ }
+ }
+ if (found == 1) {
+ break;
+ }
+ }
+
+ if (physical) {
+ free(physical);
+ }
+
+ if (!goodPath) {
+ fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ }
+ }
+ return (ret);
+}
+
+
+
+int
+fchba_dump_map(char **argv)
+{
+ int path_index = 0;
+ uint64_t wwn;
+ uint64_t lun = 0;
+ HBA_HANDLE handle;
+ HBA_ADAPTERATTRIBUTES hbaAttrs;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_PORTATTRIBUTES discPortAttrs;
+ HBA_FCPTARGETMAPPINGV2 *map;
+ HBA_STATUS status;
+ int count, adapterIndex, portIndex, mapIndex, discIndex;
+ char name[256], *physical, *comp_phys;
+ L_inquiry inq;
+ struct scsi_extended_sense sense;
+ HBA_UINT8 scsiStatus;
+ int matched;
+ int done;
+ uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense);
+ boolean_t goodPath = B_FALSE;
+ int ret = 0;
+ uint32_t responseSize = DEFAULT_LUN_LENGTH;
+ uchar_t raw_luns[DEFAULT_LUN_LENGTH];
+ struct rep_luns_rsp *lun_resp;
+
+
+ if (loadLibrary()) {
+ return (-1);
+ }
+ for (path_index = 0; argv[path_index] != NULL; path_index++) {
+ goodPath = B_FALSE;
+
+ if (is_wwn(argv[path_index])) {
+ (void) sscanf(argv[path_index], "%016llx", &wwn);
+ } else if (!is_path(argv[path_index])) {
+ fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ continue;
+ }
+
+ count = getNumberOfAdapters();
+
+ done = 0;
+ /* Loop over all HBAs */
+ for (adapterIndex = 0; adapterIndex < count && !done;
+ adapterIndex ++) {
+ if (skip_hba(adapterIndex)) {
+ continue;
+ }
+ status = HBA_GetAdapterName(adapterIndex, (char *)&name);
+ if (status != HBA_STATUS_OK) {
+ /* May have been DR'd */
+ continue;
+ }
+ handle = HBA_OpenAdapter(name);
+ if (handle == 0) {
+ /* May have been DR'd */
+ continue;
+ }
+
+ if (getAdapterAttrs(handle, name, &hbaAttrs)) {
+ /* Should never happen */
+ HBA_CloseAdapter(handle);
+ continue;
+ }
+
+
+ /* Loop over all HBA Ports */
+ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts && !done;
+ portIndex++) {
+ if (getAdapterPortAttrs(handle, name, portIndex,
+ &portAttrs)) {
+ continue;
+ }
+
+
+ matched = 0;
+ if (is_wwn(argv[path_index])) {
+ if (wwn == wwnConversion(
+ portAttrs.NodeWWN.wwn) ||
+ wwn == wwnConversion(
+ portAttrs.PortWWN.wwn)) {
+ matched = 1;
+ }
+ } else {
+ if (is_path(argv[path_index]) &&
+ ((physical = get_slash_devices_from_osDevName(
+ argv[path_index],
+ STANDARD_DEVNAME_HANDLING)) != NULL) &&
+ ((comp_phys = get_slash_devices_from_osDevName(
+ portAttrs.OSDeviceName,
+ STANDARD_DEVNAME_HANDLING)) != NULL)) {
+ char *tmp = strstr(physical, ":devctl");
+ if (tmp) {
+ *tmp = '\0';
+ } else {
+ tmp = strstr(physical, ":fc");
+ if (tmp) {
+ *tmp = '\0';
+ }
+ }
+ if (strstr(comp_phys, physical)) {
+ matched = 1;
+ }
+ }
+ if (physical) {
+ free(physical);
+ physical = NULL;
+ }
+ if (comp_phys) {
+ free(comp_phys);
+ comp_phys = NULL;
+ }
+ }
+
+ if (!fetch_mappings(handle, portAttrs.PortWWN, &map)) {
+ mapIndex = match_mappings(argv[path_index], map);
+ if (mapIndex >= 0) {
+ matched = 1;
+ }
+ } else {
+ continue;
+ }
+
+ if (matched) {
+ goodPath = B_TRUE;
+ printf(MSGSTR(2095,
+ "Pos Port_ID Hard_Addr Port WWN"
+ " Node WWN Type\n"));
+ for (discIndex = 0;
+ discIndex < portAttrs.NumberofDiscoveredPorts;
+ discIndex++) {
+ if (getDiscPortAttrs(handle, name, portIndex,
+ discIndex, &discPortAttrs)) {
+ /* Move on to the next target */
+ continue;
+ }
+
+ printf("%-4d %-6x %-6x %016llx %016llx",
+ discIndex,
+ discPortAttrs.PortFcId, 0,
+ wwnConversion(discPortAttrs.PortWWN.wwn),
+ wwnConversion(discPortAttrs.NodeWWN.wwn));
+
+ /*
+ * devices are not all required to respond to
+ * Scsi Inquiry calls sent to LUN 0. We must
+ * fisrt issue a ReportLUN and then send the
+ * SCSI Inquiry call to the first LUN Returned
+ * from the ReportLUN call
+ */
+ memset(&sense, 0, sizeof (sense));
+ status = HBA_ScsiReportLUNsV2(handle,
+ portAttrs.PortWWN,
+ discPortAttrs.PortWWN,
+ (void *)raw_luns, &responseSize, &scsiStatus,
+ (void *)&sense, &senseSize);
+ if (status == HBA_STATUS_OK) {
+ lun_resp =
+ (struct rep_luns_rsp *)
+ (unsigned long)raw_luns;
+ lun = ntohll(
+ wwnConversion(lun_resp->lun[0].val));
+ } else {
+ /*
+ * in case we are unable to retrieve report
+ * LUN data, we will blindly try sending the
+ * INQUIRY to lun 0.
+ */
+ lun = 0;
+ }
+ memset(&sense, 0, sizeof (sense));
+ status = HBA_ScsiInquiryV2(handle,
+ portAttrs.PortWWN,
+ discPortAttrs.PortWWN,
+ lun, 0, 0,
+ &inq, &inquirySize,
+ &scsiStatus,
+ &sense, &senseSize);
+ if (status != HBA_STATUS_OK) {
+ inq.inq_dtype = 0x1f;
+ }
+ print_fabric_dtype_prop(portAttrs.PortWWN.wwn,
+ map->entry[mapIndex].FcpId.PortWWN.wwn,
+ inq.inq_dtype);
+ }
+ /* Now dump this HBA's stats */
+ printf("%-4d %-6x %-6x %016llx %016llx",
+ discIndex,
+ portAttrs.PortFcId, 0,
+ wwnConversion(portAttrs.PortWWN.wwn),
+ wwnConversion(portAttrs.NodeWWN.wwn));
+ print_fabric_dtype_prop(portAttrs.PortWWN.wwn,
+ portAttrs.PortWWN.wwn, 0x1f);
+ done = 1;
+ }
+ }
+ }
+ if (!goodPath) {
+ fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ }
+ }
+ return (ret);
+}
+
+int
+fchba_display_link_status(char **argv)
+{
+ int path_index = 0;
+ uint64_t wwn;
+ HBA_HANDLE handle;
+ HBA_ADAPTERATTRIBUTES hbaAttrs;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_PORTATTRIBUTES discPortAttrs;
+ HBA_FCPTARGETMAPPINGV2 *map;
+ HBA_STATUS status;
+ int count, adapterIndex, portIndex, discIndex;
+ char name[256], *physical, *comp_phys;
+ int matched;
+ struct fc_rls_acc_params rls;
+ uint32_t rls_size = sizeof (rls);
+ boolean_t goodPath = B_FALSE;
+ int ret = 0;
+
+ if (loadLibrary()) {
+ return (-1);
+ }
+ for (path_index = 0; argv[path_index] != NULL; path_index++) {
+ goodPath = B_FALSE;
+
+ if (is_wwn(argv[path_index])) {
+ (void) sscanf(argv[path_index], "%016llx", &wwn);
+ } else if (!is_path(argv[path_index])) {
+ fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ continue;
+ }
+
+ count = getNumberOfAdapters();
+
+ /* Loop over all HBAs */
+ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
+ if (skip_hba(adapterIndex)) {
+ continue;
+ }
+ status = HBA_GetAdapterName(adapterIndex, (char *)&name);
+ if (status != HBA_STATUS_OK) {
+ /* May have been DR'd */
+ continue;
+ }
+ handle = HBA_OpenAdapter(name);
+ if (handle == 0) {
+ /* May have been DR'd */
+ continue;
+ }
+
+ if (getAdapterAttrs(handle, name, &hbaAttrs)) {
+ /* Should never happen */
+ HBA_CloseAdapter(handle);
+ continue;
+ }
+
+
+ /* Loop over all HBA Ports */
+ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
+ portIndex++) {
+ if (getAdapterPortAttrs(handle, name, portIndex,
+ &portAttrs)) {
+ continue;
+ }
+
+ matched = 0;
+ if (is_wwn(argv[path_index])) {
+ if (wwn == wwnConversion(
+ portAttrs.NodeWWN.wwn) ||
+ wwn == wwnConversion(
+ portAttrs.PortWWN.wwn)) {
+ matched = 1;
+ }
+ } else {
+ if (is_path(argv[path_index]) &&
+ ((physical = get_slash_devices_from_osDevName(
+ argv[path_index],
+ STANDARD_DEVNAME_HANDLING)) != NULL) &&
+ ((comp_phys = get_slash_devices_from_osDevName(
+ portAttrs.OSDeviceName,
+ STANDARD_DEVNAME_HANDLING)) != NULL)) {
+ char *tmp = strstr(physical, ":devctl");
+ if (tmp) {
+ *tmp = '\0';
+ } else {
+ tmp = strstr(physical, ":fc");
+ if (tmp) {
+ *tmp = '\0';
+ }
+ }
+ if (strstr(comp_phys, physical)) {
+ matched = 1;
+ }
+ }
+ if (physical) {
+ free(physical);
+ physical = NULL;
+ }
+ if (comp_phys) {
+ free(comp_phys);
+ comp_phys = NULL;
+ }
+ }
+
+ if (!matched) {
+ if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
+ continue;
+ }
+ }
+
+ if (matched || match_mappings(argv[path_index], map) >= 0) {
+ goodPath = B_TRUE;
+ fprintf(stdout,
+ MSGSTR(2007, "\nLink Error Status "
+ "information for loop:%s\n"), argv[path_index]);
+ fprintf(stdout, MSGSTR(2008, "al_pa lnk fail "
+ " sync loss signal loss sequence err"
+ " invalid word CRC\n"));
+
+ for (discIndex = 0;
+ discIndex < portAttrs.NumberofDiscoveredPorts;
+ discIndex++) {
+
+
+ if (getDiscPortAttrs(handle, name, portIndex,
+ discIndex, &discPortAttrs)) {
+ continue;
+ }
+
+ status = HBA_SendRLS(handle, portAttrs.PortWWN,
+ discPortAttrs.PortWWN,
+ &rls, &rls_size);
+ if (status != HBA_STATUS_OK) {
+ memset(&rls, 0xff, sizeof (rls));
+ }
+
+ if ((rls.rls_link_fail == 0xffffffff) &&
+ (rls.rls_sync_loss == 0xffffffff) &&
+ (rls.rls_sig_loss == 0xffffffff) &&
+ (rls.rls_prim_seq_err == 0xffffffff) &&
+ (rls.rls_invalid_word == 0xffffffff) &&
+ (rls.rls_invalid_crc == 0xffffffff)) {
+ fprintf(stdout,
+ "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n",
+ discPortAttrs.PortFcId,
+ rls.rls_link_fail,
+ rls.rls_sync_loss,
+ rls.rls_sig_loss,
+ rls.rls_prim_seq_err,
+ rls.rls_invalid_word,
+ rls.rls_invalid_crc);
+ } else {
+ fprintf(stdout,
+ "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n",
+ discPortAttrs.PortFcId,
+ rls.rls_link_fail,
+ rls.rls_sync_loss,
+ rls.rls_sig_loss,
+ rls.rls_prim_seq_err,
+ rls.rls_invalid_word,
+ rls.rls_invalid_crc);
+ }
+
+
+ }
+ /* Now dump this HBA's stats */
+ status = HBA_SendRLS(handle, portAttrs.PortWWN,
+ portAttrs.PortWWN,
+ &rls, &rls_size);
+ if (status != HBA_STATUS_OK) {
+ memset(&rls, 0xff, sizeof (rls));
+ }
+
+ if ((rls.rls_link_fail == 0xffffffff) &&
+ (rls.rls_sync_loss == 0xffffffff) &&
+ (rls.rls_sig_loss == 0xffffffff) &&
+ (rls.rls_prim_seq_err == 0xffffffff) &&
+ (rls.rls_invalid_word == 0xffffffff) &&
+ (rls.rls_invalid_crc == 0xffffffff)) {
+ fprintf(stdout,
+ "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n",
+ portAttrs.PortFcId,
+ rls.rls_link_fail,
+ rls.rls_sync_loss,
+ rls.rls_sig_loss,
+ rls.rls_prim_seq_err,
+ rls.rls_invalid_word,
+ rls.rls_invalid_crc);
+ } else {
+ fprintf(stdout,
+ "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n",
+ portAttrs.PortFcId,
+ rls.rls_link_fail,
+ rls.rls_sync_loss,
+ rls.rls_sig_loss,
+ rls.rls_prim_seq_err,
+ rls.rls_invalid_word,
+ rls.rls_invalid_crc);
+ }
+ }
+ }
+ }
+ if (!goodPath) {
+ fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ }
+ }
+ (void) fprintf(stdout,
+ MSGSTR(2009, "NOTE: These LESB counts are not"
+ " cleared by a reset, only power cycles.\n"
+ "These counts must be compared"
+ " to previously read counts.\n"));
+ return (ret);
+}
+
+typedef struct _PathInformation {
+ char pathClass[MAXPATHLEN];
+ char pathState[MAXPATHLEN];
+ int32_t pathInfoState;
+ int32_t pathInfoExternalState;
+} PathInformation;
+
+struct lun_tracking {
+ HBA_FCPSCSIENTRYV2 map;
+ HBA_WWN hba_pwwn;
+ char hba_path[MAXPATHLEN];
+ PathInformation info;
+
+ /* Points to another lun_tracking instance with the same map->LUID */
+ struct lun_tracking *next_path;
+
+ /* Points to next lun_tracking with a different map->LUID */
+ struct lun_tracking *next_lun;
+};
+
+
+static const char VHCI_COMPONENT[] = "scsi_vhci";
+static void
+scsi_vhci_details(struct lun_tracking *lun)
+{
+ HBA_FCPSCSIENTRYV2 entry = lun->map;
+ int retval = 0;
+ int pathcnt, i, count, found = 0;
+ char temppath[MAXPATHLEN];
+ char buf[MAXPATHLEN];
+ char *path_state[5];
+
+ char *phys_path = get_slash_devices_from_osDevName(
+ entry.ScsiId.OSDeviceName,
+ STANDARD_DEVNAME_HANDLING);
+ char *devPath = NULL;
+ char *trailingCruft = NULL;
+ char devaddr[MAXPATHLEN];
+ sv_iocdata_t ioc;
+ int prop_buf_size = SV_PROP_MAX_BUF_SIZE;
+ char *path_class_val = NULL;
+ char client_path[MAXPATHLEN];
+ char phci_path[MAXPATHLEN];
+
+ /* Only proceed if we are an mpxio path */
+ if (phys_path == NULL || strstr(phys_path, VHCI_COMPONENT) == NULL) {
+ return;
+ }
+
+ path_state[0] = MSGSTR(2400, "INIT");
+ path_state[1] = MSGSTR(2401, "ONLINE");
+ path_state[2] = MSGSTR(2402, "STANDBY");
+ path_state[3] = MSGSTR(2403, "FAULT");
+ path_state[4] = MSGSTR(2404, "OFFLINE");
+
+ sprintf(devaddr, "%016llx,%x", wwnConversion(
+ entry.FcpId.PortWWN.wwn),
+ entry.ScsiId.ScsiOSLun);
+
+ /* First get the controller path */
+ sprintf(temppath, "/dev/cfg/c%d", entry.ScsiId.ScsiBusNumber);
+ if ((count = readlink(temppath, buf, sizeof (buf)))) {
+ buf[count] = '\0';
+ /* Now skip over the leading "../.." */
+ devPath = strstr(buf, "/devices/");
+ if (devPath == NULL) {
+ strcpy(lun->info.pathClass, "Unavailable");
+ strcpy(lun->info.pathState, "Unavailable");
+ free(phys_path);
+ return;
+ }
+
+ /* Now chop off the trailing ":xxx" portion if present */
+ trailingCruft = strrchr(buf, ':');
+ if (trailingCruft) {
+ trailingCruft[0] = '\0';
+ }
+ } else {
+ strcpy(lun->info.pathClass, "Unavailable");
+ strcpy(lun->info.pathState, "Unavailable");
+ free(phys_path);
+ return;
+ }
+
+ ioc.client = client_path;
+ ioc.phci = phci_path;
+
+ retval = get_scsi_vhci_pathinfo(phys_path, &ioc, &pathcnt);
+ if (retval != 0) {
+ print_errString(retval, NULL);
+ exit(-1);
+ }
+
+ for (i = 0; i < pathcnt; i++) {
+ nvlist_t *nvl;
+ if (strstr(devPath, ioc.ret_buf[i].device.ret_phci)) {
+ /* This could break someday if MPxIO changes devaddr */
+ if (strstr(ioc.ret_buf[i].ret_addr, devaddr)) {
+ retval = nvlist_unpack(ioc.ret_buf[i].ret_prop.buf,
+ prop_buf_size, &nvl, 0);
+ if (retval != 0) {
+ strcpy(lun->info.pathClass,
+ "UNKNOWN PROB");
+ } else {
+ strcpy(lun->info.pathState,
+ path_state[ioc.ret_buf[i].ret_state]);
+ lun->info.pathInfoState = ioc.ret_buf[i].ret_state;
+ lun->info.pathInfoExternalState =
+ ioc.ret_buf[i].ret_ext_state;
+ if (nvlist_lookup_string(nvl, "path-class",
+ &path_class_val) == 0) {
+ strcpy(lun->info.pathClass, path_class_val);
+ } else {
+ strcpy(lun->info.pathClass, "UNKNOWN");
+ }
+ }
+ nvlist_free(nvl);
+ found++;
+ break;
+ }
+ }
+
+ }
+
+ if (!found) {
+ strcpy(lun->info.pathClass, "Unavailable");
+ strcpy(lun->info.pathState, "Unavailable");
+ }
+ free(phys_path);
+
+ /* free everything we alloced */
+ for (i = 0; i < ioc.buf_elem; i++) {
+ free(ioc.ret_buf[i].ret_prop.buf);
+ free(ioc.ret_buf[i].ret_prop.ret_buf_size);
+ }
+ free(ioc.ret_buf);
+
+}
+
+/* Utility routine to add new entries to the list (ignores dups) */
+static void
+add_lun_path(struct lun_tracking *head, HBA_FCPSCSIENTRYV2 *map,
+ HBA_WWN pwwn, char *path)
+{
+ struct lun_tracking *tmp = NULL, *cmp = NULL;
+
+ for (tmp = head; tmp != NULL; tmp = tmp->next_lun) {
+ if (memcmp(&tmp->map.LUID, &map->LUID,
+ sizeof (HBA_LUID)) == 0) {
+
+ /* Ensure this isn't a duplicate */
+ for (cmp = tmp; cmp->next_path != NULL;
+ cmp = cmp->next_path) {
+ if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) {
+ return;
+ }
+ }
+ if (memcmp(&cmp->map, map, sizeof (cmp->map)) == 0) {
+ return;
+ }
+
+ /* We have a new entry to add */
+ cmp->next_path = (struct lun_tracking *)calloc(1,
+ sizeof (struct lun_tracking));
+ cmp = cmp->next_path;
+ (void) memcpy(&cmp->map, map,
+ sizeof (cmp->map));
+ (void) memcpy(&cmp->hba_pwwn, &pwwn,
+ sizeof (cmp->hba_pwwn));
+ (void) snprintf(cmp->hba_path, MAXPATHLEN,
+ path);
+ scsi_vhci_details(cmp);
+ return;
+ }
+ }
+ /* Append a new LUN at the end of the list */
+ for (tmp = head; tmp->next_lun != NULL; tmp = tmp->next_lun) {}
+ tmp->next_lun = (struct lun_tracking *)calloc(1,
+ sizeof (struct lun_tracking));
+ tmp = tmp->next_lun;
+ (void) memcpy(&tmp->map, map,
+ sizeof (tmp->map));
+ (void) memcpy(&tmp->hba_pwwn, &pwwn,
+ sizeof (tmp->hba_pwwn));
+ (void) snprintf(tmp->hba_path, MAXPATHLEN,
+ path);
+ scsi_vhci_details(tmp);
+}
+
+/*ARGSUSED*/
+int
+fchba_display_config(char **argv, int option_t_input, int argc)
+{
+ int path_index = 0;
+ uint64_t wwn;
+ uint64_t lun = 0;
+ HBA_HANDLE handle;
+ HBA_ADAPTERATTRIBUTES hbaAttrs;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_FCPTARGETMAPPINGV2 *map;
+ HBA_STATUS status;
+ int count, adapterIndex, portIndex;
+ char name[256];
+ L_inquiry inq;
+ struct scsi_extended_sense sense;
+ struct page80 serial;
+ HBA_UINT8 scsiStatus;
+ uint32_t inquirySize = sizeof (inq), senseSize = sizeof (sense);
+ uint32_t serialSize = sizeof (serial);
+ struct mode_page *pg_hdr;
+ uchar_t *pg_buf;
+ float lunMbytes;
+ struct capacity_data_struct cap_data;
+ uint32_t cap_data_size = sizeof (cap_data);
+ struct mode_header_g1 *mode_header_ptr;
+ int offset;
+ char *phys_path = NULL;
+ int mpxio = 0;
+ int wwnCompare = 0;
+ char *physical = NULL;
+ struct lun_tracking *head = NULL;
+ boolean_t goodPath = B_FALSE;
+ int ret = 0;
+
+
+
+ if ((status = loadLibrary())) {
+ return (-1);
+ }
+ for (path_index = 0; argv[path_index] != NULL; path_index++) {
+ goodPath = B_FALSE;
+
+ if (is_wwn(argv[path_index])) {
+ (void) sscanf(argv[path_index], "%016llx", &wwn);
+ wwnCompare = 1;
+ } else if (!is_path(argv[path_index])) {
+ fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ continue;
+ }
+ if (!wwnCompare) {
+ /* Convert the paths to phsyical paths */
+ physical = get_slash_devices_from_osDevName(argv[path_index],
+ STANDARD_DEVNAME_HANDLING);
+ if (!physical) {
+ fprintf(stderr, MSGSTR(112,
+ "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ continue;
+ }
+ }
+
+ count = getNumberOfAdapters();
+
+
+ /*
+ * We have to loop twice to ensure we don't miss any
+ * extra paths for other targets in a multi-target device
+ */
+
+ /* First check WWN/path comparisons */
+ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
+ if (skip_hba(adapterIndex)) {
+ continue;
+ }
+ status = HBA_GetAdapterName(adapterIndex, (char *)&name);
+ if (status != HBA_STATUS_OK) {
+ /* May have been DR'd */
+ continue;
+ }
+ handle = HBA_OpenAdapter(name);
+ if (handle == 0) {
+ /* May have been DR'd */
+ continue;
+ }
+ if (getAdapterAttrs(handle, name, &hbaAttrs)) {
+ /* Should never happen */
+ HBA_CloseAdapter(handle);
+ continue;
+ }
+
+ /* Loop over all HBA Ports */
+ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
+ portIndex++) {
+ int matched = 0;
+ int mapIndex;
+ char *tmp;
+ if (getAdapterPortAttrs(handle, name, portIndex,
+ &portAttrs)) {
+ continue;
+ }
+
+ if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
+ continue;
+ }
+
+
+
+ for (mapIndex = 0; mapIndex < map->NumberOfEntries;
+ mapIndex ++) {
+ matched = 0;
+ if (wwnCompare) {
+ if (wwn == wwnConversion(
+ map->entry[mapIndex].FcpId.NodeWWN.wwn) ||
+ wwn == wwnConversion(
+ map->entry[mapIndex].FcpId.PortWWN.wwn)) {
+ matched = 1;
+ }
+ } else {
+ tmp = get_slash_devices_from_osDevName(
+ map->entry[mapIndex].ScsiId.OSDeviceName,
+ STANDARD_DEVNAME_HANDLING);
+ if ((tmp != NULL) && (strncmp(physical, tmp,
+ MAXPATHLEN) == 0)) {
+ matched = 1;
+ free(tmp);
+ }
+ }
+ if (matched && head == NULL) {
+ goodPath = B_TRUE;
+ head = (struct lun_tracking *)calloc(1,
+ sizeof (struct lun_tracking));
+ (void) memcpy(&head->map, &map->entry[mapIndex],
+ sizeof (head->map));
+ (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN,
+ sizeof (head->hba_pwwn));
+ (void) snprintf(head->hba_path, MAXPATHLEN,
+ portAttrs.OSDeviceName);
+ scsi_vhci_details(head);
+ } else if (matched) {
+ goodPath = B_TRUE;
+ add_lun_path(head, &map->entry[mapIndex],
+ portAttrs.PortWWN, portAttrs.OSDeviceName);
+ }
+ }
+ }
+ }
+
+ if (physical) {
+ free(physical);
+ }
+
+ /* Now do it again and look for matching LUIDs (aka GUIDs) */
+ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
+ if (skip_hba(adapterIndex)) {
+ continue;
+ }
+ status = HBA_GetAdapterName(adapterIndex, (char *)&name);
+ if (status != HBA_STATUS_OK) {
+ /* May have been DR'd */
+ continue;
+ }
+ handle = HBA_OpenAdapter(name);
+ if (handle == 0) {
+ /* May have been DR'd */
+ continue;
+ }
+
+ if (getAdapterAttrs(handle, name, &hbaAttrs)) {
+ /* Should never happen */
+ HBA_CloseAdapter(handle);
+ continue;
+ }
+
+
+ /* Loop over all HBA Ports */
+ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
+ portIndex++) {
+ int matched = 0;
+ int mapIndex;
+ if (getAdapterPortAttrs(handle, name, portIndex,
+ &portAttrs)) {
+ continue;
+ }
+
+ if (fetch_mappings(handle, portAttrs.PortWWN, &map)) {
+ continue;
+ }
+
+
+ for (mapIndex = 0; mapIndex < map->NumberOfEntries;
+ mapIndex ++) {
+ struct lun_tracking *outer;
+ matched = 0;
+ for (outer = head; outer != NULL;
+ outer = outer->next_lun) {
+ struct lun_tracking *inner;
+ for (inner = outer; inner != NULL;
+ inner = inner->next_path) {
+ if (memcmp(&inner->map.LUID,
+ &map->entry[mapIndex].LUID,
+ sizeof (HBA_LUID)) == 0) {
+ matched = 1;
+ break;
+ }
+ }
+ if (matched) {
+ break;
+ }
+ }
+ if (matched && head == NULL) {
+ goodPath = B_TRUE;
+ head = (struct lun_tracking *)calloc(1,
+ sizeof (struct lun_tracking));
+ (void) memcpy(&head->map, &map->entry[mapIndex],
+ sizeof (head->map));
+ (void) memcpy(&head->hba_pwwn, &portAttrs.PortWWN,
+ sizeof (head->hba_pwwn));
+ (void) snprintf(head->hba_path, MAXPATHLEN,
+ portAttrs.OSDeviceName);
+ scsi_vhci_details(head);
+ } else if (matched) {
+ goodPath = B_TRUE;
+ add_lun_path(head, &map->entry[mapIndex],
+ portAttrs.PortWWN, portAttrs.OSDeviceName);
+ }
+ }
+ }
+ }
+ if (!goodPath) {
+ fprintf(stderr, MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ fprintf(stderr, "\n");
+ ret = -1;
+ /* Just bomb out instead of going on */
+ return (ret);
+ }
+ }
+
+ /* Now display all the LUNs that we found that matched */
+ {
+ struct lun_tracking *first_time;
+ struct lun_tracking *tmp_path;
+ for (first_time = head; first_time != NULL;
+ first_time = first_time->next_lun) {
+ struct lun_tracking *path;
+ phys_path = get_slash_devices_from_osDevName(
+ first_time->map.ScsiId.OSDeviceName,
+ STANDARD_DEVNAME_HANDLING);
+ /* Change behavior if this is an MPxIO device */
+ if (phys_path != NULL) {
+ if (strstr(phys_path, VHCI_COMPONENT) != NULL) {
+ mpxio = 1;
+ }
+ }
+
+ for (tmp_path = first_time; tmp_path != NULL;
+ tmp_path = tmp_path->next_path) {
+ if (mpxio && (strncmp(tmp_path->info.pathState,
+ "ONLINE", strlen(tmp_path->info.pathState)))) {
+ /* continue to next online path */
+ continue;
+ }
+ status = HBA_OpenAdapterByWWN(&handle,
+ tmp_path->hba_pwwn);
+ if (status != HBA_STATUS_OK) {
+ fprintf(stderr, MSGSTR(2431,
+ "Error: Failed to get handle for %s "),
+ tmp_path->hba_path);
+ printStatus(status);
+ fprintf(stderr, "\n");
+ /* continue to next path */
+ continue;
+ }
+
+ lun = tmp_path->map.FcpId.FcpLun;
+ memset(&inq, 0, sizeof (inq));
+ memset(&sense, 0, sizeof (sense));
+
+ status = HBA_ScsiInquiryV2(handle,
+ tmp_path->hba_pwwn,
+ tmp_path->map.FcpId.PortWWN,
+ lun, 0, 0,
+ &inq, &inquirySize,
+ &scsiStatus,
+ &sense, &senseSize);
+
+ if (status == HBA_STATUS_OK) {
+ break;
+ }
+ HBA_CloseAdapter(handle);
+ }
+
+ if (tmp_path == NULL) {
+ fprintf(stderr, MSGSTR(2430,
+ "Error: I/O failure communicating with %s "),
+ first_time->map.ScsiId.OSDeviceName);
+ printStatus(status);
+ fprintf(stderr, "\n");
+ continue;
+ }
+
+ switch ((inq.inq_dtype & DTYPE_MASK)) {
+ case DTYPE_DIRECT:
+ fprintf(stdout, MSGSTR(121,
+ "DEVICE PROPERTIES for disk: %s\n"),
+ first_time->map.ScsiId.OSDeviceName);
+ break;
+ case DTYPE_SEQUENTIAL: /* Tape */
+ fprintf(stdout, MSGSTR(2249,
+ "DEVICE PROPERTIES for tape: %s\n"),
+ first_time->map.ScsiId.OSDeviceName);
+ break;
+ default:
+ fprintf(stdout, MSGSTR(2250,
+ "DEVICE PROPERTIES for: %s\n"),
+ first_time->map.ScsiId.OSDeviceName);
+ break;
+ }
+ fprintf(stdout, " ");
+ fprintf(stdout, MSGSTR(3, "Vendor:"));
+ fprintf(stdout, "\t\t");
+ print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
+ fprintf(stdout, MSGSTR(2115, "\n Product ID:\t\t"));
+ print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
+
+ fprintf(stdout, "\n ");
+ fprintf(stdout, MSGSTR(2119, "Revision:"));
+ fprintf(stdout, "\t\t");
+ print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
+
+ fprintf(stdout, "\n ");
+ fprintf(stdout, MSGSTR(17, "Serial Num:"));
+ fprintf(stdout, "\t\t");
+ (void) fflush(stdout);
+ /*
+ * Call the inquiry cmd on page 0x80 only if the vendor
+ * supports page 0x80.
+ */
+ if ((find_supported_inq_page(handle, first_time->hba_pwwn,
+ first_time->map.FcpId.PortWWN, lun, 0x80))) {
+ memset(&serial, 0, sizeof (serial));
+ status = HBA_ScsiInquiryV2(handle,
+ first_time->hba_pwwn,
+ first_time->map.FcpId.PortWWN,
+ lun, 1, 0x80,
+ &serial, &serialSize,
+ &scsiStatus,
+ &sense, &senseSize);
+ if (status == HBA_STATUS_OK) {
+ print_chars(serial.inq_serial,
+ sizeof (serial.inq_serial), 0);
+ } else {
+ fprintf(stdout, MSGSTR(2506, "Unsupported"));
+ }
+ } else {
+ fprintf(stdout, MSGSTR(2506, "Unsupported"));
+ }
+ HBA_CloseAdapter(handle);
+ if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) {
+ /* Read capacity wont work on standby paths, so try till OK */
+ for (tmp_path = first_time; tmp_path != NULL;
+ tmp_path = tmp_path->next_path) {
+ if (mpxio && (strncmp(tmp_path->info.pathState,
+ "ONLINE", strlen(tmp_path->info.pathState)))) {
+ /* continue to next online path */
+ continue;
+ }
+ status = HBA_OpenAdapterByWWN(&handle,
+ tmp_path->hba_pwwn);
+ if (status != HBA_STATUS_OK) {
+ /* continue to next path */
+ continue;
+ }
+
+ status = HBA_ScsiReadCapacityV2(handle,
+ tmp_path->hba_pwwn,
+ tmp_path->map.FcpId.PortWWN,
+ tmp_path->map.FcpId.FcpLun,
+ &cap_data, &cap_data_size,
+ &scsiStatus,
+ &sense, &senseSize);
+ if (status == HBA_STATUS_OK) {
+ break;
+ } else if (status == HBA_STATUS_SCSI_CHECK_CONDITION &&
+ sense.es_key == KEY_UNIT_ATTENTION) {
+ /*
+ * retry for check-condition state when unit attention
+ * condition has been established
+ */
+ status = HBA_ScsiReadCapacityV2(handle,
+ tmp_path->hba_pwwn,
+ tmp_path->map.FcpId.PortWWN,
+ tmp_path->map.FcpId.FcpLun,
+ &cap_data, &cap_data_size,
+ &scsiStatus,
+ &sense, &senseSize);
+ if (status == HBA_STATUS_OK) {
+ break;
+ }
+ }
+ HBA_CloseAdapter(handle);
+ }
+ }
+ if (handle != HBA_HANDLE_INVALID) {
+ HBA_CloseAdapter(handle);
+ }
+ if (status != HBA_STATUS_OK) {
+ /* Make sure we don't display garbage */
+ cap_data.block_size = 0;
+ cap_data.last_block_addr = 0;
+ }
+
+ if (cap_data.block_size > 0 &&
+ cap_data.last_block_addr > 0) {
+ lunMbytes = ntohl(cap_data.last_block_addr) + 1;
+ lunMbytes *= ntohl(cap_data.block_size);
+ lunMbytes /= (float)(1024*1024);
+ fprintf(stdout, "\n ");
+ fprintf(stdout, MSGSTR(60,
+ "Unformatted capacity:\t%6.3f MBytes"), lunMbytes);
+ }
+ fprintf(stdout, "\n");
+
+ /*
+ * get mode page information for FC device.
+ * do not do mode sense if this is a tape device.
+ * mode sense will rewind the tape
+ */
+ if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_SEQUENTIAL) {
+ if (get_mode_page(first_time->map.ScsiId.OSDeviceName,
+ &pg_buf) == 0) {
+ mode_header_ptr = (struct mode_header_g1 *)
+ (void *)pg_buf;
+ offset = sizeof (struct mode_header_g1) +
+ ntohs(mode_header_ptr->bdesc_length);
+ pg_hdr = (struct mode_page *)&pg_buf[offset];
+
+ while (offset < (ntohs(mode_header_ptr->length) +
+ sizeof (mode_header_ptr->length))) {
+ if (pg_hdr->code == MODEPAGE_CACHING) {
+ struct mode_caching *pg8_buf;
+ pg8_buf = (struct mode_caching *)
+ (void *)pg_hdr;
+ if (pg8_buf->wce) {
+ fprintf(stdout, MSGSTR(2122,
+ " Write Cache:\t\t"
+ "Enabled\n"));
+ }
+ if (pg8_buf->rcd == 0) {
+ fprintf(stdout, MSGSTR(2123,
+ " Read Cache:\t\t"
+ "Enabled\n"));
+ fprintf(stdout, MSGSTR(2509,
+ " Minimum prefetch:\t0x%x\n"
+ " Maximum prefetch:\t0x%x\n"),
+ pg8_buf->min_prefetch,
+ pg8_buf->max_prefetch);
+ }
+ break;
+ }
+ offset += pg_hdr->length +
+ sizeof (struct mode_page);
+ pg_hdr = (struct mode_page *)&pg_buf[offset];
+ }
+ }
+ }
+
+ fprintf(stdout, " %s\t\t", MSGSTR(35, "Device Type:"));
+ if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
+ fprintf(stdout, "%s\n",
+ dtype[inq.inq_dtype & DTYPE_MASK]);
+ } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
+ fprintf(stdout, MSGSTR(2432, "Reserved"));
+ } else {
+ /* dtype of 0x1f is returned */
+ fprintf(stdout, MSGSTR(2433, "Unknown"));
+ }
+
+ fprintf(stdout, MSGSTR(2128, " Path(s):\n"));
+ fprintf(stdout, "\n");
+ fprintf(stdout, " %s\n",
+ first_time->map.ScsiId.OSDeviceName);
+ if (phys_path != NULL) {
+ fprintf(stdout, " %s\n", phys_path);
+ }
+
+ /* Now display all paths to this LUN */
+ for (path = first_time; path != NULL;
+ path = path->next_path) {
+ /* Display the controller information */
+ fprintf(stdout, MSGSTR(2303, " Controller \t%s\n"),
+ path->hba_path);
+
+ fprintf(stdout, MSGSTR(2507,
+ " Device Address\t\t%016llx,%x\n"),
+ wwnConversion(
+ path->map.FcpId.PortWWN.wwn),
+ path->map.ScsiId.ScsiOSLun);
+
+ fprintf(stdout, MSGSTR(2508,
+ " Host controller port WWN\t%016llx\n"),
+ wwnConversion(path->hba_pwwn.wwn));
+
+ if (mpxio) {
+ fprintf(stdout, MSGSTR(2305,
+ " Class\t\t\t%s\n"), path->info.pathClass);
+ fprintf(stdout, MSGSTR(2306,
+ " State\t\t\t%s\n"), path->info.pathState);
+ }
+ if (phys_path != NULL) {
+ free(phys_path);
+ phys_path = NULL;
+ }
+ }
+ printf("\n");
+ }
+ }
+ return (ret);
+}
+
+/*
+ * handle expert-mode hotplug commands
+ *
+ * return 0 iff all is okay
+ */
+int
+fchba_hotplug_e(int todo, char **argv, int verbose_flag, int force_flag)
+{
+char *path_phys = NULL;
+int exit_code;
+devctl_hdl_t dcp;
+
+ if (todo != DEV_ONLINE &&
+ todo != DEV_OFFLINE) {
+ fprintf(stderr, "%s\n", strerror(ENOTSUP));
+ return (-1);
+ }
+
+ /* Convert the paths to phsyical paths */
+ path_phys = get_slash_devices_from_osDevName(argv[0],
+ NOT_IGNORE_DANGLING_LINK);
+ if (!path_phys) {
+ fprintf(stderr, MSGSTR(112,
+ "Error: Invalid pathname (%s)"),
+ argv[0]);
+ fprintf(stderr, "\n");
+ return (-1);
+ }
+ if (verbose_flag) {
+ (void) fprintf(stdout,
+ MSGSTR(5516,
+ "phys path = \"%s\"\n"),
+ path_phys);
+ }
+ /* acquire rights to hack on device */
+ if ((dcp = devctl_device_acquire(path_phys,
+ force_flag ? 0 : DC_EXCL)) == NULL) {
+
+ (void) fprintf(stderr, MSGSTR(5517,
+ "Error: can't acquire \"%s\": %s\n"),
+ path_phys, strerror(errno));
+ return (1);
+ }
+
+ switch (todo) {
+ case DEV_ONLINE:
+ exit_code = devctl_device_online(dcp);
+ break;
+ case DEV_OFFLINE:
+ exit_code = devctl_device_offline(dcp);
+ break;
+ }
+
+ if (exit_code != 0) {
+ perror(MSGSTR(5518, "devctl"));
+ }
+
+ /* all done now -- release device */
+ devctl_release(dcp);
+
+ if (path_phys) {
+ free(path_phys);
+ }
+
+ return (exit_code);
+}
+
+/*
+ * Returns non zero if we should use FC-HBA.
+ * For x86, luxadm uses FC-HBA.
+ */
+int
+use_fchba()
+{
+
+#ifdef __x86
+ return (1);
+#else
+ return (0);
+#endif
+
+}
+
+/*
+ * Returns non-zero if we should skip the HBA at index "i"
+ */
+int
+skip_hba(int i) {
+ HBA_LIBRARYATTRIBUTES lib_attrs;
+ (void) HBA_GetVendorLibraryAttributes(i, &lib_attrs);
+ if (strncmp(lib_attrs.VName, VSL_NAME,
+ sizeof (lib_attrs.VName)) == 0) {
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Function to determine if the given page is supported by vendor.
+ */
+int
+find_supported_inq_page(HBA_HANDLE handle, HBA_WWN hwwn, HBA_WWN pwwn,
+ uint64_t lun, int page_num)
+{
+ struct scsi_extended_sense sense;
+ L_inquiry00 inq00;
+ uchar_t *data;
+ HBA_STATUS status = HBA_STATUS_ERROR;
+ int index;
+ HBA_UINT8 scsiStatus;
+ uint32_t inqSize = sizeof (inq00);
+ uint32_t senseSize = sizeof (sense);
+
+ status = HBA_ScsiInquiryV2(handle, hwwn, pwwn, lun, 1, 0x00,
+ &inq00, &inqSize, &scsiStatus, &sense, &senseSize);
+
+ if (status == HBA_STATUS_OK) {
+ data = (uchar_t *)&inq00;
+ for (index = 4; (index <= inq00.len+3)&&
+ (data[index] <= page_num); index ++) {
+ if (data[index] == page_num) {
+ return (1);
+ }
+ }
+ }
+ return (0);
+}
diff --git a/usr/src/cmd/luxadm/g_adm.c b/usr/src/cmd/luxadm/g_adm.c
new file mode 100644
index 0000000000..b6377b00da
--- /dev/null
+++ b/usr/src/cmd/luxadm/g_adm.c
@@ -0,0 +1,6673 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#define LUX_SF_INST_SHIFT4MINOR 6
+#define LUX_SF_MINOR2INST(x) (x >> LUX_SF_INST_SHIFT4MINOR)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <kstat.h>
+#include <sys/mkdev.h>
+#include <locale.h>
+#include <nl_types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <strings.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <termio.h> /* For password */
+#include <signal.h>
+#include <sys/scsi/scsi.h>
+#include <sys/scsi/generic/commands.h>
+#include <l_common.h>
+#include <l_error.h>
+#include <stgcom.h>
+#include <a_state.h>
+#include <devid.h>
+#include <g_state.h>
+#include "common.h"
+
+extern char *dtype[];
+extern char *whoami;
+extern int Options;
+extern const int OPTION_A;
+extern const int OPTION_B;
+extern const int OPTION_C;
+extern const int OPTION_D;
+extern const int OPTION_E;
+extern const int OPTION_F;
+extern const int OPTION_L;
+extern const int OPTION_P;
+extern const int OPTION_R;
+extern const int OPTION_T;
+extern const int OPTION_V;
+extern const int OPTION_Z;
+extern const int OPTION_Y;
+extern const int OPTION_CAPF;
+extern const int PVERBOSE;
+extern const int SAVE;
+extern const int EXPERT;
+
+static struct termios termios;
+static int termio_fd;
+static void pho_display_config(char *);
+static void dpm_display_config(char *);
+static void n_rem_list_entry(uchar_t, struct gfc_map *,
+ WWN_list **);
+static void n_rem_list_entry_fabric(int, struct gfc_map *,
+ WWN_list **);
+static void n_rem_wwn_entry(uchar_t *, WWN_list **);
+static void display_disk_info(L_inquiry, L_disk_state,
+ Path_struct *, struct mode_page *, int, char *, int);
+static void display_lun_info(L_disk_state, Path_struct *,
+ struct mode_page *, int, WWN_list *, char *);
+static void display_fc_disk(struct path_struct *, char *, gfc_map_t *,
+ L_inquiry, int);
+static void adm_display_err(char *, int);
+static void temperature_messages(struct l_state_struct *, int);
+static void ctlr_messages(struct l_state_struct *, int, int);
+static void fan_messages(struct l_state_struct *, int, int);
+static void ps_messages(struct l_state_struct *, int, int);
+static void abnormal_condition_display(struct l_state_struct *);
+static void loop_messages(struct l_state_struct *, int, int);
+static void revision_msg(struct l_state_struct *, int);
+static void mb_messages(struct l_state_struct *, int, int);
+static void back_plane_messages(struct l_state_struct *, int, int);
+static void dpm_SSC100_messages(struct l_state_struct *, int, int);
+static void mb_messages(struct l_state_struct *, int, int);
+static void back_plane_messages(struct l_state_struct *, int, int);
+static void dpm_SSC100_messages(struct l_state_struct *, int, int);
+static void trans_decode(Trans_elem_st *trans);
+static void trans_messages(struct l_state_struct *, int);
+static void adm_print_pathlist(char *);
+static void display_path_info(char *, char *, WWN_list *);
+static void copy_wwn_data_to_str(char *, const uchar_t *);
+static void adm_mplist_free(struct mplist_struct *);
+static int lun_display(Path_struct *path_struct, L_inquiry inq_struct,
+ int verbose);
+static int non_encl_fc_disk_display(Path_struct *path_struct,
+ L_inquiry inq_struct, int verbose);
+static int get_enclStatus(char *phys_path, char *encl_name, int off_flag);
+static int get_host_controller_pwwn(char *hba_path, uchar_t *pwwn);
+static int get_lun_capacity(char *devpath,
+ struct scsi_capacity_16 *cap_data);
+static int get_path_status(char *devpath, int *status);
+static int get_FC4_host_controller_pwwn(char *hba_path, uchar_t *pwwn);
+
+/*
+ * Gets the device's state from the SENA IB and
+ * checks whether device is offlined, bypassed
+ * or if the slot is empty and prints it to the
+ * stdout.
+ *
+ * RETURNS:
+ * 0 O.K.
+ * non-zero otherwise
+ */
+int
+print_devState(char *devname, char *ppath, int fr_flag, int slot,
+ int verbose_flag)
+{
+L_state l_state;
+int err;
+int i, elem_index = 0;
+uchar_t device_off, ib_status_code, bypass_a_en, bypass_b_en;
+Bp_elem_st bpf, bpr;
+
+
+ if ((err = l_get_status(ppath, &l_state, verbose_flag)) != 0) {
+ (void) print_errString(err, ppath);
+ return (err);
+ }
+
+ for (i = 0; i < (int)l_state.ib_tbl.config.enc_num_elem; i++) {
+ elem_index++;
+ if (l_state.ib_tbl.config.type_hdr[i].type == ELM_TYP_BP) {
+ break;
+ }
+ elem_index += l_state.ib_tbl.config.type_hdr[i].num;
+ }
+ (void) bcopy((const void *)
+ &(l_state.ib_tbl.p2_s.element[elem_index]),
+ (void *)&bpf, sizeof (bpf));
+ (void) bcopy((const void *)
+ &(l_state.ib_tbl.p2_s.element[elem_index + 1]),
+ (void *)&bpr, sizeof (bpr));
+
+ if (fr_flag) {
+ device_off = l_state.drv_front[slot].ib_status.dev_off;
+ bypass_a_en = l_state.drv_front[slot].ib_status.bypass_a_en;
+ bypass_b_en = l_state.drv_front[slot].ib_status.bypass_b_en;
+ ib_status_code = l_state.drv_front[slot].ib_status.code;
+ } else {
+ device_off = l_state.drv_rear[slot].ib_status.dev_off;
+ bypass_a_en = l_state.drv_rear[slot].ib_status.bypass_a_en;
+ bypass_b_en = l_state.drv_rear[slot].ib_status.bypass_b_en;
+ ib_status_code = l_state.drv_rear[slot].ib_status.code;
+ }
+ if (device_off) {
+ (void) fprintf(stdout,
+ MSGSTR(2000,
+ "%s is offlined and bypassed.\n"
+ " Could not get device specific"
+ " information.\n\n"),
+ devname);
+ } else if (bypass_a_en && bypass_b_en) {
+ (void) fprintf(stdout,
+ MSGSTR(2001,
+ "%s is bypassed (Port:AB).\n"
+ " Could not get device specific"
+ " information.\n\n"),
+ devname);
+ } else if (ib_status_code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(2002,
+ "Slot %s is empty.\n\n"),
+ devname);
+ } else if (((bpf.code != S_NOT_INSTALLED) &&
+ ((bpf.byp_a_enabled || bpf.en_bypass_a) &&
+ (bpf.byp_b_enabled || bpf.en_bypass_b))) ||
+ ((bpr.code != S_NOT_INSTALLED) &&
+ ((bpr.byp_a_enabled || bpr.en_bypass_a) &&
+ (bpr.byp_b_enabled || bpr.en_bypass_b)))) {
+ (void) fprintf(stdout,
+ MSGSTR(2003,
+ "Backplane(Port:AB) is bypassed.\n"
+ " Could not get device specific"
+ " information for"
+ " %s.\n\n"), devname);
+ } else {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ devname);
+ }
+ return (-1);
+}
+
+/*
+ * Given an error number, this functions
+ * calls the get_errString() to print a
+ * corresponding error message to the stderr.
+ * get_errString() always returns an error
+ * message, even in case of undefined error number.
+ * So, there is no need to check for a NULL pointer
+ * while printing the error message to the stdout.
+ *
+ * RETURNS: N/A
+ *
+ */
+void
+print_errString(int errnum, char *devpath)
+{
+
+char *errStr;
+
+ errStr = g_get_errString(errnum);
+
+ if (devpath == NULL) {
+ (void) fprintf(stderr,
+ "%s \n\n", errStr);
+ } else {
+ (void) fprintf(stderr,
+ "%s - %s.\n\n", errStr, devpath);
+ }
+
+ /* free the allocated memory for error string */
+ if (errStr != NULL)
+ (void) free(errStr);
+}
+
+/*
+ * adm_inquiry() Display the inquiry information for
+ * a SENA enclosure(s) or disk(s).
+ *
+ * RETURNS:
+ * none.
+ */
+int
+adm_inquiry(char **argv)
+{
+L_inquiry inq;
+L_inquiry80 inq80;
+size_t serial_len;
+int path_index = 0, retval = 0;
+int slot, f_r, err = 0, argpwwn, argnwwn;
+char inq_path[MAXNAMELEN];
+char *path_phys = NULL, *ptr;
+Path_struct *path_struct;
+WWN_list *wwn_list, *wwn_list_ptr, *list_start;
+char last_logical_path[MAXPATHLEN];
+
+ while (argv[path_index] != NULL) {
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) strcpy(inq_path, argv[path_index]);
+ if (((ptr = strstr(inq_path, ",")) != NULL) &&
+ ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') ||
+ (*(ptr +1) == 's'))) {
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ path_index++;
+ retval++;
+ continue;
+ }
+ *ptr = NULL;
+ slot = path_struct->slot;
+ f_r = path_struct->f_flag;
+ path_phys = NULL;
+ if ((err = l_convert_name(inq_path, &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ }
+ path_index++;
+ retval++;
+ continue;
+ }
+ if ((err = print_devState(argv[path_index],
+ path_struct->p_physical_path,
+ f_r, slot, Options & PVERBOSE)) != 0) {
+ path_index++;
+ retval++;
+ continue;
+ }
+ } else {
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ } else {
+ (void) fprintf(stderr, "\n ");
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ }
+ }
+ path_index++;
+ retval++;
+ continue;
+ }
+
+ if (strstr(argv[path_index], "/") != NULL) {
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) fprintf(stderr, "\n");
+ (void) print_errString(err, argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ path_index++;
+ retval++;
+ continue;
+ }
+
+ serial_len = sizeof (inq80.inq_serial);
+ if (err = g_get_serial_number(path_phys, inq80.inq_serial,
+ &serial_len)) {
+ (void) fprintf(stderr, "\n");
+ (void) print_errString(err, argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ path_index++;
+ retval++;
+ continue;
+ }
+ print_inq_data(argv[path_index], path_phys, inq,
+ inq80.inq_serial, serial_len);
+ path_index++;
+ continue;
+ }
+ if ((err = g_get_wwn_list(&wwn_list, 0)) != 0) {
+ return (err);
+ }
+ g_sort_wwn_list(&wwn_list);
+ list_start = wwn_list;
+ argpwwn = argnwwn = 0;
+ (void) strcpy(last_logical_path, path_phys);
+ for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
+ wwn_list_ptr = wwn_list_ptr->wwn_next) {
+ if (strcasecmp(wwn_list_ptr->port_wwn_s, path_struct->argv) ==
+ 0) {
+ list_start = wwn_list_ptr;
+ argpwwn = 1;
+ break;
+ } else if (strcasecmp(wwn_list_ptr->node_wwn_s,
+ path_struct->argv) == 0) {
+ list_start = wwn_list_ptr;
+ argnwwn = 1;
+ break;
+ }
+ }
+
+ if (!(argpwwn || argnwwn)) {
+ /*
+ * if the wwn list is null or the arg device not found
+ * from the wwn list, still go ahead to issue inquiry.
+ */
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) fprintf(stderr, "\n");
+ (void) print_errString(err, argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ path_index++;
+ retval++;
+ continue;
+ }
+
+ serial_len = sizeof (inq80.inq_serial);
+ if (err = g_get_serial_number(path_phys, inq80.inq_serial,
+ &serial_len)) {
+ (void) fprintf(stderr, "\n");
+ (void) print_errString(err, argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ path_index++;
+ retval++;
+ continue;
+ }
+ print_inq_data(argv[path_index], path_phys, inq,
+ inq80.inq_serial, serial_len);
+ (void) g_free_wwn_list(&wwn_list);
+ path_index++;
+ continue;
+ }
+
+ for (wwn_list_ptr = list_start; wwn_list_ptr != NULL;
+ wwn_list_ptr = wwn_list_ptr->wwn_next) {
+ if (argpwwn) {
+ if (strcasecmp(wwn_list_ptr->port_wwn_s,
+ path_struct->argv) != 0) {
+ continue;
+ }
+ (void) strcpy(path_phys,
+ wwn_list_ptr->physical_path);
+ } else if (argnwwn) {
+ if (strcasecmp(wwn_list_ptr->node_wwn_s,
+ path_struct->argv) != 0) {
+ continue;
+ }
+ if (strstr(wwn_list_ptr->logical_path,
+ last_logical_path) != NULL) {
+ continue;
+ }
+ (void) strcpy(path_phys,
+ wwn_list_ptr->physical_path);
+ (void) strcpy(last_logical_path,
+ wwn_list_ptr->logical_path);
+ }
+
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) fprintf(stderr, "\n");
+ (void) print_errString(err, argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ retval++;
+ break;
+ }
+
+ serial_len = sizeof (inq80.inq_serial);
+ if (err = g_get_serial_number(path_phys, inq80.inq_serial,
+ &serial_len)) {
+ (void) fprintf(stderr, "\n");
+ (void) print_errString(err, argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ retval++;
+ break;
+ }
+ print_inq_data(argv[path_index], path_phys, inq,
+ inq80.inq_serial, serial_len);
+
+ }
+
+ (void) g_free_wwn_list(&wwn_list);
+ path_index++;
+ }
+ return (retval);
+}
+
+/*
+ * FORCELIP expert function
+ */
+int
+adm_forcelip(char **argv)
+{
+int slot, f_r, path_index = 0, err = 0, retval = 0;
+Path_struct *path_struct = NULL;
+char *path_phys = NULL, *ptr;
+char err_path[MAXNAMELEN];
+
+ while (argv[path_index] != NULL) {
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) strcpy(err_path, argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ path_index++;
+ retval++;
+ continue;
+ }
+ if (((ptr = strstr(err_path, ", ")) != NULL) &&
+ ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') ||
+ (*(ptr +1) == 's'))) {
+ *ptr = NULL;
+ slot = path_struct->slot;
+ f_r = path_struct->f_flag;
+ path_phys = NULL;
+ if ((err = l_convert_name(err_path,
+ &path_phys, &path_struct,
+ Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr, MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ }
+ path_index++;
+ retval++;
+ continue;
+ }
+ if ((err = print_devState(argv[path_index],
+ path_struct->p_physical_path,
+ f_r, slot, Options & PVERBOSE)) != 0) {
+ path_index++;
+ retval++;
+ continue;
+ }
+ } else {
+ (void) fprintf(stderr, "\n ");
+ (void) fprintf(stderr, MSGSTR(112,
+ "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ }
+ path_index++;
+ retval++;
+ continue;
+ }
+ if (err = g_force_lip(path_phys, Options & PVERBOSE)) {
+ (void) print_errString(err, argv[path_index]);
+ path_index++;
+ retval++;
+ continue;
+ }
+ path_index++;
+ if (path_struct != NULL) {
+ (void) free(path_struct);
+ }
+ }
+ return (retval);
+}
+
+
+/*
+ * DISPLAY function
+ *
+ * RETURNS:
+ * 0 O.K.
+ */
+int
+adm_display_config(char **argv)
+{
+L_inquiry inq, ses_inq;
+int i, slot, f_r, path_index = 0, err = 0, opnerr = 0;
+int retval = 0;
+gfc_map_t map;
+Path_struct *path_struct;
+char *path_phys = NULL, *ptr;
+char ses_path[MAXPATHLEN], inq_path[MAXNAMELEN];
+
+
+ while (argv[path_index] != NULL) {
+ VERBPRINT(MSGSTR(2108, " Displaying information for: %s\n"),
+ argv[path_index]);
+ map.dev_addr = (gfc_port_dev_info_t *)NULL;
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ if (strstr(argv[path_index], SCSI_VHCI) == NULL) {
+
+ (void) strcpy(inq_path, argv[path_index]);
+ if (((ptr = strstr(inq_path, ",")) != NULL) &&
+ ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') ||
+ (*(ptr +1) == 's'))) {
+
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ path_index++;
+ retval++;
+ continue;
+ }
+ *ptr = NULL;
+ slot = path_struct->slot;
+ f_r = path_struct->f_flag;
+ if ((err = l_convert_name(inq_path, &path_phys,
+ &path_struct, Options & PVERBOSE))
+ != 0) {
+
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ }
+ path_index++;
+ retval++;
+ continue;
+ }
+
+ if ((err = print_devState(argv[path_index],
+ path_struct->p_physical_path,
+ f_r, slot, Options & PVERBOSE)) != 0) {
+ path_index++;
+ retval++;
+ continue;
+ }
+ } else {
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ } else {
+ (void) fprintf(stderr, "\n ");
+ (void) fprintf(stderr,
+ MSGSTR(112,
+ "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ }
+ }
+
+ } else {
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ } else {
+ (void) fprintf(stderr, "\n ");
+ (void) fprintf(stderr,
+ MSGSTR(112,
+ "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ (void) fprintf(stderr, "\n");
+ }
+ }
+
+ path_index++;
+ retval++;
+ continue;
+ }
+
+ /*
+ * See what kind of device we are talking to.
+ */
+ if ((opnerr = g_get_inquiry(path_phys, &inq)) != 0) {
+ if (opnerr == L_OPEN_PATH_FAIL) {
+ /*
+ * We check only for L_OPEN_PATH_FAIL because
+ * that is the only error code returned by
+ * g_get_inquiry() which is not got from the ioctl
+ * call itself. So, we are dependent, in a way, on the
+ * implementation of g_get_inquiry().
+ *
+ */
+ (void) print_errString(errno, argv[path_index]);
+ path_index++;
+ retval++;
+ continue;
+ }
+ } else if (!g_enclDiskChk((char *)inq.inq_vid,
+ (char *)inq.inq_pid)) {
+ if ((err = lun_display(path_struct,
+ inq, Options & PVERBOSE)) != 0) {
+ (void) print_errString(err, path_phys);
+ exit(1);
+ }
+ } else if (strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != NULL) {
+ /*
+ * Display SENA enclosure.
+ */
+ (void) fprintf(stdout, "\n\t\t\t\t ");
+ print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
+
+ (void) fprintf(stdout, "\n");
+ if (Options & OPTION_R) {
+ adm_display_err(path_phys,
+ (inq.inq_dtype & DTYPE_MASK));
+ } else {
+ pho_display_config(path_phys);
+ }
+ } else if ((((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)) &&
+ (l_get_enc_type(inq) == DAK_ENC_TYPE)) {
+ /*
+ * Display for the Daktari/DPM
+ */
+ (void) fprintf(stdout, "\n\t\t");
+ for (i = 0; i < sizeof (inq.inq_pid); i++) {
+ (void) fprintf(stdout, "%c", inq.inq_pid[i]);
+ }
+ (void) fprintf(stdout, "\n");
+ if (Options & OPTION_R) {
+ adm_display_err(path_phys,
+ (inq.inq_dtype & DTYPE_MASK));
+ } else {
+ dpm_display_config(path_phys);
+ }
+ /*
+ * if device is in SENA enclosure
+ *
+ * if the slot is valid, then I know this is a SENA enclosure
+ * and can continue
+ * otherwise:
+ * I first get the ses_path, if this doesn't fail
+ * I retrieve the inquiry data from the ses node
+ * and check teh PID to make sure this is a SENA
+ */
+ } else if (((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) &&
+ ((path_struct->slot_valid == 1) ||
+ ((g_get_dev_map(path_phys, &map,
+ (Options & PVERBOSE)) == 0) &&
+ (l_get_ses_path(path_phys, ses_path,
+ &map, Options & PVERBOSE) == 0) &&
+ (g_get_inquiry(ses_path, &ses_inq) == 0) &&
+ ((strstr((char *)ses_inq.inq_pid, ENCLOSURE_PROD_ID)
+ != NULL))))) {
+ if (Options & OPTION_R) {
+ adm_display_err(path_phys,
+ (inq.inq_dtype & DTYPE_MASK));
+ } else {
+ display_fc_disk(path_struct, ses_path, &map, inq,
+ Options & PVERBOSE);
+ }
+
+ } else if (strstr((char *)inq.inq_pid, "SUN_SEN") != 0) {
+ if (strcmp(argv[path_index], path_phys) != 0) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout,
+ MSGSTR(5, "Physical Path:"));
+ (void) fprintf(stdout, "\n %s\n", path_phys);
+ }
+ (void) fprintf(stdout, MSGSTR(2109, "DEVICE is a "));
+ print_chars(inq.inq_vid, sizeof (inq.inq_vid), 1);
+ (void) fprintf(stdout, " ");
+ print_chars(inq.inq_pid, sizeof (inq.inq_pid), 1);
+ (void) fprintf(stdout, MSGSTR(2110, " card."));
+ if (inq.inq_len > 31) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(26, "Revision:"));
+ (void) fprintf(stdout, " ");
+ print_chars(inq.inq_revision,
+ sizeof (inq.inq_revision), 0);
+ }
+ (void) fprintf(stdout, "\n");
+ /* if device is not in SENA or SSA enclosures. */
+ } else if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
+ switch ((inq.inq_dtype & DTYPE_MASK)) {
+ case DTYPE_DIRECT:
+ case DTYPE_SEQUENTIAL: /* Tape */
+ if (Options & OPTION_R) {
+ adm_display_err(path_phys,
+ (inq.inq_dtype & DTYPE_MASK));
+ } else if (non_encl_fc_disk_display(path_struct,
+ inq, Options & PVERBOSE) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(2111,
+ "Error: getting the device"
+ " information.\n"));
+ retval++;
+ }
+ break;
+ /* case 0x01: same as default */
+ default:
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(35,
+ "Device Type:"));
+ (void) fprintf(stdout, "%s\n",
+ dtype[inq.inq_dtype & DTYPE_MASK]);
+ break;
+ }
+ } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
+ (void) fprintf(stdout,
+ MSGSTR(2112, " Device type: Reserved"));
+ (void) fprintf(stdout, "\n");
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(2113, " Device type: Unknown device"));
+ (void) fprintf(stdout, "\n");
+ }
+ path_index++;
+ if (map.dev_addr != NULL) {
+ free((void *)map.dev_addr);
+ }
+ (void) free(path_struct);
+ }
+ return (retval);
+}
+
+
+/*
+ * Powers off a list of SENA enclosure(s)
+ * and disk(s) which is provided by the user.
+ *
+ * RETURNS:
+ * none.
+ */
+int
+adm_power_off(char **argv, int off_flag)
+{
+int path_index = 0, err = 0, retval = 0;
+L_inquiry inq;
+char *path_phys = NULL;
+Path_struct *path_struct;
+
+ while (argv[path_index] != NULL) {
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ /*
+ * In case we did not find the device
+ * in the /devices directory.
+ *
+ * Only valid for pathnames like box,f1
+ */
+ if (path_struct->ib_path_flag) {
+ path_phys = path_struct->p_physical_path;
+ } else {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ }
+ path_index++;
+ retval++;
+ continue;
+ }
+ }
+ if (path_struct->ib_path_flag) {
+ /*
+ * We are addressing a disk using a path
+ * format type box,f1.
+ */
+ if (err = l_dev_pwr_up_down(path_phys,
+ path_struct, off_flag, Options & PVERBOSE,
+ Options & OPTION_CAPF)) {
+ /*
+ * Is it Bypassed... try to give more
+ * informtaion.
+ */
+ print_devState(argv[path_index],
+ path_struct->p_physical_path,
+ path_struct->f_flag, path_struct->slot,
+ Options & PVERBOSE);
+ retval++;
+ }
+ path_index++;
+ continue;
+ }
+
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) print_errString(err, argv[path_index]);
+ path_index++;
+ retval++;
+ continue;
+ }
+ if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != 0) ||
+ (strncmp((char *)inq.inq_vid, "SUN ",
+ sizeof (inq.inq_vid)) &&
+ ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI))) {
+
+ if (get_enclStatus(path_phys, argv[path_index],
+ off_flag) != 0) {
+ path_index++;
+ retval++;
+ continue;
+ }
+ /* power off SENA enclosure. */
+ if (err = l_pho_pwr_up_down(argv[path_index], path_phys,
+ off_flag, Options & PVERBOSE,
+ Options & OPTION_CAPF)) {
+ (void) print_errString(err, argv[path_index]);
+ retval++;
+ }
+ } else if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) {
+ if (err = l_dev_pwr_up_down(path_phys,
+ path_struct, off_flag, Options & PVERBOSE,
+ Options & OPTION_CAPF)) {
+ (void) print_errString(err, argv[path_index]);
+ retval++;
+ }
+ } else {
+ /*
+ * SSA section:
+ */
+ (void) print_errString(L_INVALID_PATH,
+ argv[path_index]);
+ }
+ path_index++;
+ }
+ return (retval);
+}
+
+
+
+void
+adm_bypass_enable(char **argv, int bypass_flag)
+{
+int path_index = 0, err = 0;
+L_inquiry inq;
+char *path_phys = NULL;
+Path_struct *path_struct;
+
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ /*
+ * In case we did not find the device
+ * in the /devices directory.
+ *
+ * Only valid for pathnames like box,f1
+ */
+ if (path_struct->ib_path_flag) {
+ path_phys = path_struct->p_physical_path;
+ } else {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ }
+ exit(-1);
+ }
+ }
+ if (path_struct->ib_path_flag) {
+ if (Options & OPTION_F) {
+ E_USEAGE();
+ exit(-1);
+ }
+ /*
+ * We are addressing a disk using a path
+ * format type box,f1 and no disk
+ * path was found.
+ * So set the Force flag so no reserved/busy
+ * check is performed.
+ */
+ if (err = l_dev_bypass_enable(path_struct,
+ bypass_flag, OPTION_CAPF,
+ Options & OPTION_A,
+ Options & PVERBOSE)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ return;
+ }
+
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != 0) ||
+ (strncmp((char *)inq.inq_vid, "SUN ",
+ sizeof (inq.inq_vid)) &&
+ ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI))) {
+ if ((!((Options & OPTION_F) ||
+ (Options & OPTION_R))) ||
+ ((Options & OPTION_R) &&
+ (Options & OPTION_F))) {
+ E_USEAGE();
+ exit(-1);
+ }
+ if (err = l_bp_bypass_enable(path_phys, bypass_flag,
+ Options & OPTION_A,
+ Options & OPTION_F,
+ Options & OPTION_CAPF,
+ Options & PVERBOSE)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ } else if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) {
+ if (Options & OPTION_F) {
+ E_USEAGE();
+ exit(-1);
+ }
+ if (err = l_dev_bypass_enable(path_struct,
+ bypass_flag, Options & OPTION_CAPF,
+ Options & OPTION_A,
+ Options & PVERBOSE)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ }
+}
+
+/*
+ * adm_download() Download subsystem microcode.
+ * Path must point to a LUX IB.
+ *
+ * RETURNS:
+ * None.
+ */
+void
+adm_download(char **argv, char *file_name)
+{
+int path_index = 0, err = 0;
+char *path_phys = NULL;
+L_inquiry inq;
+Path_struct *path_struct;
+
+ while (argv[path_index] != NULL) {
+ /*
+ * See what kind of device we are talking to.
+ */
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ }
+ exit(-1);
+ }
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != 0) ||
+ (strncmp((char *)inq.inq_vid, "SUN ",
+ sizeof (inq.inq_vid)) &&
+ ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI))) {
+ if (err = l_download(path_phys,
+ file_name, (Options & SAVE),
+ (Options & PVERBOSE))) {
+ (void) print_errString(err,
+ (err == L_OPEN_PATH_FAIL) ?
+ argv[path_index]: file_name);
+ exit(-1);
+ }
+ } else {
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[path_index]);
+ }
+ path_index++;
+ }
+}
+
+/*
+ * display_link_status() Reads and displays the link status.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+display_link_status(char **argv)
+{
+AL_rls *rls = NULL, *n;
+int path_index = 0, err = 0;
+char *path_phys = NULL;
+Path_struct *path_struct;
+
+
+ while (argv[path_index] != NULL) {
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ }
+ exit(-1);
+ }
+ if (err = g_rdls(path_phys, &rls, Options & PVERBOSE)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ n = rls;
+ if (n != NULL) {
+ (void) fprintf(stdout,
+ MSGSTR(2007, "\nLink Error Status "
+ "information for loop:%s\n"),
+ n->driver_path);
+ (void) fprintf(stdout, MSGSTR(2008, "al_pa lnk fail "
+ " sync loss signal loss sequence err"
+ " invalid word CRC\n"));
+ }
+ while (n) {
+ if ((n->payload.rls_linkfail == 0xffffffff) &&
+ (n->payload.rls_syncfail == 0xffffffff) &&
+ (n->payload.rls_sigfail == 0xffffffff) &&
+ (n->payload.rls_primitiverr == 0xffffffff) &&
+ (n->payload.rls_invalidword == 0xffffffff) &&
+ (n->payload.rls_invalidcrc == 0xffffffff)) {
+ (void) fprintf(stdout,
+ "%x\t%-12d%-12d%-14d%-15d%-15d%-12d\n",
+ n->al_ha,
+ n->payload.rls_linkfail,
+ n->payload.rls_syncfail,
+ n->payload.rls_sigfail,
+ n->payload.rls_primitiverr,
+ n->payload.rls_invalidword,
+ n->payload.rls_invalidcrc);
+ } else {
+ (void) fprintf(stdout,
+ "%x\t%-12u%-12u%-14u%-15u%-15u%-12u\n",
+ n->al_ha,
+ n->payload.rls_linkfail,
+ n->payload.rls_syncfail,
+ n->payload.rls_sigfail,
+ n->payload.rls_primitiverr,
+ n->payload.rls_invalidword,
+ n->payload.rls_invalidcrc);
+ }
+ n = n->next;
+ }
+
+ path_index++;
+ }
+ (void) fprintf(stdout,
+ MSGSTR(2009, "NOTE: These LESB counts are not"
+ " cleared by a reset, only power cycles.\n"
+ "These counts must be compared"
+ " to previously read counts.\n"));
+}
+
+
+/*
+ * ib_present_chk() Check to see if IB 0 or 1 is present in the box.
+ *
+ * RETURN:
+ * 1 if ib present
+ * 0 otherwise
+ */
+int
+ib_present_chk(struct l_state_struct *l_state, int which_one)
+{
+Ctlr_elem_st ctlr;
+int i;
+int elem_index = 0;
+int result = 1;
+
+ for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) {
+ elem_index++; /* skip global */
+ if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_IB) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + which_one],
+ (void *)&ctlr, sizeof (ctlr));
+ if (ctlr.code == S_NOT_INSTALLED) {
+ result = 0;
+ }
+ break;
+ }
+ elem_index += l_state->ib_tbl.config.type_hdr[i].num;
+ }
+ return (result);
+}
+
+/*
+ * print_individual_state() Print individual disk status.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+print_individual_state(int status, int port)
+{
+ if (status & L_OPEN_FAIL) {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(28, "Open Failed"));
+ (void) fprintf(stdout, ") ");
+ } else if (status & L_NOT_READY) {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(20, "Not Ready"));
+ (void) fprintf(stdout, ") ");
+ } else if (status & L_NOT_READABLE) {
+ (void) fprintf(stdout, "(");
+ (void) fprintf(stdout,
+ MSGSTR(88, "Not Readable"));
+ (void) fprintf(stdout, ") ");
+ } else if (status & L_SPUN_DWN_D) {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(68, "Spun Down"));
+ (void) fprintf(stdout, ") ");
+ } else if (status & L_SCSI_ERR) {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(70, "SCSI Error"));
+ (void) fprintf(stdout, ") ");
+ } else if (status & L_RESERVED) {
+ if (port == PORT_A) {
+ (void) fprintf(stdout,
+ MSGSTR(2010,
+ " (Rsrv cnflt:A) "));
+ } else if (port == PORT_B) {
+ (void) fprintf(stdout,
+ MSGSTR(2011,
+ " (Rsrv cnflt:B) "));
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(2012,
+ " (Reserve cnflt)"));
+ }
+ } else if (status & L_NO_LABEL) {
+ (void) fprintf(stdout, "(");
+ (void) fprintf(stdout,
+ MSGSTR(92, "No UNIX Label"));
+ (void) fprintf(stdout, ") ");
+ }
+}
+
+
+/*
+ * display_disk_msg() Displays status for
+ * an individual SENA device.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+display_disk_msg(struct l_disk_state_struct *dsk_ptr,
+ struct l_state_struct *l_state, Bp_elem_st *bp, int front_flag)
+{
+int loop_flag = 0;
+int a_and_b = 0;
+int state_a = 0, state_b = 0;
+
+ if (dsk_ptr->ib_status.code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(30, "Not Installed"));
+ (void) fprintf(stdout, " ");
+ if (dsk_ptr->ib_status.fault ||
+ dsk_ptr->ib_status.fault_req) {
+ (void) fprintf(stdout, "(");
+ (void) fprintf(stdout,
+ MSGSTR(2013, "Faulted"));
+ (void) fprintf(stdout,
+ ") ");
+ } else if (dsk_ptr->ib_status.ident ||
+ dsk_ptr->ib_status.rdy_to_ins ||
+ dsk_ptr->ib_status.rmv) {
+ (void) fprintf(stdout,
+ MSGSTR(2014,
+ "(LED Blinking) "));
+ } else {
+ (void) fprintf(stdout,
+ " ");
+ }
+ } else if (dsk_ptr->ib_status.dev_off) {
+ (void) fprintf(stdout, MSGSTR(2015, "Off"));
+ if (dsk_ptr->ib_status.fault || dsk_ptr->ib_status.fault_req) {
+ (void) fprintf(stdout, "(");
+ (void) fprintf(stdout,
+ MSGSTR(2016, "Faulted"));
+ (void) fprintf(stdout,
+ ") ");
+ } else if (dsk_ptr->ib_status.bypass_a_en &&
+ dsk_ptr->ib_status.bypass_b_en) {
+ (void) fprintf(stdout,
+ MSGSTR(2017,
+ "(Bypassed:AB)"));
+ (void) fprintf(stdout,
+ " ");
+ } else if (dsk_ptr->ib_status.bypass_a_en) {
+ (void) fprintf(stdout,
+ MSGSTR(2018,
+ "(Bypassed: A)"));
+ (void) fprintf(stdout,
+ " ");
+ } else if (dsk_ptr->ib_status.bypass_b_en) {
+ (void) fprintf(stdout,
+ MSGSTR(2019,
+ "(Bypassed: B)"));
+ (void) fprintf(stdout,
+ " ");
+ } else {
+ (void) fprintf(stdout,
+ " ");
+ }
+ } else {
+ (void) fprintf(stdout, MSGSTR(2020, "On"));
+
+ if (dsk_ptr->ib_status.fault || dsk_ptr->ib_status.fault_req) {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(2021, "Faulted"));
+ (void) fprintf(stdout, ") ");
+ } else if (dsk_ptr->ib_status.bypass_a_en &&
+ dsk_ptr->ib_status.bypass_b_en) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout,
+ MSGSTR(2022, "(Bypassed:AB)"));
+ (void) fprintf(stdout, " ");
+ } else if (ib_present_chk(l_state, 0) &&
+ dsk_ptr->ib_status.bypass_a_en) {
+ /*
+ * Before printing that the port is bypassed
+ * verify that there is an IB for this port.
+ * If not then don't print.
+ */
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout,
+ MSGSTR(2023, "(Bypassed: A)"));
+ (void) fprintf(stdout, " ");
+ } else if (ib_present_chk(l_state, 1) &&
+ dsk_ptr->ib_status.bypass_b_en) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout,
+ MSGSTR(2024, "(Bypassed: B)"));
+ (void) fprintf(stdout, " ");
+ } else if ((bp->code != S_NOT_INSTALLED) &&
+ ((bp->byp_a_enabled || bp->en_bypass_a) &&
+ !(bp->byp_b_enabled || bp->en_bypass_b))) {
+ (void) fprintf(stdout,
+ MSGSTR(2025,
+ " (Bypassed BP: A)"));
+ } else if ((bp->code != S_NOT_INSTALLED) &&
+ ((bp->byp_b_enabled || bp->en_bypass_b) &&
+ !(bp->byp_a_enabled || bp->en_bypass_a))) {
+ (void) fprintf(stdout,
+ MSGSTR(2026,
+ "(Bypassed BP: B)"));
+ } else if ((bp->code != S_NOT_INSTALLED) &&
+ ((bp->byp_a_enabled || bp->en_bypass_a) &&
+ (bp->byp_b_enabled || bp->en_bypass_b))) {
+ (void) fprintf(stdout,
+ MSGSTR(2027,
+ "(Bypassed BP:AB)"));
+ } else {
+ state_a = dsk_ptr->g_disk_state.d_state_flags[PORT_A];
+ state_b = dsk_ptr->g_disk_state.d_state_flags[PORT_B];
+ a_and_b = state_a & state_b;
+
+ if (dsk_ptr->l_state_flag & L_NO_LOOP) {
+ (void) fprintf(stdout,
+ MSGSTR(2028,
+ " (Loop not accessible)"));
+ loop_flag = 1;
+ } else if (dsk_ptr->l_state_flag & L_INVALID_WWN) {
+ (void) fprintf(stdout,
+ MSGSTR(2029,
+ " (Invalid WWN) "));
+ } else if (dsk_ptr->l_state_flag & L_INVALID_MAP) {
+ (void) fprintf(stdout,
+ MSGSTR(2030,
+ " (Login failed) "));
+ } else if (dsk_ptr->l_state_flag & L_NO_PATH_FOUND) {
+ (void) fprintf(stdout,
+ MSGSTR(2031,
+ " (No path found)"));
+ } else if (a_and_b) {
+ print_individual_state(a_and_b, PORT_A_B);
+ } else if (state_a && (!state_b)) {
+ print_individual_state(state_a, PORT_A);
+ } else if ((!state_a) && state_b) {
+ print_individual_state(state_b, PORT_B);
+ } else if (state_a || state_b) {
+ /* NOTE: Double state - should do 2 lines. */
+ print_individual_state(state_a | state_b,
+ PORT_A_B);
+ } else {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(29, "O.K."));
+ (void) fprintf(stdout,
+ ") ");
+ }
+ }
+ if (loop_flag) {
+ (void) fprintf(stdout, " ");
+ } else if (strlen(dsk_ptr->g_disk_state.node_wwn_s)) {
+ (void) fprintf(stdout, "%s",
+ dsk_ptr->g_disk_state.node_wwn_s);
+ } else {
+ (void) fprintf(stdout, " ");
+ }
+ }
+ if (front_flag) {
+ (void) fprintf(stdout, " ");
+ }
+}
+
+
+
+/*
+ * pho_display_config() Displays device status
+ * information for a SENA enclosure.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+pho_display_config(char *path_phys)
+{
+L_state l_state;
+Bp_elem_st bpf, bpr;
+int i, j, elem_index = 0, err = 0;
+
+
+ /* Get global status */
+ if (err = l_get_status(path_phys, &l_state,
+ (Options & PVERBOSE))) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+
+ /*
+ * Look for abnormal status.
+ */
+ if (l_state.ib_tbl.p2_s.ui.ab_cond) {
+ abnormal_condition_display(&l_state);
+ }
+
+ (void) fprintf(stdout,
+ MSGSTR(2032, " DISK STATUS \n"
+ "SLOT FRONT DISKS (Node WWN) "
+ " REAR DISKS (Node WWN)\n"));
+ /*
+ * Print the status for each disk
+ */
+ for (j = 0; j < (int)l_state.ib_tbl.config.enc_num_elem; j++) {
+ elem_index++;
+ if (l_state.ib_tbl.config.type_hdr[j].type == ELM_TYP_BP)
+ break;
+ elem_index += l_state.ib_tbl.config.type_hdr[j].num;
+ }
+ (void) bcopy((const void *)
+ &(l_state.ib_tbl.p2_s.element[elem_index]),
+ (void *)&bpf, sizeof (bpf));
+ (void) bcopy((const void *)
+ &(l_state.ib_tbl.p2_s.element[elem_index + 1]),
+ (void *)&bpr, sizeof (bpr));
+
+ for (i = 0; i < (int)l_state.total_num_drv/2; i++) {
+ (void) fprintf(stdout, "%-2d ", i);
+ display_disk_msg(&l_state.drv_front[i], &l_state, &bpf, 1);
+ display_disk_msg(&l_state.drv_rear[i], &l_state, &bpr, 0);
+ (void) fprintf(stdout, "\n");
+ }
+
+
+
+ /*
+ * Display the subsystem status.
+ */
+ (void) fprintf(stdout,
+ MSGSTR(2242,
+ " SUBSYSTEM STATUS\nFW Revision:"));
+ print_chars(l_state.ib_tbl.config.prod_revision,
+ sizeof (l_state.ib_tbl.config.prod_revision), 1);
+ (void) fprintf(stdout, MSGSTR(2034, " Box ID:%d"),
+ l_state.ib_tbl.box_id);
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(90, "Node WWN:"));
+ for (i = 0; i < 8; i++) {
+ (void) fprintf(stdout, "%1.2x",
+ l_state.ib_tbl.config.enc_node_wwn[i]);
+ }
+ /* Make sure NULL terminated although it is supposed to be */
+ if (strlen((const char *)l_state.ib_tbl.enclosure_name) <=
+ sizeof (l_state.ib_tbl.enclosure_name)) {
+ (void) fprintf(stdout, MSGSTR(2035, " Enclosure Name:%s\n"),
+ l_state.ib_tbl.enclosure_name);
+ }
+
+ /*
+ *
+ */
+ elem_index = 0;
+ /* Get and print CONTROLLER messages */
+ for (i = 0; i < (int)l_state.ib_tbl.config.enc_num_elem; i++) {
+ elem_index++; /* skip global */
+ switch (l_state.ib_tbl.config.type_hdr[i].type) {
+ case ELM_TYP_PS:
+ ps_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_FT:
+ fan_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_BP:
+ back_plane_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_IB:
+ ctlr_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_LN:
+ /*
+ * NOTE: I just use the Photon's message
+ * string here and don't look at the
+ * language code. The string includes
+ * the language name.
+ */
+ if (l_state.ib_tbl.config.type_hdr[i].text_len != 0) {
+ (void) fprintf(stdout, "%s\t",
+ l_state.ib_tbl.config.text[i]);
+ }
+ break;
+ case ELM_TYP_LO: /* Loop configuration */
+ loop_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_MB: /* Loop configuration */
+ mb_messages(&l_state, i, elem_index);
+ break;
+
+ }
+ /*
+ * Calculate the index to each element.
+ */
+ elem_index += l_state.ib_tbl.config.type_hdr[i].num;
+ }
+ (void) fprintf(stdout, "\n");
+}
+
+
+
+
+/*
+ * dpm_display_config() Displays device status
+ * information for a DAKTARI enclosure.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+dpm_display_config(char *path_phys)
+{
+L_state l_state;
+Bp_elem_st bpf, bpr;
+int i, j, elem_index = 0, err = 0, count;
+
+
+ /* Get global status */
+ if (err = l_get_status(path_phys, &l_state,
+ (Options & PVERBOSE))) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+
+ /*
+ * Look for abnormal status.
+ */
+ if (l_state.ib_tbl.p2_s.ui.ab_cond) {
+ abnormal_condition_display(&l_state);
+ }
+
+ (void) fprintf(stdout,
+ MSGSTR(2247, " DISK STATUS \n"
+ "SLOT DISKS (Node WWN) \n"));
+ /*
+ * Print the status for each disk
+ */
+ for (j = 0; j < (int)l_state.ib_tbl.config.enc_num_elem; j++) {
+ elem_index++;
+ if (l_state.ib_tbl.config.type_hdr[j].type == ELM_TYP_BP)
+ break;
+ elem_index += l_state.ib_tbl.config.type_hdr[j].num;
+ }
+ (void) bcopy((const void *)
+ &(l_state.ib_tbl.p2_s.element[elem_index]),
+ (void *)&bpf, sizeof (bpf));
+ (void) bcopy((const void *)
+ &(l_state.ib_tbl.p2_s.element[elem_index + 1]),
+ (void *)&bpr, sizeof (bpr));
+
+ for (i = 0, count = 0;
+ i < (int)l_state.total_num_drv/2;
+ i++, count++) {
+ (void) fprintf(stdout, "%-2d ", count);
+ display_disk_msg(&l_state.drv_front[i], &l_state, &bpf, 1);
+ (void) fprintf(stdout, "\n");
+ }
+ for (i = 0; i < (int)l_state.total_num_drv/2; i++, count++) {
+ (void) fprintf(stdout, "%-2d ", count);
+ display_disk_msg(&l_state.drv_rear[i], &l_state, &bpf, 1);
+ (void) fprintf(stdout, "\n");
+ }
+
+
+ /*
+ * Display the subsystem status.
+ */
+ (void) fprintf(stdout,
+ MSGSTR(2033,
+ "\t\tSUBSYSTEM STATUS\nFW Revision:"));
+ for (i = 0; i < sizeof (l_state.ib_tbl.config.prod_revision); i++) {
+ (void) fprintf(stdout, "%c",
+ l_state.ib_tbl.config.prod_revision[i]);
+ }
+ (void) fprintf(stdout, MSGSTR(2034, " Box ID:%d"),
+ l_state.ib_tbl.box_id);
+ (void) fprintf(stdout, "\n ");
+
+ (void) fprintf(stdout, MSGSTR(90, "Node WWN:"));
+
+ for (i = 0; i < 8; i++) {
+ (void) fprintf(stdout, "%1.2x",
+ l_state.ib_tbl.config.enc_node_wwn[i]);
+ }
+ /* Make sure NULL terminated although it is supposed to be */
+ if (strlen((const char *)l_state.ib_tbl.enclosure_name) <=
+ sizeof (l_state.ib_tbl.enclosure_name)) {
+ (void) fprintf(stdout, MSGSTR(2035, " Enclosure Name:%s\n"),
+ l_state.ib_tbl.enclosure_name);
+ }
+
+ /*
+ *
+ */
+ elem_index = 0;
+ /* Get and print CONTROLLER messages */
+ for (i = 0; i < (int)l_state.ib_tbl.config.enc_num_elem; i++) {
+ elem_index++; /* skip global */
+ switch (l_state.ib_tbl.config.type_hdr[i].type) {
+ case ELM_TYP_PS:
+ ps_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_FT:
+ fan_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_BP:
+ dpm_SSC100_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_IB:
+ ctlr_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_LN:
+ /*
+ * NOTE: I just use the Photon's message
+ * string here and don't look at the
+ * language code. The string includes
+ * the language name.
+ */
+ if (l_state.ib_tbl.config.type_hdr[i].text_len != 0) {
+ (void) fprintf(stdout, "%s\t",
+ l_state.ib_tbl.config.text[i]);
+ }
+ break;
+ case ELM_TYP_LO: /* Loop configuration */
+ loop_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_MB: /* Loop configuration */
+ mb_messages(&l_state, i, elem_index);
+ break;
+ case ELM_TYP_FL:
+ trans_messages(&l_state, 1);
+ break;
+
+ }
+ /*
+ * Calculate the index to each element.
+ */
+ elem_index += l_state.ib_tbl.config.type_hdr[i].num;
+ }
+ (void) fprintf(stdout, "\n");
+}
+
+
+
+
+
+
+/*
+ * Change the FPM (Front Panel Module) password of the
+ * subsystem associated with the IB addressed by the
+ * enclosure or pathname to name.
+ *
+ */
+void
+intfix(void)
+{
+ if (termio_fd) {
+ termios.c_lflag |= ECHO;
+ ioctl(termio_fd, TCSETS, &termios);
+ }
+ exit(SIGINT);
+}
+
+
+/*
+ * up_password() Changes the password for SENA enclosure.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+up_password(char **argv)
+{
+int path_index = 0, err = 0;
+char password[1024];
+char input[1024];
+int i, j, matched, equal;
+L_inquiry inq;
+void (*sig)();
+char *path_phys = NULL;
+Path_struct *path_struct;
+
+
+ if ((termio_fd = open("/dev/tty", O_RDONLY)) == -1) {
+ (void) fprintf(stderr,
+ MSGSTR(2036, "Error: tty open failed.\n"));
+ exit(-1);
+ }
+ ioctl(termio_fd, TCGETS, &termios);
+ sig = sigset(SIGINT, (void (*)())intfix);
+ /*
+ * Make sure path valid and is to a PHO
+ * before bothering operator.
+ */
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ }
+ exit(-1);
+ }
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) &&
+ (!(strncmp((char *)inq.inq_vid, "SUN ",
+ sizeof (inq.inq_vid)) &&
+ ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) {
+ /*
+ * Again this is like the ssaadm code in that the name
+ * is still not defined before this code must be released.
+ */
+ (void) fprintf(stderr,
+ MSGSTR(2037, "Error: Enclosure is not a %s\n"),
+ ENCLOSURE_PROD_ID);
+ exit(-1);
+ }
+ (void) fprintf(stdout,
+ MSGSTR(2038,
+ "Changing FPM password for subsystem %s\n"),
+ argv[path_index]);
+
+ equal = 0;
+ while (!equal) {
+ memset(input, 0, sizeof (input));
+ memset(password, 0, sizeof (password));
+ (void) fprintf(stdout,
+ MSGSTR(2039, "New password: "));
+
+ termios.c_lflag &= ~ECHO;
+ ioctl(termio_fd, TCSETS, &termios);
+
+ (void) gets(input);
+ (void) fprintf(stdout,
+ MSGSTR(2040, "\nRe-enter new password: "));
+ (void) gets(password);
+ termios.c_lflag |= ECHO;
+ ioctl(termio_fd, TCSETS, &termios);
+ for (i = 0; input[i]; i++) {
+ if (!isdigit(input[i])) {
+ (void) fprintf(stderr,
+ MSGSTR(2041, "\nError: Invalid password."
+ " The password"
+ " must be 4 decimal-digit characters.\n"));
+ exit(-1);
+ }
+ }
+ if (i && (i != 4)) {
+ (void) fprintf(stderr,
+ MSGSTR(2042, "\nError: Invalid password."
+ " The password"
+ " must be 4 decimal-digit characters.\n"));
+ exit(-1);
+ }
+ for (j = 0; password[j]; j++) {
+ if (!isdigit(password[j])) {
+ (void) fprintf(stderr,
+ MSGSTR(2043, "\nError: Invalid password."
+ " The password"
+ " must be 4 decimal-digit characters.\n"));
+ exit(-1);
+ }
+ }
+ if (i != j) {
+ matched = -1;
+ } else for (i = matched = 0; password[i]; i++) {
+ if (password[i] == input[i]) {
+ matched++;
+ }
+ }
+ if ((matched != -1) && (matched == i)) {
+ equal = 1;
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(2044, "\npassword: They don't match;"
+ " try again.\n"));
+ }
+ }
+ (void) fprintf(stdout, "\n");
+ sscanf(input, "%s", password);
+ (void) signal(SIGINT, sig); /* restore signal handler */
+
+ /* Send new password to IB */
+ if (l_new_password(path_phys, input)) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+}
+
+/*
+ * Call g_failover to process failover command
+ */
+void
+adm_failover(char **argv)
+{
+int path_index = 0, err = 0;
+char pathclass[20];
+char *path_phys = NULL;
+
+ (void) memset(pathclass, 0, sizeof (pathclass));
+ (void) strcpy(pathclass, argv[path_index++]);
+ if ((strcmp(pathclass, "primary") != 0) &&
+ (strcmp(pathclass, "secondary") != 0)) {
+ (void) fprintf(stderr,
+ MSGSTR(2300, "Incorrect pathclass\n"));
+ exit(-1);
+ }
+
+ while (argv[path_index] != NULL) {
+ path_phys = g_get_physical_name(argv[path_index++]);
+ if ((path_phys == NULL) ||
+ (strstr(path_phys, SCSI_VHCI) == NULL)) {
+ (void) fprintf(stderr,
+ MSGSTR(2301, "Incorrect pathname\n"));
+ exit(-1);
+ }
+
+ if (err = g_failover(path_phys, pathclass)) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+ }
+}
+
+
+
+/*
+ * up_encl_name() Update the enclosures logical name.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+up_encl_name(char **argv, int argc)
+{
+int i, rval, al_pa, path_index = 0, err = 0;
+L_inquiry inq;
+Box_list *b_list = NULL;
+uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE];
+char wwn1[(WWN_SIZE*2)+1], name[1024], *path_phys = NULL;
+Path_struct *path_struct;
+
+ (void) memset(name, 0, sizeof (name));
+ (void) memset(&inq, 0, sizeof (inq));
+ (void) sscanf(argv[path_index++], "%s", name);
+ for (i = 0; name[i]; i++) {
+ if ((!isalnum(name[i]) &&
+ ((name[i] != '#') &&
+ (name[i] != '-') &&
+ (name[i] != '_') &&
+ (name[i] != '.'))) || i >= 16) {
+ (void) fprintf(stderr,
+ MSGSTR(2045, "Error: Invalid enclosure name.\n"));
+ (void) fprintf(stderr, MSGSTR(2046,
+ "Usage: %s [-v] subcommand {a name consisting of"
+ " 1-16 alphanumeric characters}"
+ " {enclosure... | pathname...}\n"), whoami);
+ exit(-1);
+ }
+ }
+
+ if (((Options & PVERBOSE) && (argc != 5)) ||
+ (!(Options & PVERBOSE) && (argc != 4))) {
+ (void) fprintf(stderr,
+ MSGSTR(114, "Error: Incorrect number of arguments.\n"));
+ (void) fprintf(stderr, MSGSTR(2047,
+ "Usage: %s [-v] subcommand {a name consisting of"
+ " 1-16 alphanumeric characters}"
+ " {enclosure... | pathname...}\n"), whoami);
+ exit(-1);
+ }
+
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ }
+ exit(-1);
+ }
+ /*
+ * Make sure we are talking to an IB.
+ */
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) &&
+ (!(strncmp((char *)inq.inq_vid, "SUN ",
+ sizeof (inq.inq_vid)) &&
+ ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) {
+ /*
+ * Again this is like the ssaadm code in that the name
+ * is still not defined before this code must be released.
+ */
+ (void) fprintf(stderr,
+ MSGSTR(2048, "Error: Pathname does not point to a %s"
+ " enclosure\n"), ENCLOSURE_PROD_NAME);
+ exit(-1);
+ }
+
+ if (err = g_get_wwn(path_phys, port_wwn, node_wwn, &al_pa,
+ Options & PVERBOSE)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+
+ for (i = 0; i < WWN_SIZE; i++) {
+ (void) sprintf(&wwn1[i << 1], "%02x", node_wwn[i]);
+ }
+ if ((err = l_get_box_list(&b_list, Options & PVERBOSE)) != 0) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if (b_list == NULL) {
+ (void) fprintf(stdout,
+ MSGSTR(93, "No %s enclosures found "
+ "in /dev/es\n"), ENCLOSURE_PROD_NAME);
+ exit(-1);
+ } else if (l_duplicate_names(b_list, wwn1, name,
+ Options & PVERBOSE)) {
+ (void) fprintf(stderr,
+ MSGSTR(2049, "Warning: The name you selected, %s,"
+ " is already being used.\n"
+ "Please choose a unique name.\n"
+ "You can use the \"probe\" subcommand to"
+ " see all of the enclosure names.\n"),
+ name);
+ (void) l_free_box_list(&b_list);
+ exit(-1);
+ }
+ (void) l_free_box_list(&b_list);
+
+ /* Send new name to IB */
+ if (rval = l_new_name(path_phys, name)) {
+ (void) print_errString(rval, path_phys);
+ exit(-1);
+ }
+ if (Options & PVERBOSE) {
+ (void) fprintf(stdout,
+ MSGSTR(2050, "The enclosure has been renamed to %s\n"),
+ name);
+ }
+}
+
+
+static int
+get_enclStatus(char *phys_path, char *encl_name, int off_flag)
+{
+int found_pwrOnDrv = 0, slot;
+int found_pwrOffDrv = 0, err = 0;
+L_state l_state;
+
+ if ((err = l_get_status(phys_path,
+ &l_state, Options & PVERBOSE)) != 0) {
+ (void) print_errString(err, encl_name);
+ return (err);
+ }
+
+ if (off_flag) {
+ for (slot = 0; slot < l_state.total_num_drv/2;
+ slot++) {
+ if (((l_state.drv_front[slot].ib_status.code !=
+ S_NOT_INSTALLED) &&
+ (!l_state.drv_front[slot].ib_status.dev_off)) ||
+ ((l_state.drv_rear[slot].ib_status.code !=
+ S_NOT_INSTALLED) &&
+ (!l_state.drv_rear[slot].ib_status.dev_off))) {
+ found_pwrOnDrv++;
+ break;
+ }
+ }
+ if (!found_pwrOnDrv) {
+ (void) fprintf(stdout,
+ MSGSTR(2051,
+ "Notice: Drives in enclosure"
+ " \"%s\" have already been"
+ " powered off.\n\n"),
+ encl_name);
+ return (-1);
+ }
+ } else {
+ for (slot = 0; slot < l_state.total_num_drv/2;
+ slot++) {
+ if (((l_state.drv_front[slot].ib_status.code !=
+ S_NOT_INSTALLED) &&
+ (l_state.drv_front[slot].ib_status.dev_off)) ||
+ ((l_state.drv_rear[slot].ib_status.code !=
+ S_NOT_INSTALLED) &&
+ (l_state.drv_rear[slot].ib_status.dev_off))) {
+ found_pwrOffDrv++;
+ break;
+ }
+ }
+ if (!found_pwrOffDrv) {
+ (void) fprintf(stdout,
+ MSGSTR(2052,
+ "Notice: Drives in enclosure"
+ " \"%s\" have already been"
+ " powered on.\n\n"),
+ encl_name);
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+
+
+
+
+/*
+ * adm_led() The led_request subcommand requests the subsystem
+ * to display the current state or turn off, on, or blink
+ * the yellow LED associated with the disk specified by the
+ * enclosure or pathname.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+adm_led(char **argv, int led_action)
+{
+int path_index = 0, err = 0;
+gfc_map_t map;
+L_inquiry inq;
+Dev_elem_st status;
+char *path_phys = NULL;
+Path_struct *path_struct;
+int enc_t = 0; /* enclosure type */
+char ses_path[MAXPATHLEN];
+L_inquiry ses_inq;
+
+ while (argv[path_index] != NULL) {
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ /* Make sure we have a device path. */
+ if (path_struct->ib_path_flag) {
+ path_phys = path_struct->p_physical_path;
+ } else {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ }
+ exit(-1);
+ }
+ }
+ if (!path_struct->ib_path_flag) {
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_DIRECT) {
+ (void) fprintf(stderr,
+ MSGSTR(2053,
+ "Error: pathname must be to a disk device.\n"
+ " %s\n"), argv[path_index]);
+ exit(-1);
+ }
+ }
+ /*
+ * See if we are in fact talking to a loop or not.
+ */
+ if (err = g_get_dev_map(path_phys, &map,
+ (Options & PVERBOSE))) {
+ (void) print_errString(err, argv[path_index]);
+
+ }
+ if (led_action == L_LED_ON) {
+ (void) fprintf(stderr, MSGSTR(2054,
+ "The led_on functionality is not applicable "
+ "to this subsystem.\n"));
+ exit(-1);
+ }
+ if (err = l_led(path_struct, led_action, &status,
+ (Options & PVERBOSE))) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+
+ /* Check to see if we have a daktari */
+ if (l_get_ses_path(path_phys, ses_path, &map,
+ (Options & PVERBOSE)) == 0) {
+ if (g_get_inquiry(ses_path, &ses_inq) == 0) {
+ enc_t = l_get_enc_type(ses_inq);
+ }
+ }
+ switch (led_action) {
+ case L_LED_STATUS:
+ if (status.fault || status.fault_req) {
+ if (!path_struct->slot_valid) {
+ (void) fprintf(stdout,
+ MSGSTR(2055, "LED state is ON for "
+ "device:\n %s\n"), path_phys);
+ } else {
+ if (enc_t == DAK_ENC_TYPE) {
+ if (path_struct->f_flag) {
+ (void) fprintf(stdout,
+ MSGSTR(2236, "LED state is ON for "
+ "device in location: slot %d\n"),
+ path_struct->slot);
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(2236, "LED state is ON for "
+ "device in location: slot %d\n"),
+ path_struct->slot +
+ (MAX_DRIVES_DAK/2));
+ }
+ } else {
+ (void) fprintf(stdout,
+ (path_struct->f_flag) ?
+ MSGSTR(2056, "LED state is ON for "
+ "device in location: front,slot %d\n")
+ : MSGSTR(2057, "LED state is ON for "
+ "device in location: rear,slot %d\n"),
+ path_struct->slot);
+ }
+ }
+ } else if (status.ident || status.rdy_to_ins ||
+ status.rmv) {
+ if (!path_struct->slot_valid) {
+ (void) fprintf(stdout, MSGSTR(2058,
+ "LED state is BLINKING for "
+ "device:\n %s\n"), path_phys);
+ } else {
+ if (enc_t == DAK_ENC_TYPE) {
+ if (path_struct->f_flag) {
+ (void) fprintf(stdout, MSGSTR(2237,
+ "LED state is BLINKING for "
+ "device in location: slot %d\n"),
+ path_struct->slot);
+ } else {
+ (void) fprintf(stdout, MSGSTR(2237,
+ "LED state is BLINKING for "
+ "device in location: slot %d\n"),
+ path_struct->slot + (MAX_DRIVES_DAK/2));
+ }
+ } else {
+ (void) fprintf(stdout,
+ (path_struct->f_flag) ?
+ MSGSTR(2059, "LED state is BLINKING for "
+ "device in location: front,slot %d\n")
+ : MSGSTR(2060, "LED state is BLINKING for "
+ "device in location: rear,slot %d\n"),
+ path_struct->slot);
+ }
+ }
+ } else {
+ if (!path_struct->slot_valid) {
+ (void) fprintf(stdout,
+ MSGSTR(2061, "LED state is OFF for "
+ "device:\n %s\n"), path_phys);
+ } else {
+ if (enc_t == DAK_ENC_TYPE) {
+ if (path_struct->f_flag) {
+ (void) fprintf(stdout, MSGSTR(2238,
+ "LED state is OFF for "
+ "device in location: slot %d\n"),
+ path_struct->slot);
+ } else {
+ (void) fprintf(stdout, MSGSTR(2238,
+ "LED state is OFF for "
+ "device in location: slot %d\n"),
+ path_struct->slot + MAX_DRIVES_DAK/2);
+ }
+ } else {
+ (void) fprintf(stdout,
+ (path_struct->f_flag) ?
+ MSGSTR(2062, "LED state is OFF for "
+ "device in location: front,slot %d\n")
+ : MSGSTR(2063, "LED state is OFF for "
+ "device in location: rear,slot %d\n"),
+ path_struct->slot);
+ }
+ }
+ }
+ break;
+ }
+ free((void *)map.dev_addr);
+ path_index++;
+ }
+}
+
+
+
+
+
+/*
+ * dump() Dump information
+ *
+ * RETURNS:
+ * none.
+ */
+void
+dump(char **argv)
+{
+uchar_t *buf;
+int path_index = 0, err = 0;
+L_inquiry inq;
+char hdr_buf[MAXNAMELEN];
+Rec_diag_hdr *hdr, *hdr_ptr;
+char *path_phys = NULL;
+Path_struct *path_struct;
+
+ /*
+ * get big buffer
+ */
+ if ((hdr = (struct rec_diag_hdr *)calloc(1, MAX_REC_DIAG_LENGTH)) ==
+ NULL) {
+ (void) print_errString(L_MALLOC_FAILED, NULL);
+ exit(-1);
+ }
+ buf = (uchar_t *)hdr;
+
+ while (argv[path_index] != NULL) {
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ }
+ exit(-1);
+ }
+ if (err = g_get_inquiry(path_phys, &inq)) {
+ (void) print_errString(err, argv[path_index]);
+ } else {
+ (void) g_dump(MSGSTR(2065, "INQUIRY data: "),
+ (uchar_t *)&inq, 5 + inq.inq_len, HEX_ASCII);
+ }
+
+ (void) memset(buf, 0, MAX_REC_DIAG_LENGTH);
+ if (err = l_get_envsen(path_phys, buf, MAX_REC_DIAG_LENGTH,
+ (Options & PVERBOSE))) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ (void) fprintf(stdout,
+ MSGSTR(2066, "\t\tEnvironmental Sense Information\n"));
+
+ /*
+ * Dump all pages.
+ */
+ hdr_ptr = hdr;
+
+ while (hdr_ptr->page_len != 0) {
+ (void) sprintf(hdr_buf, MSGSTR(2067, "Page %d: "),
+ hdr_ptr->page_code);
+ (void) g_dump(hdr_buf, (uchar_t *)hdr_ptr,
+ HEADER_LEN + hdr_ptr->page_len, HEX_ASCII);
+ hdr_ptr += ((HEADER_LEN + hdr_ptr->page_len) /
+ sizeof (struct rec_diag_hdr));
+ }
+ path_index++;
+ }
+ (void) free(buf);
+}
+
+
+
+/*
+ * display_socal_stats() Display socal driver kstat information.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+display_socal_stats(int port, char *socal_path, struct socal_stats *fc_stats)
+{
+int i;
+int header_flag = 0;
+char status_msg_buf[MAXNAMELEN];
+int num_status_entries;
+
+ (void) fprintf(stdout, MSGSTR(2068,
+ "\tInformation for FC Loop on port %d of"
+ " FC100/S Host Adapter\n\tat path: %s\n"),
+ port, socal_path);
+ if (fc_stats->version > 1) {
+ (void) fprintf(stdout, "\t");
+ (void) fprintf(stdout, MSGSTR(32,
+ "Information from %s"), fc_stats->drvr_name);
+ (void) fprintf(stdout, "\n");
+ if ((*fc_stats->node_wwn != NULL) &&
+ (*fc_stats->port_wwn[port] != NULL)) {
+ (void) fprintf(stdout, MSGSTR(104,
+ " Host Adapter WWN's: Node:%s"
+ " Port:%s\n"),
+ fc_stats->node_wwn,
+ fc_stats->port_wwn[port]);
+ }
+ if (*fc_stats->fw_revision != NULL) {
+ (void) fprintf(stdout, MSGSTR(105,
+ " Host Adapter Firmware Revision: %s\n"),
+ fc_stats->fw_revision);
+ }
+ if (fc_stats->parity_chk_enabled != 0) {
+ (void) fprintf(stdout, MSGSTR(2069,
+ " This Host Adapter checks S-Bus parity.\n"));
+ }
+ }
+
+ (void) fprintf(stdout, MSGSTR(2070,
+ " Version Resets Req_Q_Intrpts Qfulls"
+ " Unsol_Resps Lips\n"));
+
+ (void) fprintf(stdout, " %4d%8d%11d%13d%10d%7d\n",
+ fc_stats->version,
+ fc_stats->resets,
+ fc_stats->reqq_intrs,
+ fc_stats->qfulls,
+ fc_stats->pstats[port].unsol_resps,
+ fc_stats->pstats[port].lips);
+
+ (void) fprintf(stdout, MSGSTR(2071,
+ " Els_rcvd Abts"
+ " Abts_ok Offlines Loop_onlines Onlines\n"));
+
+ (void) fprintf(stdout, " %4d%9d%10d%9d%13d%10d\n",
+ fc_stats->pstats[port].els_rcvd,
+ fc_stats->pstats[port].abts,
+ fc_stats->pstats[port].abts_ok,
+ fc_stats->pstats[port].offlines,
+ fc_stats->pstats[port].online_loops,
+ fc_stats->pstats[port].onlines);
+
+ /* If any status conditions exist then display */
+ if (fc_stats->version > 1) {
+ num_status_entries = FC_STATUS_ENTRIES;
+ } else {
+ num_status_entries = 64;
+ }
+
+ for (i = 0; i < num_status_entries; i++) {
+ if (fc_stats->pstats[port].resp_status[i] != 0) {
+ if (header_flag++ == 0) {
+ (void) fprintf(stdout, MSGSTR(2072,
+ " Fibre Channel Transport status:\n "
+ "Status Value"
+ " Count\n"));
+ }
+ (void) l_format_fc_status_msg(status_msg_buf,
+ fc_stats->pstats[port].resp_status[i], i);
+ (void) fprintf(stdout, " %s\n",
+ status_msg_buf);
+ }
+ }
+}
+
+
+
+/*
+ * display_sf_stats() Display sf driver kstat information.
+ *
+ * This routine is called by private loop device only
+ *
+ * RETURNS:
+ * none.
+ */
+void
+display_sf_stats(char *path_phys, int dtype, struct sf_stats *sf_stats)
+{
+int i, al_pa, err = 0;
+gfc_map_t map;
+uchar_t node_wwn[WWN_SIZE];
+uchar_t port_wwn[WWN_SIZE];
+gfc_port_dev_info_t *dev_addr_list;
+
+ if (sf_stats->version > 1) {
+ (void) fprintf(stdout, "\n\t");
+ (void) fprintf(stdout, MSGSTR(32,
+ "Information from %s"),
+ sf_stats->drvr_name);
+ (void) fprintf(stdout, "\n");
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(2073, "\n\t\tInformation from sf driver:\n"));
+ }
+
+ (void) fprintf(stdout, MSGSTR(2074,
+ " Version Lip_count Lip_fail"
+ " Alloc_fail #_cmds "
+ "Throttle_limit Pool_size\n"));
+
+ (void) fprintf(stdout, " %4d%9d%12d%11d%10d%11d%12d\n",
+ sf_stats->version,
+ sf_stats->lip_count,
+ sf_stats->lip_failures,
+ sf_stats->cralloc_failures,
+ sf_stats->ncmds,
+ sf_stats->throttle_limit,
+ sf_stats->cr_pool_size);
+
+ (void) fprintf(stdout, MSGSTR(2075,
+ "\n\t\tTARGET ERROR INFORMATION:\n"));
+ (void) fprintf(stdout, MSGSTR(2076,
+ "AL_PA Els_fail Timouts Abts_fail"
+ " Tsk_m_fail "
+ " Data_ro_mis Dl_len_mis Logouts\n"));
+
+ if (err = g_get_dev_map(path_phys, &map, (Options & PVERBOSE))) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+
+ if (dtype == DTYPE_DIRECT) {
+ if (err = g_get_wwn(path_phys, port_wwn, node_wwn, &al_pa,
+ Options & PVERBOSE)) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+ /* for san toleration, only need to modify the code */
+ /* such that the current sf_al_map structure replaced */
+ /* by the new gfc_map structure for private loop device */
+ for (i = 0, dev_addr_list = map.dev_addr; i < map.count;
+ i++, dev_addr_list++) {
+ if (dev_addr_list->gfc_port_dev.priv_port.sf_al_pa
+ == al_pa) {
+ (void) fprintf(stdout,
+ "%3x%10d%8d%10d%11d%13d%11d%9d\n",
+ al_pa,
+ sf_stats->tstats[i].els_failures,
+ sf_stats->tstats[i].timeouts,
+ sf_stats->tstats[i].abts_failures,
+ sf_stats->tstats[i].task_mgmt_failures,
+ sf_stats->tstats[i].data_ro_mismatches,
+ sf_stats->tstats[i].dl_len_mismatches,
+ sf_stats->tstats[i].logouts_recvd);
+ break;
+ }
+ }
+ if (i >= map.count) {
+ (void) print_errString(L_INVALID_LOOP_MAP, path_phys);
+ exit(-1);
+ }
+ } else {
+ for (i = 0, dev_addr_list = map.dev_addr; i < map.count;
+ i++, dev_addr_list++) {
+ (void) fprintf(stdout,
+ "%3x%10d%8d%10d%11d%13d%11d%9d\n",
+ dev_addr_list->gfc_port_dev.priv_port.sf_al_pa,
+ sf_stats->tstats[i].els_failures,
+ sf_stats->tstats[i].timeouts,
+ sf_stats->tstats[i].abts_failures,
+ sf_stats->tstats[i].task_mgmt_failures,
+ sf_stats->tstats[i].data_ro_mismatches,
+ sf_stats->tstats[i].dl_len_mismatches,
+ sf_stats->tstats[i].logouts_recvd);
+ }
+ }
+ free((void *)map.dev_addr);
+}
+
+
+
+/*
+ * adm_display_err() Displays enclosure specific
+ * error information.
+ *
+ * RETURNS:
+ * none.
+ */
+static void
+adm_display_err(char *path_phys, int dtype)
+{
+int i, drvr_inst, sf_inst, socal_inst, port, al_pa, err = 0;
+char *char_ptr, socal_path[MAXPATHLEN], drvr_path[MAXPATHLEN];
+struct stat sbuf;
+kstat_ctl_t *kc;
+kstat_t *ifp_ks, *sf_ks, *fc_ks;
+sf_stats_t sf_stats;
+socal_stats_t fc_stats;
+ifp_stats_t ifp_stats;
+int header_flag = 0, pathcnt = 1;
+char status_msg_buf[MAXNAMELEN];
+gfc_map_t map;
+uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE];
+uint_t path_type;
+gfc_port_dev_info_t *dev_addr_list;
+mp_pathlist_t pathlist;
+int p_on = 0, p_st = 0;
+
+ if ((kc = kstat_open()) == (kstat_ctl_t *)NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(2077, " Error: can't open kstat\n"));
+ exit(-1);
+ }
+
+ if (strstr(path_phys, SCSI_VHCI)) {
+ (void) strcpy(drvr_path, path_phys);
+ if (err = g_get_pathlist(drvr_path, &pathlist)) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+ pathcnt = pathlist.path_count;
+ p_on = p_st = 0;
+ for (i = 0; i < pathcnt; i++) {
+ if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
+ if (pathlist.path_info[i].path_state ==
+ MDI_PATHINFO_STATE_ONLINE) {
+ p_on = i;
+ break;
+ } else if (pathlist.path_info[i].path_state ==
+ MDI_PATHINFO_STATE_STANDBY) {
+ p_st = i;
+ }
+ }
+ }
+ if (pathlist.path_info[p_on].path_state ==
+ MDI_PATHINFO_STATE_ONLINE) {
+ /* on_line path */
+ (void) strcpy(drvr_path,
+ pathlist.path_info[p_on].path_hba);
+ } else {
+ /* standby or path0 */
+ (void) strcpy(drvr_path,
+ pathlist.path_info[p_st].path_hba);
+ }
+ free(pathlist.path_info);
+ } else {
+
+ (void) strcpy(drvr_path, path_phys);
+
+ if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
+ (void) print_errString(L_INVLD_PATH_NO_SLASH_FND,
+ path_phys);
+ exit(-1);
+ }
+ *char_ptr = '\0'; /* Make into nexus or HBA driver path. */
+ }
+ /*
+ * Each HBA and driver stack has its own structures
+ * for this, so we have to handle each one individually.
+ */
+ path_type = g_get_path_type(drvr_path);
+
+ if (path_type) { /* Quick sanity check for valid path */
+ if ((err = g_get_nexus_path(drvr_path, &char_ptr)) != 0) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+ (void) strcpy(socal_path, char_ptr);
+
+ }
+
+ /* attach :devctl to get node stat instead of dir stat. */
+ (void) strcat(drvr_path, FC_CTLR);
+
+ if (stat(drvr_path, &sbuf) < 0) {
+ (void) print_errString(L_LSTAT_ERROR, path_phys);
+ exit(-1);
+ }
+
+ drvr_inst = minor(sbuf.st_rdev);
+
+
+ /*
+ * first take care of ifp card.
+ */
+ if (path_type & FC4_PCI_FCA) {
+ if ((ifp_ks = kstat_lookup(kc, "ifp",
+ drvr_inst, "statistics")) != NULL) {
+
+ if (kstat_read(kc, ifp_ks, &ifp_stats) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(2082,
+ "Error: could not read ifp%d\n"), drvr_inst);
+ exit(-1);
+ }
+ (void) fprintf(stdout, MSGSTR(2083,
+ "\tInformation for FC Loop of"
+ " FC100/P Host Adapter\n\tat path: %s\n"),
+ drvr_path);
+ if (ifp_stats.version > 1) {
+ (void) fprintf(stdout, "\t");
+ (void) fprintf(stdout, MSGSTR(32,
+ "Information from %s"),
+ ifp_stats.drvr_name);
+ (void) fprintf(stdout, "\n");
+ if ((*ifp_stats.node_wwn != NULL) &&
+ (*ifp_stats.port_wwn != NULL)) {
+ (void) fprintf(stdout, MSGSTR(104,
+ " Host Adapter WWN's: Node:%s"
+ " Port:%s\n"),
+ ifp_stats.node_wwn,
+ ifp_stats.port_wwn);
+ }
+ if (*ifp_stats.fw_revision != NULL) {
+ (void) fprintf(stdout, MSGSTR(105,
+ " Host Adapter Firmware Revision: %s\n"),
+ ifp_stats.fw_revision);
+ }
+ if (ifp_stats.parity_chk_enabled != 0) {
+ (void) fprintf(stdout, MSGSTR(2084,
+ " This Host Adapter checks "
+ "PCI-Bus parity.\n"));
+ }
+ }
+
+ (void) fprintf(stdout, MSGSTR(2085,
+ " Version Lips\n"));
+ (void) fprintf(stdout, " %10d%7d\n",
+ ifp_stats.version,
+ ifp_stats.lip_count);
+ /* If any status conditions exist then display */
+ for (i = 0; i < FC_STATUS_ENTRIES; i++) {
+ if (ifp_stats.resp_status[i] != 0) {
+ if (header_flag++ == 0) {
+ (void) fprintf(stdout, MSGSTR(2086,
+ " Fibre Channel Transport "
+ "status:\n "
+ "Status "
+ " Value"
+ " Count\n"));
+ }
+ (void) l_format_ifp_status_msg(
+ status_msg_buf,
+ ifp_stats.resp_status[i], i);
+ (void) fprintf(stdout, " %s\n",
+ status_msg_buf);
+ }
+ }
+
+ (void) fprintf(stdout, MSGSTR(2087,
+ "\n\t\tTARGET ERROR INFORMATION:\n"));
+ (void) fprintf(stdout, MSGSTR(2088,
+ "AL_PA logouts_recvd task_mgmt_failures"
+ " data_ro_mismatches data_len_mismatch\n"));
+
+ if (err = g_get_dev_map(path_phys, &map,
+ (Options & PVERBOSE))) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+
+
+ if (dtype == DTYPE_DIRECT) {
+ if (err = g_get_wwn(path_phys, port_wwn,
+ node_wwn, &al_pa,
+ Options & PVERBOSE)) {
+ (void) print_errString(err,
+ path_phys);
+ exit(-1);
+ }
+ for (i = 0, dev_addr_list = map.dev_addr;
+ i < map.count; i++,
+ dev_addr_list++) {
+ if (dev_addr_list->gfc_port_dev.
+ priv_port.sf_al_pa
+ == al_pa) {
+ (void) fprintf
+ (stdout,
+ "%3x%14d%18d%20d%20d\n",
+ al_pa,
+ ifp_stats.tstats[i].
+ logouts_recvd,
+ ifp_stats.tstats[i].
+ task_mgmt_failures,
+ ifp_stats.tstats[i].
+ data_ro_mismatches,
+ ifp_stats.tstats[i].
+ dl_len_mismatches);
+ break;
+ }
+ }
+ if (i >= map.count) {
+
+ (void) print_errString(
+ L_INVALID_LOOP_MAP, path_phys);
+ exit(-1);
+ }
+
+ } else {
+ for (i = 0, dev_addr_list = map.dev_addr;
+ i < map.count; i++,
+ dev_addr_list++) {
+ (void) fprintf(stdout,
+ "%3x%14d%18d%20d%20d\n",
+ dev_addr_list->gfc_port_dev.
+ priv_port.sf_al_pa,
+ ifp_stats.tstats[i].logouts_recvd,
+ ifp_stats.tstats[i].task_mgmt_failures,
+ ifp_stats.tstats[i].data_ro_mismatches,
+ ifp_stats.tstats[i].dl_len_mismatches);
+ }
+ }
+
+ free((void *)map.dev_addr);
+ }
+ } else if (path_type & FC4_SF_XPORT) {
+ /*
+ * process cards with sf xport nodes.
+ */
+ if (stat(socal_path, &sbuf) < 0) {
+ (void) print_errString(L_LSTAT_ERROR, path_phys);
+ exit(-1);
+ }
+ socal_inst = minor(sbuf.st_rdev)/2;
+ port = socal_inst%2;
+
+ sf_inst = LUX_SF_MINOR2INST(minor(sbuf.st_rdev));
+ if (!(sf_ks = kstat_lookup(kc, "sf", sf_inst,
+ "statistics"))) {
+ (void) fprintf(stderr,
+ MSGSTR(2078,
+ " Error: could not lookup driver stats for sf%d\n"),
+ sf_inst);
+ exit(-1);
+ }
+ if (!(fc_ks = kstat_lookup(kc, "socal", socal_inst,
+ "statistics"))) {
+ (void) fprintf(stderr,
+ MSGSTR(2079,
+ " Error: could not lookup driver stats for socal%d\n"),
+ socal_inst);
+ exit(-1);
+ }
+ if (kstat_read(kc, sf_ks, &sf_stats) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(2080,
+ " Error: could not read driver stats for sf%d\n"),
+ sf_inst);
+ exit(-1);
+ }
+ if (kstat_read(kc, fc_ks, &fc_stats) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(2081,
+ " Error: could not read driver stats for socal%d\n"),
+ socal_inst);
+ exit(-1);
+ }
+ (void) display_socal_stats(port, socal_path, &fc_stats);
+ (void) display_sf_stats(path_phys, dtype, &sf_stats);
+ } else if ((path_type & FC_FCA_MASK) == FC_PCI_FCA) {
+ fprintf(stderr, MSGSTR(2252,
+ "\n WARNING!! display -r on qlc is"
+ " currently not supported.\n"));
+ } else {
+ fprintf(stderr, MSGSTR(2253,
+ "\n WARNING!! display -r is not supported on path\n"
+ " %s\n"), drvr_path);
+ }
+ (void) kstat_close(kc);
+
+}
+
+
+
+/*ARGSUSED*/
+/*
+ * adm_display_verbose_disk() Gets the mode page information
+ * for a SENA disk and prints that information.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+adm_display_verbose_disk(char *path, int verbose)
+{
+uchar_t *pg_buf;
+Mode_header_10 *mode_header_ptr;
+Mp_01 *pg1_buf;
+Mp_04 *pg4_buf;
+struct mode_page *pg_hdr;
+int offset, hdr_printed = 0, err = 0;
+
+ if ((err = l_get_mode_pg(path, &pg_buf, verbose)) == 0) {
+
+ mode_header_ptr = (struct mode_header_10_struct *)(int)pg_buf;
+ pg_hdr = ((struct mode_page *)((int)pg_buf +
+ (uchar_t)sizeof (struct mode_header_10_struct) +
+ (uchar_t *)(uintptr_t)(mode_header_ptr->bdesc_length)));
+ offset = sizeof (struct mode_header_10_struct) +
+ mode_header_ptr->bdesc_length;
+ while (offset < (mode_header_ptr->length +
+ sizeof (mode_header_ptr->length))) {
+ switch (pg_hdr->code) {
+ case 0x01:
+ pg1_buf = (struct mode_page_01_struct *)
+ (int)pg_hdr;
+ P_DPRINTF(" adm_display_verbose_disk:"
+ "Mode Sense page 1 found.\n");
+ if (hdr_printed++ == 0) {
+ (void) fprintf(stdout,
+ MSGSTR(2089,
+ " Mode Sense data:\n"));
+ }
+ (void) fprintf(stdout,
+ MSGSTR(2090,
+ " AWRE:\t\t\t%d\n"
+ " ARRE:\t\t\t%d\n"
+ " Read Retry Count:\t\t"
+ "%d\n"
+ " Write Retry Count:\t\t"
+ "%d\n"),
+ pg1_buf->awre,
+ pg1_buf->arre,
+ pg1_buf->read_retry_count,
+ pg1_buf->write_retry_count);
+ break;
+ case MODEPAGE_GEOMETRY:
+ pg4_buf = (struct mode_page_04_struct *)
+ (int)pg_hdr;
+ P_DPRINTF(" adm_display_verbose_disk:"
+ "Mode Sense page 4 found.\n");
+ if (hdr_printed++ == 0) {
+ (void) fprintf(stdout,
+ MSGSTR(2091,
+ " Mode Sense data:\n"));
+ }
+ if (pg4_buf->rpm) {
+ (void) fprintf(stdout,
+ MSGSTR(2092,
+ " Medium rotation rate:\t"
+ "%d RPM\n"), pg4_buf->rpm);
+ }
+ break;
+ }
+ offset += pg_hdr->length + sizeof (struct mode_page);
+ pg_hdr = ((struct mode_page *)((int)pg_buf +
+ (uchar_t)offset));
+ }
+
+
+
+
+
+ } else if (getenv("_LUX_P_DEBUG") != NULL) {
+ (void) print_errString(err, path);
+ }
+}
+
+/*
+ * Print out the port_wwn or node_wwn
+ */
+void
+print_wwn(FILE *fd, uchar_t *pn_wwn)
+{
+
+ (void) fprintf(fd,
+ " %1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ pn_wwn[0], pn_wwn[1], pn_wwn[2], pn_wwn[3],
+ pn_wwn[4], pn_wwn[5], pn_wwn[6], pn_wwn[7]);
+}
+
+/*
+ * Print out the fabric dev port_id, hard_addr, port_wwn and node_wwn
+ */
+void
+print_fabric_prop(int pos, uchar_t *port_wwn, uchar_t *node_wwn, int port_addr,
+ int hard_addr)
+{
+ (void) fprintf(stdout, "%-4d %-6x %-6x ",
+ pos, port_addr, hard_addr);
+ print_wwn(stdout, port_wwn);
+ print_wwn(stdout, node_wwn);
+}
+
+/*
+ * Print out the private loop dev port_id, hard_addr, port_wwn and node_wwn
+ */
+void
+print_private_loop_prop(int pos, uchar_t *port_wwn, uchar_t *node_wwn,
+ int port_addr, int hard_addr)
+{
+ (void) fprintf(stdout, "%-3d %-2x %-2x %-2x ",
+ pos, port_addr, g_sf_alpa_to_switch[port_addr], hard_addr);
+ print_wwn(stdout, port_wwn);
+ print_wwn(stdout, node_wwn);
+}
+
+/*
+ * Get the device map from
+ * fc nexus driver and prints the map.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+dump_map(char **argv)
+{
+int i = 0, path_index = 0, pathcnt = 1;
+int limited_map_flag = 0, err = 0;
+char *path_phys = NULL;
+Path_struct *path_struct;
+struct lilpmap limited_map;
+uint_t dev_type;
+char temp2path[MAXPATHLEN];
+mp_pathlist_t pathlist;
+int p_pw = 0, p_on = 0, p_st = 0;
+gfc_dev_t map_root, map_dev;
+int *port_addr, *hard_addr, pos = 0, count;
+uchar_t *hba_port_wwn, *port_wwn, *node_wwn, *dtype_prop;
+uint_t map_topo;
+
+ while (argv[path_index] != NULL) {
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err, argv[path_index]);
+ }
+ exit(-1);
+ }
+
+ if (strstr(path_phys, SCSI_VHCI) != NULL) {
+ /* obtain phci */
+ (void) strcpy(temp2path, path_phys);
+ if (err = g_get_pathlist(temp2path, &pathlist)) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+ pathcnt = pathlist.path_count;
+ p_pw = p_on = p_st = 0;
+ for (i = 0; i < pathcnt; i++) {
+ if (pathlist.path_info[i].path_state <
+ MAXPATHSTATE) {
+ if (strstr(pathlist.path_info[i].
+ path_addr,
+ path_struct->argv) != NULL) {
+ p_pw = i;
+ break;
+ }
+ if (pathlist.path_info[i].path_state ==
+ MDI_PATHINFO_STATE_ONLINE) {
+ p_on = i;
+ }
+ if (pathlist.path_info[i].path_state ==
+ MDI_PATHINFO_STATE_STANDBY) {
+ p_st = i;
+ }
+ }
+ }
+ if (strstr(pathlist.path_info[p_pw].path_addr,
+ path_struct->argv) != NULL) {
+ /* matching input pwwn */
+ (void) strcpy(temp2path,
+ pathlist.path_info[p_pw].path_hba);
+ } else if (pathlist.path_info[p_on].path_state ==
+ MDI_PATHINFO_STATE_ONLINE) {
+ /* on_line path */
+ (void) strcpy(temp2path,
+ pathlist.path_info[p_on].path_hba);
+ } else {
+ /* standby or path0 */
+ (void) strcpy(temp2path,
+ pathlist.path_info[p_st].path_hba);
+ }
+ free(pathlist.path_info);
+ (void) strcat(temp2path, FC_CTLR);
+ } else {
+ (void) strcpy(temp2path, path_phys);
+ }
+
+ if ((dev_type = g_get_path_type(temp2path)) == 0) {
+ (void) print_errString(L_INVALID_PATH,
+ argv[path_index]);
+ exit(-1);
+ }
+
+ if ((map_root = g_dev_map_init(temp2path, &err,
+ MAP_FORMAT_LILP)) == NULL) {
+ if (dev_type & FC_FCA_MASK) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ } else {
+ /*
+ * This did not work so try the FCIO_GETMAP
+ * type ioctl.
+ */
+ if (err = g_get_limited_map(path_phys,
+ &limited_map, (Options & PVERBOSE))) {
+ (void) print_errString(err,
+ argv[path_index]);
+ exit(-1);
+ }
+ limited_map_flag++;
+ }
+
+ }
+
+ if (limited_map_flag) {
+ (void) fprintf(stdout,
+ MSGSTR(2093,
+ "Host Adapter AL_PA: %x\n"),
+ limited_map.lilp_myalpa);
+
+ (void) fprintf(stdout,
+ MSGSTR(2094,
+ "Pos AL_PA\n"));
+ for (i = 0; i < (uint_t)limited_map.lilp_length; i++) {
+ (void) fprintf(stdout, "%-3d %-2x\n",
+ i, limited_map.lilp_list[i]);
+ }
+ } else {
+ if ((err = g_dev_prop_lookup_bytes(map_root,
+ PORT_WWN_PROP, &count, &hba_port_wwn)) != 0) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((err = g_get_map_topology(
+ map_root, &map_topo)) != 0) {
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+
+ if ((map_dev = g_get_first_dev(map_root, &err)) == NULL) {
+ if (err == L_NO_SUCH_DEV_FOUND) {
+ g_dev_map_fini(map_root);
+ (void) fprintf(stderr,
+ MSGSTR(2308, " No devices are found on %s.\n"),
+ argv[path_index]);
+ exit(-1);
+ } else {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ }
+
+ switch (map_topo) {
+ case FC_TOP_FABRIC:
+ case FC_TOP_PUBLIC_LOOP:
+ case FC_TOP_PT_PT:
+ (void) fprintf(stdout,
+ MSGSTR(2095, "Pos Port_ID Hard_Addr Port WWN"
+ " Node WWN Type\n"));
+ while (map_dev) {
+ if ((err = g_dev_prop_lookup_ints(
+ map_dev, PORT_ADDR_PROP, &port_addr)) != 0) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((err = g_dev_prop_lookup_bytes(map_dev,
+ PORT_WWN_PROP, &count, &port_wwn)) != 0) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((err = g_dev_prop_lookup_bytes(map_dev,
+ NODE_WWN_PROP, &count, &node_wwn)) != 0) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((err = g_dev_prop_lookup_ints(
+ map_dev, HARD_ADDR_PROP, &hard_addr)) != 0) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ print_fabric_prop(pos++, port_wwn,
+ node_wwn, *port_addr, *hard_addr);
+ if ((err = g_dev_prop_lookup_bytes(map_dev,
+ INQ_DTYPE_PROP, &count, &dtype_prop)) != 0) {
+ (void) fprintf(stdout,
+ MSGSTR(2307, " Failed to get the type.\n"));
+ } else {
+ print_fabric_dtype_prop(hba_port_wwn, port_wwn,
+ *dtype_prop);
+ }
+
+ if (((map_dev = g_get_next_dev(
+ map_dev, &err)) == NULL) &&
+ (err != L_NO_SUCH_DEV_FOUND)) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ }
+ break;
+ case FC_TOP_PRIVATE_LOOP:
+ (void) fprintf(stdout,
+ MSGSTR(2295,
+ "Pos AL_PA ID Hard_Addr "
+ "Port WWN Node WWN Type\n"));
+
+ while (map_dev) {
+ if ((err = g_dev_prop_lookup_ints(
+ map_dev, PORT_ADDR_PROP, &port_addr)) != 0) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((err = g_dev_prop_lookup_bytes(map_dev,
+ PORT_WWN_PROP, &count, &port_wwn)) != 0) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((err = g_dev_prop_lookup_bytes(map_dev,
+ NODE_WWN_PROP, &count, &node_wwn)) != 0) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ if ((err = g_dev_prop_lookup_ints(
+ map_dev, HARD_ADDR_PROP, &hard_addr)) != 0) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ print_private_loop_prop(pos++, port_wwn,
+ node_wwn, *port_addr, *hard_addr);
+ if ((err = g_dev_prop_lookup_bytes(map_dev,
+ INQ_DTYPE_PROP, &count, &dtype_prop)) != 0) {
+ (void) fprintf(stdout,
+ MSGSTR(2307, " Failed to get the type.\n"));
+ } else {
+ print_private_loop_dtype_prop(hba_port_wwn,
+ port_wwn, *dtype_prop);
+ }
+
+ if (((map_dev = g_get_next_dev(
+ map_dev, &err)) == NULL) &&
+ (err != L_NO_SUCH_DEV_FOUND)) {
+ g_dev_map_fini(map_root);
+ (void) print_errString(err, argv[path_index]);
+ exit(-1);
+ }
+ }
+ break;
+ default:
+ (void) print_errString(L_UNEXPECTED_FC_TOPOLOGY,
+ argv[path_index]);
+ exit(-1);
+ }
+ g_dev_map_fini(map_root);
+ }
+ limited_map_flag = 0;
+ path_index++;
+ }
+}
+
+/*
+ * Gets a list of non-SENA fcal devices
+ * found on the system.
+ *
+ * OUTPUT:
+ * wwn_list pointer
+ * NULL: No non-enclosure devices found.
+ * !NULL: Devices found
+ * wwn_list points to a linked list of wwn's.
+ * RETURNS:
+ * 0 O.K.
+ */
+int
+n_get_non_encl_list(WWN_list **wwn_list_ptr, int verbose)
+{
+int i, j, k, err, found_ib = 0, pathcnt = 1;
+WWN_list *wwn_list;
+Box_list *b_list = NULL;
+gfc_map_t map;
+uchar_t box_id;
+gfc_port_dev_info_t *dev_addr_list;
+char phci_path[MAXPATHLEN], oldphci_path[MAXPATHLEN];
+mp_pathlist_t pathlist;
+
+
+ /*
+ * Only interested in devices that are not part of
+ * a Photon enclosure.
+ */
+ if ((err = l_get_box_list(&b_list, verbose)) != 0) {
+ return (err); /* Failure */
+ }
+
+ if (err = g_get_wwn_list(&wwn_list, verbose)) {
+ (void) l_free_box_list(&b_list);
+ return (err);
+ }
+
+ while (b_list != NULL) {
+
+ pathcnt = 1;
+ if (strstr(b_list->b_physical_path, SCSI_VHCI) != NULL) {
+ (void) strcpy(phci_path, b_list->b_physical_path);
+ if (err = g_get_pathlist(phci_path, &pathlist)) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+ pathcnt = pathlist.path_count;
+ }
+
+ for (k = 0; k < pathcnt; k++) {
+
+ if ((k > 0) && (strstr(oldphci_path,
+ pathlist.path_info[k].path_hba))) {
+ continue;
+ }
+
+ if (strstr(b_list->b_physical_path, SCSI_VHCI) == NULL) {
+ if ((err = g_get_dev_map(b_list->b_physical_path,
+ &map, verbose)) != 0) {
+ (void) g_free_wwn_list(&wwn_list);
+ (void) l_free_box_list(&b_list);
+ return (err);
+ }
+ } else {
+ (void) strcpy(phci_path,
+ pathlist.path_info[k].path_hba);
+ (void) strcpy(oldphci_path, phci_path);
+ (void) strcat(phci_path, FC_CTLR);
+ if (g_get_dev_map(phci_path, &map, verbose)) {
+ continue;
+ }
+ if (pathcnt == 1) {
+ free(pathlist.path_info);
+ }
+ }
+
+
+ switch (map.hba_addr.port_topology) {
+ case FC_TOP_FABRIC:
+ case FC_TOP_PUBLIC_LOOP:
+
+ for (i = 0, dev_addr_list = map.dev_addr;
+ i < map.count; i++, dev_addr_list++) {
+ for (found_ib = 1, j = 0; j < WWN_SIZE;
+ j++) {
+ if (b_list->b_node_wwn[j] !=
+ dev_addr_list->gfc_port_dev.
+ pub_port.dev_nwwn.raw_wwn[j]) {
+ found_ib = 0;
+ }
+ }
+ if (found_ib) {
+ (void) n_rem_list_entry_fabric(
+ dev_addr_list->gfc_port_dev.
+ pub_port.dev_did.port_id, &map,
+ &wwn_list);
+ }
+ }
+ break;
+
+ case FC_TOP_PRIVATE_LOOP:
+
+ for (i = 0, dev_addr_list = map.dev_addr;
+ i < map.count; i++, dev_addr_list++) {
+ for (found_ib = 1, j = 0; j < WWN_SIZE;
+ j++) {
+ if (b_list->b_node_wwn[j] !=
+ dev_addr_list->gfc_port_dev.
+ priv_port.sf_node_wwn[j]) {
+ found_ib = 0;
+ }
+ }
+ if (found_ib) {
+ box_id = g_sf_alpa_to_switch
+ [dev_addr_list->gfc_port_dev.
+ priv_port.sf_al_pa] &
+ BOX_ID_MASK;
+ /* This function has been added */
+ /* here only to keep from having */
+ /* to tab over farther */
+ (void) n_rem_list_entry(box_id, &map,
+ &wwn_list);
+ if (wwn_list == NULL) {
+ /* Return the list */
+ *wwn_list_ptr = NULL;
+ break;
+ }
+ }
+ }
+ break;
+ case FC_TOP_PT_PT:
+ (void) free((void *)map.dev_addr);
+ return (L_PT_PT_FC_TOP_NOT_SUPPORTED);
+ default:
+ (void) free((void *)map.dev_addr);
+ return (L_UNEXPECTED_FC_TOPOLOGY);
+ }
+ free((void *)map.dev_addr);
+
+ }
+ if (pathcnt > 1) {
+ free(pathlist.path_info);
+ }
+
+ b_list = b_list->box_next;
+ }
+ /* Return the list */
+ *wwn_list_ptr = wwn_list;
+ (void) l_free_box_list(&b_list);
+ return (0);
+}
+
+
+
+/*
+ * n_rem_list_entry() We found an IB so remove disks that
+ * are in the Photon from the individual device list.
+ *
+ * OUTPUT:
+ * wwn_list - removes the fcal disks that are in SENA enclosure
+ *
+ * RETURNS:
+ * none
+ */
+void
+n_rem_list_entry(uchar_t box_id, struct gfc_map *map,
+ struct wwn_list_struct **wwn_list)
+{
+int k;
+gfc_port_dev_info_t *dev_addr_list;
+
+ N_DPRINTF(" n_rem_list_entry: Removing devices"
+ " with box_id=0x%x from device list.\n", box_id);
+
+
+ for (k = 0, dev_addr_list = map->dev_addr; k < map->count;
+ k++, dev_addr_list++) {
+ if ((g_sf_alpa_to_switch[dev_addr_list->gfc_port_dev.
+ priv_port.sf_hard_address] & BOX_ID_MASK)
+ == box_id) {
+ n_rem_wwn_entry(dev_addr_list->gfc_port_dev.
+ priv_port.sf_node_wwn, wwn_list);
+ }
+ }
+
+}
+
+
+
+/*
+ * n_rem_list_entry_fabric() We found an IB so remove disks that
+ * are in the Photon from the individual device list.
+ *
+ * OUTPUT:
+ * wwn_list - removes the fcal disks that are in SENA enclosure
+ *
+ * RETURNS:
+ * none
+ */
+void
+n_rem_list_entry_fabric(int pa, struct gfc_map *map,
+ struct wwn_list_struct **wwn_list)
+{
+int k;
+gfc_port_dev_info_t *dev_addr_ptr;
+
+ N_DPRINTF(" n_rem_list_entry: Removing devices"
+ " with the same domain and area ID as"
+ " 0x%x PA from device list.\n", pa);
+ for (k = 0, dev_addr_ptr = map->dev_addr; k < map->count;
+ k++, dev_addr_ptr++) {
+
+ /* matching the domain and area id with input alpa, */
+ /* ignoring last 8 bits. */
+ if ((dev_addr_ptr->gfc_port_dev.pub_port.dev_did.port_id |
+ 0xff) == (pa | 0xff)) {
+ n_rem_wwn_entry(dev_addr_ptr->
+ gfc_port_dev.pub_port.dev_nwwn.raw_wwn,
+ wwn_list);
+ }
+ }
+}
+
+
+/*
+ * n_rem_wwn_entry() removes input wwn from wwn_list.
+ *
+ * OUTPUT:
+ * wwn_list - removes the input wwn from wwn_list if found.
+ *
+ * RETURNS:
+ * none
+ */
+void
+n_rem_wwn_entry(uchar_t node_wwn[], struct wwn_list_struct **wwn_list)
+{
+int l, found_dev;
+WWN_list *inner, *l1;
+
+ inner = *wwn_list;
+ while (inner != NULL) {
+ for (found_dev = 1, l = 0; l < WWN_SIZE; l++) {
+ if (inner->w_node_wwn[l] != node_wwn[l]) {
+ found_dev = 0;
+ }
+ }
+ if (found_dev) {
+ /* Remove this entry from the list */
+ if (inner->wwn_prev != NULL) {
+ inner->wwn_prev->wwn_next =
+ inner->wwn_next;
+ } else {
+ *wwn_list = inner->wwn_next;
+ }
+ if (inner->wwn_next != NULL) {
+ inner->wwn_next->wwn_prev =
+ inner->wwn_prev;
+ }
+ l1 = inner;
+ N_DPRINTF(" n_rem_wwn_entry: "
+ "Removing Logical=%s "
+ "Current=0x%x, "
+ "Prev=0x%x, Next=0x%x\n",
+ l1->logical_path,
+ l1,
+ l1->wwn_prev,
+ l1->wwn_next);
+ inner = inner->wwn_next;
+ if ((l1->wwn_prev == NULL) &&
+ (l1->wwn_next) == NULL) {
+ (void) free(l1->physical_path);
+ (void) free(l1->logical_path);
+ (void) free(l1);
+ *wwn_list = NULL;
+ N_DPRINTF(" n_rem_list_entry: "
+ "No non-Photon "
+ "devices left"
+ " in the list.\n");
+ return;
+ }
+ (void) free(l1->physical_path);
+ (void) free(l1->logical_path);
+ (void) free(l1);
+ } else {
+ inner = inner->wwn_next;
+ }
+ }
+}
+
+
+/*
+ * non_encl_probe() Finds and displays a list of
+ * non-SENA fcal devices which is found on the
+ * system.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+non_encl_probe()
+{
+WWN_list *wwn_list, *wwn_listh, *inner, *l1;
+int err = 0;
+char lun_a[MAXPATHLEN], lun_b[MAXPATHLEN], temppath[MAXPATHLEN];
+char *tempptra, *tempptrb, *tempptr;
+mp_pathlist_t pathlist;
+int compare_result, retr_outer = 0;
+ddi_devid_t devid1 = NULL, devid2 = NULL;
+di_node_t root = DI_NODE_NIL;
+
+ if (err = n_get_non_encl_list(&wwn_list, (Options & PVERBOSE))) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+
+ g_sort_wwn_list(&wwn_list);
+
+ wwn_listh = wwn_list;
+
+ if (wwn_list != NULL) {
+ if (wwn_list->wwn_next != NULL) {
+ (void) fprintf(stdout,
+ MSGSTR(2098, "\nFound Fibre Channel device(s):\n"));
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(2099, "\nFound Fibre Channel device:\n"));
+ }
+ } else {
+ return;
+ }
+
+ while (wwn_list != NULL) {
+ if (strstr(wwn_list->physical_path, SCSI_VHCI) != NULL) {
+ (void) strcpy(temppath, wwn_list->physical_path);
+ if ((!g_get_pathlist(temppath,
+ &pathlist)) &&
+ ((tempptra = strchr(pathlist.path_info[0].
+ path_addr, ','))) != NULL) {
+ tempptra++;
+ (void) strcpy(lun_a, tempptra);
+ free(pathlist.path_info);
+ }
+ } else {
+ if ((((tempptr = strstr(wwn_list->physical_path,
+ SLSH_DRV_NAME_ST)) != NULL) ||
+ ((tempptr = strstr(wwn_list->physical_path,
+ SLSH_DRV_NAME_SSD)) != NULL)) &&
+ ((tempptra = strchr(tempptr, ',')) != NULL)) {
+ tempptra++;
+ (void) strcpy(lun_a, tempptra);
+ }
+ }
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(90, "Node WWN:"));
+ (void) fprintf(stdout, "%s ", wwn_list->node_wwn_s);
+
+ if (wwn_list->device_type < 0x10) {
+ (void) fprintf(stdout, MSGSTR(35, "Device Type:"));
+ (void) fprintf(stdout, "%s",
+ dtype[wwn_list->device_type]);
+ } else if (wwn_list->device_type < 0x1f) {
+ (void) fprintf(stdout, MSGSTR(2100,
+ "Type:Reserved"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2101,
+ "Type:Unknown"));
+ }
+ (void) fprintf(stdout, "\n ");
+ (void) fprintf(stdout, MSGSTR(31, "Logical Path:%s"),
+ wwn_list->logical_path);
+ (void) fprintf(stdout, "\n");
+
+ if (Options & OPTION_P) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout,
+ MSGSTR(5, "Physical Path:"));
+ (void) fprintf(stdout, "\n %s\n", wwn_list->physical_path);
+ }
+ inner = wwn_list->wwn_next;
+
+ while (inner != NULL) {
+ if (strcmp(inner->node_wwn_s, wwn_list->node_wwn_s) == 0) {
+
+ if (tempptra != NULL) {
+ if (strstr(inner->physical_path,
+ SCSI_VHCI) != NULL) {
+ (void) strcpy(temppath,
+ inner->physical_path);
+
+ if ((!g_get_pathlist(temppath, &pathlist)) &&
+ ((tempptrb = strchr(
+ pathlist.path_info[0].path_addr, ','))) !=
+ NULL) {
+ tempptrb++;
+ (void) strcpy(lun_b, tempptrb);
+ free(pathlist.path_info);
+ }
+ } else {
+ if ((((tempptr = strstr(inner->physical_path,
+ SLSH_DRV_NAME_ST)) != NULL) ||
+ ((tempptr = strstr(inner->physical_path,
+ SLSH_DRV_NAME_SSD)) != NULL)) &&
+ ((tempptrb = strchr(tempptr, ',')) != NULL)) {
+ tempptrb++;
+ (void) strcpy(lun_b, tempptrb);
+ }
+ }
+ }
+
+ if (((tempptra == NULL) || (strcmp(lun_a, lun_b)) == 0)) {
+
+ /*
+ * Have we retrieved a snapshot yet?
+ */
+ if (root == DI_NODE_NIL) {
+ if ((root = di_init("/", DINFOCPYALL)) ==
+ DI_NODE_NIL) {
+ (void) fprintf(stdout,
+ MSGSTR(2319,
+ "\nFailed to get device tree snapshot:\n"));
+ exit(1);
+ }
+ }
+
+ /* Apply devid to ssd devices only */
+ if (!retr_outer && strstr(wwn_list->physical_path,
+ SLSH_DRV_NAME_SSD) != NULL) {
+ if ((err = g_devid_get(wwn_list->physical_path,
+ &devid1, root, SSD_DRVR_NAME)) != 0) {
+ (void) print_errString(err,
+ wwn_list->physical_path);
+ }
+ /*
+ * Try retrieve of devid only once. If it fails
+ * don't try it again but print error,
+ * There should be a devid prop.
+ */
+ retr_outer = 1;
+ }
+ /*
+ * Apply devid to block devices only.
+ * Get devid of inner path and compare
+ * with outer path's devid.
+ */
+ if ((strstr(inner->physical_path,
+ SLSH_DRV_NAME_SSD) != NULL) &&
+ devid1 != NULL) {
+
+ if ((err = g_devid_get(inner->physical_path,
+ &devid2, root, SSD_DRVR_NAME)) != 0) {
+
+ (void) print_errString(err,
+ inner->physical_path);
+ compare_result = 0;
+ } else {
+ compare_result = devid_compare(devid1, devid2);
+ }
+ } else {
+ /* devid isn't applied */
+ compare_result = 0;
+ }
+
+ if (compare_result == 0) {
+
+ if (strcmp(wwn_list->logical_path,
+ inner->logical_path)) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout,
+ MSGSTR(31, "Logical Path:%s"),
+ inner->logical_path);
+ (void) fprintf(stdout, "\n");
+
+ if (Options & OPTION_P) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(5,
+ "Physical Path:"));
+ (void) fprintf(stdout, "\n %s\n",
+ inner->physical_path);
+ }
+ }
+
+ /* Remove this entry from the list */
+ if (inner->wwn_prev != NULL) {
+ inner->wwn_prev->wwn_next =
+ inner->wwn_next;
+ }
+
+ if (inner->wwn_next != NULL) {
+ inner->wwn_next->wwn_prev =
+ inner->wwn_prev;
+ }
+ free(inner->physical_path);
+ free(inner->logical_path);
+ l1 = inner;
+ inner = inner->wwn_next;
+ (void) free(l1);
+
+ } else {
+ inner = inner->wwn_next;
+ } /* End if (compare_result == 0) */
+
+ } else {
+ inner = inner->wwn_next;
+ }
+ } else {
+ inner = inner->wwn_next;
+ }
+ devid2 = NULL;
+ }
+ wwn_list = wwn_list->wwn_next;
+ retr_outer = 0;
+ devid1 = NULL;
+ } /* End while (wwn_list != NULL) */
+
+ (void) g_free_wwn_list(&wwn_listh);
+ (void) di_fini(root);
+}
+
+void
+pho_probe()
+{
+
+Box_list *b_list, *o_list, *c_list;
+int multi_path_flag, multi_print_flag;
+int duplicate_names_found = 0, err = 0;
+
+ b_list = o_list = c_list = NULL;
+ if ((err = l_get_box_list(&b_list, Options & PVERBOSE)) != 0) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+ if (b_list == NULL) {
+ (void) fprintf(stdout,
+ MSGSTR(93, "No %s enclosures found "
+ "in /dev/es\n"), ENCLOSURE_PROD_NAME);
+ } else {
+ o_list = b_list;
+ if (b_list->box_next != NULL) {
+ (void) fprintf(stdout, MSGSTR(2102,
+ "Found Enclosure(s)"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2103, "Found Enclosure"));
+ }
+ (void) fprintf(stdout, ":\n");
+ while (b_list != NULL) {
+ /* Don't re-print multiple paths */
+ c_list = o_list;
+ multi_print_flag = 0;
+ while (c_list != b_list) {
+ if (strcmp(c_list->b_node_wwn_s,
+ b_list->b_node_wwn_s) == 0) {
+ multi_print_flag = 1;
+ break;
+ }
+ c_list = c_list->box_next;
+ }
+ if (multi_print_flag) {
+ b_list = b_list->box_next;
+ continue;
+ }
+ (void) fprintf(stdout,
+ MSGSTR(2104, "%s Name:%s Node WWN:%s "),
+ b_list->prod_id_s, b_list->b_name,
+ b_list->b_node_wwn_s);
+ /*
+ * Print logical path on same line if not multipathed.
+ */
+ multi_path_flag = 0;
+ c_list = o_list;
+ while (c_list != NULL) {
+ if ((c_list != b_list) &&
+ (strcmp(c_list->b_node_wwn_s,
+ b_list->b_node_wwn_s) == 0)) {
+ multi_path_flag = 1;
+ }
+ c_list = c_list->box_next;
+ }
+ if (multi_path_flag) {
+ (void) fprintf(stdout, "\n ");
+ }
+ (void) fprintf(stdout,
+ MSGSTR(31, "Logical Path:%s"),
+ b_list->logical_path);
+
+ if (Options & OPTION_P) {
+ (void) fprintf(stdout, "\n ");
+ (void) fprintf(stdout,
+ MSGSTR(5, "Physical Path:"));
+ (void) fprintf(stdout, "%s",
+ b_list->b_physical_path);
+ }
+ c_list = o_list;
+ while (c_list != NULL) {
+ if ((c_list != b_list) &&
+ (strcmp(c_list->b_node_wwn_s,
+ b_list->b_node_wwn_s) == 0)) {
+ (void) fprintf(stdout, "\n ");
+ (void) fprintf(stdout,
+ MSGSTR(31, "Logical Path:%s"),
+ c_list->logical_path);
+ if (Options & OPTION_P) {
+ (void) fprintf(stdout, "\n ");
+ (void) fprintf(stdout,
+ MSGSTR(5, "Physical Path:"));
+ (void) fprintf(stdout, "%s",
+ c_list->b_physical_path);
+ }
+ }
+ c_list = c_list->box_next;
+ }
+ (void) fprintf(stdout, "\n");
+ /* Check for duplicate names */
+ if (l_duplicate_names(o_list, b_list->b_node_wwn_s,
+ (char *)b_list->b_name,
+ Options & PVERBOSE)) {
+ duplicate_names_found++;
+ }
+ b_list = b_list->box_next;
+ }
+ }
+ if (duplicate_names_found) {
+ (void) fprintf(stdout,
+ MSGSTR(2105, "\nWARNING: There are enclosures with "
+ "the same names.\n"
+ "You can not use the \"enclosure\""
+ " name to specify these subsystems.\n"
+ "Please use the \"enclosure_name\""
+ " subcommand to select unique names.\n\n"));
+ }
+ (void) l_free_box_list(&b_list);
+}
+
+/*
+ * display_port_status() Prints the device's
+ * port status.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+display_port_status(int d_state_flag)
+{
+
+ if (d_state_flag & L_OPEN_FAIL) {
+ (void) fprintf(stdout, MSGSTR(28, "Open Failed"));
+ } else if (d_state_flag & L_NOT_READY) {
+ (void) fprintf(stdout, MSGSTR(20, "Not Ready"));
+ } else if (d_state_flag & L_NOT_READABLE) {
+ (void) fprintf(stdout, MSGSTR(88, "Not Readable"));
+ } else if (d_state_flag & L_SPUN_DWN_D) {
+ (void) fprintf(stdout, MSGSTR(68, "Spun Down"));
+ } else if (d_state_flag & L_SCSI_ERR) {
+ (void) fprintf(stdout, MSGSTR(70, "SCSI Error"));
+ } else if (d_state_flag & L_RESERVED) {
+ (void) fprintf(stdout, MSGSTR(73, "Reservation conflict"));
+ } else if (d_state_flag & L_NO_LABEL) {
+ (void) fprintf(stdout, MSGSTR(92, "No UNIX Label"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(29, "O.K."));
+ }
+ (void) fprintf(stdout, "\n");
+}
+
+/*
+ * Displays individual SENA
+ * FC disk information.
+ *
+ * Caller to this routine should free the storage due to
+ * the use of g_get_dev_map
+ *
+ * RETURNS:
+ * none.
+ */
+void
+display_fc_disk(struct path_struct *path_struct, char *ses_path,
+ gfc_map_t *map, L_inquiry inq, int verbose)
+{
+static WWN_list *wwn_list = NULL;
+static char path_phys[MAXPATHLEN];
+static L_disk_state l_disk_state;
+static L_inquiry local_inq;
+static uchar_t node_wwn[WWN_SIZE];
+char same_path_phys = B_FALSE; /* To chk for repeat args */
+uchar_t port_wwn[WWN_SIZE], *pg_buf;
+char logical_path[MAXPATHLEN];
+int al_pa, port_a_flag = 0;
+int offset, mode_data_avail = 0;
+int no_path_flag = 0, err = 0;
+L_state l_state;
+Mode_header_10 *mode_header_ptr = NULL;
+struct mode_page *pg_hdr;
+
+ /*
+ * Do a quick check to see if its the same path as in last call.
+ * path_phys is a static array and so dont worry about its
+ * initialization.
+ */
+ if (strcmp(path_phys, path_struct->p_physical_path) == 0)
+ same_path_phys = B_TRUE;
+
+ (void) strcpy(path_phys, path_struct->p_physical_path);
+ (void) memset((char *)logical_path, 0, sizeof (logical_path));
+
+ /*
+ * slot_valid is 1 when argument is of the form 'enclosure,[f|r]<n>'.
+ * If slot_valid != 1, g_get_dev_map and l_get_ses_path would
+ * already have been called
+ */
+ if (path_struct->slot_valid == 1) {
+ /* Get the location information. */
+ if (err = g_get_dev_map(path_phys, map, (Options & PVERBOSE))) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+ if (err = l_get_ses_path(path_phys, ses_path, map,
+ (Options & PVERBOSE))) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+ }
+
+ /*
+ * Get the WWN for our disk if we already haven't or if there was an
+ * error earlier
+ */
+ if (same_path_phys == B_FALSE) {
+ if (err = g_get_wwn(path_phys, port_wwn, node_wwn,
+ &al_pa, (Options & PVERBOSE))) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+
+ if (err = g_get_inquiry(ses_path, &local_inq)) {
+ (void) print_errString(err, ses_path);
+ exit(-1);
+ }
+ }
+
+ /*
+ * We are interested only a couple of ib_tbl fields and
+ * those get filled using l_get_ib_status.
+ * Note that NOT ALL of ib_tbl fields get filled here
+ */
+ if ((err = l_get_ib_status(ses_path, &l_state,
+ Options & PVERBOSE)) != 0) {
+ (void) print_errString(err, ses_path);
+ exit(-1);
+ }
+
+ /*
+ * Get path to all the FC disk and tape devices.
+ * if we haven't already done so in a previous pass
+ */
+ if ((wwn_list == NULL) && (err = g_get_wwn_list(&wwn_list, verbose))) {
+ (void) print_errString(err, ses_path);
+ exit(-1); /* Failure */
+ }
+
+ /*
+ * Get the disk status if it is a different path_phys from
+ * last time.
+ */
+ if (same_path_phys == B_FALSE) {
+ (void) memset(&l_disk_state, 0,
+ sizeof (struct l_disk_state_struct));
+ if (err = l_get_disk_status(path_phys, &l_disk_state,
+ wwn_list, (Options & PVERBOSE))) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+ }
+
+ if (l_disk_state.l_state_flag & L_NO_PATH_FOUND) {
+ (void) fprintf(stderr, MSGSTR(2106,
+ "\nWARNING: No path found "
+ "in /dev/rdsk directory\n"
+ " Please check the logical links in /dev/rdsk\n"
+ " (It may be necessary to run the \"disks\" "
+ "program.)\n\n"));
+
+ /* Just call to get the status directly. */
+ if (err = l_get_port(ses_path, &port_a_flag, verbose)) {
+ (void) print_errString(err, ses_path);
+ exit(-1);
+ }
+ if (err = l_get_disk_port_status(path_phys,
+ &l_disk_state, port_a_flag,
+ (Options & PVERBOSE))) {
+ (void) print_errString(err, path_phys);
+ exit(-1);
+ }
+ no_path_flag++;
+ }
+
+ if (strlen(l_disk_state.g_disk_state.node_wwn_s) == 0) {
+ (void) sprintf(l_disk_state.g_disk_state.node_wwn_s,
+ "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ node_wwn[0], node_wwn[1], node_wwn[2], node_wwn[3],
+ node_wwn[4], node_wwn[5], node_wwn[6], node_wwn[7]);
+ }
+
+ /* get mode page information for FC device */
+ if (l_get_mode_pg(path_phys, &pg_buf, Options & PVERBOSE) == 0) {
+ mode_header_ptr = (struct mode_header_10_struct *)(int)pg_buf;
+ pg_hdr = ((struct mode_page *)((int)pg_buf +
+ (uchar_t)sizeof (struct mode_header_10_struct) +
+ (uchar_t *)(uintptr_t)(mode_header_ptr->bdesc_length)));
+ offset = sizeof (struct mode_header_10_struct) +
+ mode_header_ptr->bdesc_length;
+ while (offset < (mode_header_ptr->length +
+ sizeof (mode_header_ptr->length)) &&
+ !mode_data_avail) {
+ if (pg_hdr->code == MODEPAGE_CACHING) {
+ mode_data_avail++;
+ break;
+ }
+ offset += pg_hdr->length + sizeof (struct mode_page);
+ pg_hdr = ((struct mode_page *)((int)pg_buf +
+ (uchar_t)offset));
+ }
+ }
+
+ switch ((inq.inq_dtype & DTYPE_MASK)) {
+ case DTYPE_DIRECT:
+ fprintf(stdout, MSGSTR(121, "DEVICE PROPERTIES for disk: %s\n"),
+ path_struct->argv);
+ break;
+ case DTYPE_SEQUENTIAL: /* Tape */
+ fprintf(stdout, MSGSTR(2249, "DEVICE PROPERTIES for tape: %s\n"),
+ path_struct->argv);
+ break;
+ default:
+ fprintf(stdout, MSGSTR(2250, "DEVICE PROPERTIES for: %s\n"),
+ path_struct->argv);
+ break;
+ }
+
+ if (l_disk_state.g_disk_state.port_a_valid) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(141, "Status(Port A):"));
+ (void) fprintf(stdout, "\t");
+ display_port_status(
+ l_disk_state.g_disk_state.d_state_flags[PORT_A]);
+ } else {
+ if (path_struct->f_flag) {
+ if ((ib_present_chk(&l_state, 0) == 1) &&
+ (l_state.drv_front[path_struct->slot].ib_status.bypass_a_en)) {
+ (void) fprintf(stdout,
+ MSGSTR(66,
+ " Status(Port A):\tBYPASSED\n"));
+ }
+ } else {
+ if ((ib_present_chk(&l_state, 0) == 1) &&
+ (l_state.drv_rear[path_struct->slot].ib_status.bypass_a_en)) {
+ (void) fprintf(stdout,
+ MSGSTR(66,
+ " Status(Port A):\tBYPASSED\n"));
+ }
+ }
+ }
+
+ if (l_disk_state.g_disk_state.port_b_valid) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(142, "Status(Port B):"));
+ (void) fprintf(stdout, "\t");
+ display_port_status(l_disk_state.g_disk_state.d_state_flags[PORT_B]);
+ } else {
+ if (path_struct->f_flag) {
+ if ((ib_present_chk(&l_state, 1) == 1) &&
+ (l_state.drv_front[path_struct->slot].ib_status.bypass_b_en)) {
+ (void) fprintf(stdout,
+ MSGSTR(65,
+ " Status(Port B):\tBYPASSED\n"));
+ }
+ } else {
+ if ((ib_present_chk(&l_state, 1) == 1) &&
+ (l_state.drv_rear[path_struct->slot].ib_status.bypass_b_en)) {
+ (void) fprintf(stdout,
+ MSGSTR(65,
+ " Status(Port B):\tBYPASSED\n"));
+ }
+ }
+ }
+
+ if (no_path_flag) {
+ (void) fprintf(stdout, " ");
+ if (port_a_flag != NULL) {
+ (void) fprintf(stdout, MSGSTR(142, "Status(Port B):"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(141, "Status(Port A):"));
+ }
+ (void) fprintf(stdout, "\t");
+ display_port_status(
+ l_disk_state.g_disk_state.d_state_flags[port_a_flag]);
+ } else if ((!l_disk_state.g_disk_state.port_a_valid) &&
+ (!l_disk_state.g_disk_state.port_b_valid)) {
+ (void) fprintf(stdout, MSGSTR(2107, " Status:\t\t"
+ "No state available.\n"));
+ }
+
+ (void) display_disk_info(inq, l_disk_state, path_struct, pg_hdr,
+ mode_data_avail, (char *)local_inq.inq_box_name, verbose);
+}
+
+
+
+
+
+/*
+ * non_encl_fc_disk_display() Prints the device specific
+ * information for an individual fcal device.
+ *
+ * RETURNS:
+ * none.
+ */
+static int
+non_encl_fc_disk_display(Path_struct *path_struct,
+ L_inquiry inq_struct, int verbose)
+{
+
+char phys_path[MAXPATHLEN];
+uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE], *pg_buf = NULL;
+L_disk_state l_disk_state;
+struct dlist *mlist;
+int i = 0, al_pa, offset, mode_data_avail = 0, err = 0;
+int path_a_found = 0, path_b_found = 0, argpwwn = 0,
+ argnwwn = 0, pathcnt = 1;
+L_inquiry local_inq;
+Mode_header_10 *mode_header_ptr;
+struct mode_page *pg_hdr;
+WWN_list *wwn_list, *wwn_list_ptr, *list_start;
+char temppath[MAXPATHLEN], last_logical_path[MAXPATHLEN];
+mp_pathlist_t pathlist;
+
+ (void) strcpy(phys_path, path_struct->p_physical_path);
+
+ /* Get path to all the FC disk and tape devices. */
+ if (err = g_get_wwn_list(&wwn_list, verbose)) {
+ return (err);
+ }
+
+ g_sort_wwn_list(&wwn_list);
+
+ list_start = wwn_list;
+ (void) strcpy(last_logical_path, phys_path);
+
+ for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
+ wwn_list_ptr = wwn_list_ptr->wwn_next) {
+ if (strcasecmp(wwn_list_ptr->port_wwn_s,
+ path_struct->argv) == 0) {
+ list_start = wwn_list_ptr;
+ argpwwn = 1;
+ break;
+ } else if (strcasecmp(wwn_list_ptr->node_wwn_s,
+ path_struct->argv) == 0) {
+ list_start = wwn_list_ptr;
+ argnwwn = 1;
+ break;
+ }
+ }
+
+ for (wwn_list_ptr = list_start; wwn_list_ptr != NULL;
+ wwn_list_ptr = wwn_list_ptr->wwn_next) {
+
+
+ if (argpwwn) {
+ if (strcasecmp(wwn_list_ptr->port_wwn_s,
+ path_struct->argv) != 0) {
+ continue;
+ }
+ (void) strcpy(phys_path, wwn_list_ptr->physical_path);
+ path_a_found = 0;
+ path_b_found = 0;
+ mode_data_avail = 0;
+ } else if (argnwwn) {
+ if (strstr(wwn_list_ptr->logical_path,
+ last_logical_path) != NULL) {
+ continue;
+ }
+ if (strcasecmp(wwn_list_ptr->node_wwn_s,
+ path_struct->argv) != 0) {
+ continue;
+ }
+ (void) strcpy(phys_path, wwn_list_ptr->physical_path);
+ (void) strcpy(last_logical_path,
+ wwn_list_ptr->logical_path);
+ path_a_found = 0;
+ path_b_found = 0;
+ mode_data_avail = 0;
+ }
+
+ (void) memset(&l_disk_state, 0, sizeof (struct l_disk_state_struct));
+
+ if ((err = g_get_multipath(phys_path,
+ &(l_disk_state.g_disk_state.multipath_list),
+ wwn_list, verbose)) != 0) {
+ return (err);
+ }
+ mlist = l_disk_state.g_disk_state.multipath_list;
+ if (mlist == NULL) {
+ l_disk_state.l_state_flag = L_NO_PATH_FOUND;
+ N_DPRINTF(" non_encl_fc_disk_display: Error finding"
+ " multiple paths to the disk.\n");
+ (void) g_free_wwn_list(&wwn_list);
+ return (-1);
+ }
+
+ /* get mode page information for FC device */
+ if (l_get_mode_pg(phys_path, &pg_buf, verbose) == 0) {
+ mode_header_ptr = (struct mode_header_10_struct *)(int)pg_buf;
+ pg_hdr = ((struct mode_page *)((int)pg_buf +
+ (uchar_t)sizeof (struct mode_header_10_struct) +
+ (uchar_t *)(uintptr_t)(mode_header_ptr->bdesc_length)));
+ offset = sizeof (struct mode_header_10_struct) +
+ mode_header_ptr->bdesc_length;
+ while (offset < (mode_header_ptr->length +
+ sizeof (mode_header_ptr->length)) &&
+ !mode_data_avail) {
+ if (pg_hdr->code == MODEPAGE_CACHING) {
+ mode_data_avail++;
+ break;
+ }
+ offset += pg_hdr->length + sizeof (struct mode_page);
+ pg_hdr = ((struct mode_page *)((int)pg_buf +
+ (uchar_t)offset));
+ }
+ }
+
+ switch ((inq_struct.inq_dtype & DTYPE_MASK)) {
+ case DTYPE_DIRECT:
+ fprintf(stdout, MSGSTR(121, "DEVICE PROPERTIES for disk: %s\n"),
+ path_struct->argv);
+ break;
+ case DTYPE_SEQUENTIAL: /* Tape */
+ fprintf(stdout, MSGSTR(2249, "DEVICE PROPERTIES for tape: %s\n"),
+ path_struct->argv);
+ break;
+ default:
+ fprintf(stdout, MSGSTR(2250, "DEVICE PROPERTIES for: %s\n"),
+ path_struct->argv);
+ break;
+ }
+ while ((mlist != NULL) && (!(path_a_found && path_b_found))) {
+ (void) strcpy(phys_path, mlist->dev_path);
+ if (err = g_get_inquiry(phys_path, &local_inq)) {
+ (void) fprintf(stderr,
+ MSGSTR(2114,
+ "non_encl_fc_disk_display: Inquiry failed\n"));
+ (void) print_errString(err, phys_path);
+ (void) g_free_multipath(
+ l_disk_state.g_disk_state.multipath_list);
+ (void) g_free_wwn_list(&wwn_list);
+ return (-1);
+ }
+ if ((err = g_get_wwn(mlist->dev_path, port_wwn, node_wwn,
+ &al_pa, verbose)) != 0) {
+ (void) print_errString(err, mlist->dev_path);
+ (void) g_free_multipath(
+ l_disk_state.g_disk_state.multipath_list);
+ (void) g_free_wwn_list(&wwn_list);
+ return (-1);
+ }
+ if (strlen(l_disk_state.g_disk_state.node_wwn_s) == 0) {
+ (void) sprintf(l_disk_state.g_disk_state.node_wwn_s,
+ "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ node_wwn[0], node_wwn[1], node_wwn[2], node_wwn[3],
+ node_wwn[4], node_wwn[5], node_wwn[6], node_wwn[7]);
+ }
+ if ((err = l_get_disk_port_status(phys_path, &l_disk_state,
+ (local_inq.inq_port) ? FC_PORT_B : FC_PORT_A,
+ verbose)) != 0) {
+ (void) print_errString(err, phys_path);
+ (void) g_free_multipath(
+ l_disk_state.g_disk_state.multipath_list);
+ exit(-1);
+ }
+
+ if ((!local_inq.inq_port) && (!path_a_found)) {
+ (void) sprintf(l_disk_state.g_disk_state.port_a_wwn_s,
+ "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ port_wwn[0], port_wwn[1], port_wwn[2], port_wwn[3],
+ port_wwn[4], port_wwn[5], port_wwn[6], port_wwn[7]);
+ path_a_found = l_disk_state.g_disk_state.port_a_valid = 1;
+ }
+ if ((local_inq.inq_port) && (!path_b_found)) {
+ path_b_found = l_disk_state.g_disk_state.port_b_valid = 1;
+ (void) sprintf(l_disk_state.g_disk_state.port_b_wwn_s,
+ "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ port_wwn[0], port_wwn[1], port_wwn[2], port_wwn[3],
+ port_wwn[4], port_wwn[5], port_wwn[6], port_wwn[7]);
+ }
+
+ if ((strstr(mlist->dev_path, SCSI_VHCI) != NULL) &&
+ (!l_get_disk_port_status(phys_path, &l_disk_state,
+ (!local_inq.inq_port) ? FC_PORT_B : FC_PORT_A,
+ verbose))) {
+ (void) strcpy(temppath, mlist->dev_path);
+ if (err = g_get_pathlist(temppath, &pathlist)) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+ pathcnt = pathlist.path_count;
+ if (pathcnt > 1) {
+ for (i = 0; i < pathcnt; i++) {
+ if ((!path_a_found) &&
+ (path_b_found) &&
+ (strstr(pathlist.path_info[i].
+ path_addr,
+ l_disk_state.g_disk_state.
+ port_b_wwn_s) == NULL)) {
+
+ (void) strncpy(l_disk_state.
+ g_disk_state.port_a_wwn_s,
+ pathlist.path_info[i].
+ path_addr, 16);
+ path_a_found = l_disk_state.
+ g_disk_state.port_a_valid = 1;
+ }
+ if ((path_a_found) &&
+ (!path_b_found) &&
+ (strstr(pathlist.path_info[i].
+ path_addr,
+ l_disk_state.g_disk_state.
+ port_a_wwn_s) == NULL)) {
+
+ (void) strncpy(l_disk_state.
+ g_disk_state.port_b_wwn_s,
+ pathlist.path_info[i].
+ path_addr, 16);
+ path_b_found = l_disk_state.
+ g_disk_state.port_b_valid = 1;
+ }
+ if ((path_a_found) && (path_b_found)) {
+ break;
+ }
+ }
+ }
+ free(pathlist.path_info);
+ }
+
+ mlist = mlist->next;
+ }
+
+ if (l_disk_state.g_disk_state.port_a_valid) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(141, "Status(Port A):"));
+ (void) fprintf(stdout, "\t");
+ display_port_status(l_disk_state.g_disk_state.d_state_flags[FC_PORT_A]);
+ }
+
+ if (l_disk_state.g_disk_state.port_b_valid) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(142, "Status(Port B):"));
+ (void) fprintf(stdout, "\t");
+ display_port_status(l_disk_state.g_disk_state.d_state_flags[FC_PORT_B]);
+ }
+
+ (void) display_disk_info(local_inq, l_disk_state, path_struct,
+ pg_hdr, mode_data_avail, NULL, verbose);
+ (void) g_free_multipath(l_disk_state.g_disk_state.multipath_list);
+
+ if (!(argpwwn || argnwwn)) {
+ break;
+ }
+
+ }
+ (void) g_free_wwn_list(&wwn_list);
+ return (0);
+}
+
+
+
+/*
+ * display_disk_info() Prints the device specific information
+ * for any FC_AL disk device.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+display_disk_info(L_inquiry inq, L_disk_state l_disk_state,
+ Path_struct *path_struct, struct mode_page *pg_hdr,
+ int mode_data_avail, char *name_buf, int options)
+{
+float num_blks;
+struct dlist *mlist;
+int port_a, port_b;
+struct my_mode_caching *pg8_buf;
+L_inquiry enc_inq;
+char *enc_phys_path;
+Path_struct *enc_path_struct;
+int enc_type = 0;
+L_inquiry80 inq80;
+size_t serial_len;
+int err;
+
+ serial_len = sizeof (inq80.inq_serial);
+ err = g_get_serial_number(path_struct->p_physical_path,
+ inq80.inq_serial, &serial_len);
+ if (err) {
+ fprintf(stderr, "\n");
+ print_errString(err, path_struct->p_physical_path);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(3, "Vendor:"));
+ (void) fprintf(stdout, "\t\t");
+ print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
+
+ (void) fprintf(stdout, MSGSTR(2115, "\n Product ID:\t\t"));
+ print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
+
+ (void) fprintf(stdout, MSGSTR(2116, "\n WWN(Node):\t\t%s"),
+ l_disk_state.g_disk_state.node_wwn_s);
+
+ if (l_disk_state.g_disk_state.port_a_valid) {
+ (void) fprintf(stdout, MSGSTR(2117, "\n WWN(Port A):\t\t%s"),
+ l_disk_state.g_disk_state.port_a_wwn_s);
+ }
+ if (l_disk_state.g_disk_state.port_b_valid) {
+ (void) fprintf(stdout, MSGSTR(2118, "\n WWN(Port B):\t\t%s"),
+ l_disk_state.g_disk_state.port_b_wwn_s);
+ }
+ (void) fprintf(stdout, "\n ");
+ (void) fprintf(stdout, MSGSTR(2119, "Revision:"));
+ (void) fprintf(stdout, "\t\t");
+ print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
+
+ (void) fprintf(stdout, "\n ");
+ (void) fprintf(stdout, MSGSTR(17, "Serial Num:"));
+ (void) fprintf(stdout, "\t\t");
+ print_chars(inq80.inq_serial, serial_len, 0);
+ num_blks = l_disk_state.g_disk_state.num_blocks;
+ if (num_blks) {
+ num_blks /= 2048; /* get Mbytes */
+ (void) fprintf(stdout, "\n ");
+ (void) fprintf(stdout,
+ MSGSTR(60,
+ "Unformatted capacity:\t%6.3f MBytes"), num_blks);
+ }
+ (void) fprintf(stdout, "\n");
+
+ if (l_disk_state.g_disk_state.persistent_reserv_flag) {
+ (void) fprintf(stdout,
+ MSGSTR(2120, " Persistent Reserve:\t"));
+ if (l_disk_state.g_disk_state.persistent_active) {
+ (void) fprintf(stdout,
+ MSGSTR(39, "Active"));
+ (void) fprintf(stdout, "\n");
+ }
+ if (l_disk_state.g_disk_state.persistent_registered) {
+ (void) fprintf(stdout,
+ MSGSTR(2121, "Found Registered Keys"));
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(87, "Not being used"));
+ }
+ (void) fprintf(stdout, "\n");
+ }
+
+ if ((mode_data_avail) && (pg_hdr->code == MODEPAGE_CACHING)) {
+ pg8_buf = (struct my_mode_caching *)(int)pg_hdr;
+ if (pg8_buf->wce) {
+ (void) fprintf(stdout,
+ MSGSTR(2122,
+ " Write Cache:\t\t"
+ "Enabled\n"));
+ }
+ if (pg8_buf->rcd == 0) {
+ (void) fprintf(stdout,
+ MSGSTR(2123,
+ " Read Cache:\t\t"
+ "Enabled\n"));
+ (void) fprintf(stdout,
+ MSGSTR(2320,
+ " Minimum prefetch:"
+ "\t0x%x\n"
+ " Maximum prefetch:"
+ "\t0x%x\n"),
+ pg8_buf->min_prefetch,
+ pg8_buf->max_prefetch);
+ }
+ }
+
+ /*
+ * When /dev/rdsk/cxtxdxsx form of input is specified
+ * for display command the initial library version didn't
+ * display Location information. The change is made
+ * to display the same Location info as the non-library version.
+ */
+
+ if (name_buf != NULL) {
+ fprintf(stdout, MSGSTR(2125, " Location:\t\t"));
+ if (path_struct->slot_valid) {
+ /*
+ * We have to do another inquiry on the enclosure (name_buf)
+ * to determine if this device is within a daktari, or
+ * a two sided device.
+ */
+ if (!l_convert_name(name_buf, &enc_phys_path,
+ &enc_path_struct, 0)) {
+ if (!g_get_inquiry(enc_phys_path, &enc_inq)) {
+ enc_type = l_get_enc_type(enc_inq);
+ }
+ }
+ /* If either of the above fail, we just assume the default */
+ free(enc_phys_path);
+ free(enc_path_struct);
+ if (enc_type == DAK_ENC_TYPE) {
+ if (path_struct->f_flag) {
+ (void) fprintf(stdout, MSGSTR(2239,
+ "In slot %d in the enclosure named: %s\n"),
+ path_struct->slot, name_buf);
+ } else {
+ (void) fprintf(stdout, MSGSTR(2239,
+ "In slot %d in the enclosure named: %s\n"),
+ path_struct->slot + (MAX_DRIVES_DAK/2),
+ name_buf);
+ }
+ } else { /* Default enclosure type */
+ (void) fprintf(stdout, path_struct->f_flag ?
+ MSGSTR(2126,
+ "In slot %d in the Front of the enclosure named: %s\n")
+ : MSGSTR(2127,
+ "In slot %d in the Rear of the enclosure named: %s\n"),
+ path_struct->slot, name_buf);
+ }
+ } else {
+ (void) fprintf(stdout, MSGSTR(2228,
+ "In the enclosure named: %s\n"),
+ name_buf);
+ }
+ }
+
+ (void) fprintf(stdout, " %s\t\t%s\n",
+ MSGSTR(35, "Device Type:"),
+ dtype[inq.inq_dtype & DTYPE_MASK]);
+
+ mlist = l_disk_state.g_disk_state.multipath_list;
+ (void) fprintf(stdout, MSGSTR(2128, " Path(s):\n"));
+ if (strstr(mlist->dev_path, SCSI_VHCI) != NULL) {
+ (void) fprintf(stdout, " %s\n %s\n",
+ mlist->logical_path, mlist->dev_path);
+ (void) adm_print_pathlist(mlist->dev_path);
+ } else {
+ while (mlist) {
+ (void) fprintf(stdout, " %s\n %s\n",
+ mlist->logical_path, mlist->dev_path);
+ mlist = mlist->next;
+ }
+ }
+
+ if (Options & OPTION_V) {
+ if (path_struct->slot_valid) {
+ port_a = PORT_A;
+ port_b = PORT_B;
+ } else {
+ port_a = FC_PORT_A;
+ port_b = FC_PORT_B;
+ }
+ /* Only bother if the state is O.K. */
+ if ((l_disk_state.g_disk_state.port_a_valid) &&
+ (l_disk_state.g_disk_state.d_state_flags[port_a] == 0))
+ adm_display_verbose_disk(path_struct->p_physical_path, options);
+ else if ((l_disk_state.g_disk_state.port_b_valid) &&
+ (l_disk_state.g_disk_state.d_state_flags[port_b] == 0))
+ adm_display_verbose_disk(path_struct->p_physical_path, options);
+ }
+ (void) fprintf(stdout, "\n");
+
+}
+
+
+
+/*
+ * temp_decode() Display temperature bytes 1-3 state.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+temp_decode(Temp_elem_st *temp)
+{
+ if (temp->ot_fail) {
+ (void) fprintf(stdout, MSGSTR(2129,
+ ": FAILURE - Over Temperature"));
+ }
+ if (temp->ut_fail) {
+ (void) fprintf(stdout, MSGSTR(2130,
+ ": FAILURE - Under Temperature"));
+ }
+ if (temp->ot_warn) {
+ (void) fprintf(stdout, MSGSTR(2131,
+ ": WARNING - Over Temperature"));
+ }
+ if (temp->ut_warn) {
+ (void) fprintf(stdout, MSGSTR(2132,
+ ": WARNING - Under Temperature"));
+ }
+}
+
+
+
+/*
+ * disp_degree() Display temperature in Degrees Celsius.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+disp_degree(Temp_elem_st *temp)
+{
+int t;
+
+ t = temp->degrees;
+ t -= 20; /* re-adjust */
+ /*
+ * NL_Comment
+ * The %c is the degree symbol.
+ */
+ (void) fprintf(stdout, ":%1.2d%cC ", t, 186);
+}
+
+
+
+/*
+ * trans_decode() Display tranceivers state.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+trans_decode(Trans_elem_st *trans)
+{
+ if (trans->disabled) {
+ (void) fprintf(stdout, ": ");
+ (void) fprintf(stdout, MSGSTR(34,
+ "Disabled"));
+ }
+ if (trans->lol) {
+ (void) fprintf(stdout, MSGSTR(2133,
+ ": Not receiving a signal"));
+ }
+ if (trans->lsr_fail) {
+ (void) fprintf(stdout, MSGSTR(2134,
+ ": Laser failed"));
+ }
+}
+
+
+
+/*
+ * trans_messages() Display tranceiver status.
+ *
+ * NOTE: The decoding of the status assumes that the elements
+ * are in order with the first two elements are for the
+ * "A" IB. It also assumes the tranceivers are numbered
+ * 0 and 1.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+trans_messages(struct l_state_struct *l_state, int ib_a_flag)
+{
+Trans_elem_st trans;
+int i, j, k;
+int count = 0;
+int elem_index = 0;
+
+ /* Get and print messages */
+ for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) {
+ elem_index++;
+ if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_FL) {
+
+ if (l_state->ib_tbl.config.type_hdr[i].text_len != 0) {
+ (void) fprintf(stdout, "\n\t\t%s\n",
+ l_state->ib_tbl.config.text[i]);
+ }
+ count = k = 0;
+
+ for (j = 0; j <
+ (int)l_state->ib_tbl.config.type_hdr[i].num; j++) {
+ /*
+ * Only display the status for the selected IB.
+ */
+ if ((count < 2 && ib_a_flag) ||
+ (count >= 2 && !ib_a_flag)) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + j],
+ (void *)&trans, sizeof (trans));
+
+ if (k == 0) {
+ (void) fprintf(stdout, "\t\t%d ", k);
+ } else {
+ (void) fprintf(stdout, "\n\t\t%d ", k);
+ }
+ if (trans.code == S_OK) {
+ (void) fprintf(stdout,
+ MSGSTR(29, "O.K."));
+ revision_msg(l_state, elem_index + j);
+ } else if ((trans.code == S_CRITICAL) ||
+ (trans.code == S_NONCRITICAL)) {
+ (void) fprintf(stdout,
+ MSGSTR(2135, "Failed"));
+ revision_msg(l_state, elem_index + j);
+ trans_decode(&trans);
+ } else if (trans.code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(30, "Not Installed"));
+ } else if (trans.code == S_NOT_AVAILABLE) {
+ (void) fprintf(stdout,
+ MSGSTR(34, "Disabled"));
+ revision_msg(l_state, elem_index + j);
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(4, "Unknown status"));
+ }
+ k++;
+ }
+ count++;
+ }
+ }
+ /*
+ * Calculate the index to each element.
+ */
+ elem_index += l_state->ib_tbl.config.type_hdr[i].num;
+ }
+ (void) fprintf(stdout, "\n");
+}
+
+
+
+/*
+ * temperature_messages() Display temperature status.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+temperature_messages(struct l_state_struct *l_state, int rear_flag)
+{
+Temp_elem_st temp;
+int i, j, last_ok = 0;
+int all_ok = 1;
+int elem_index = 0;
+
+ /* Get and print messages */
+ for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) {
+ elem_index++; /* skip global */
+ if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_TS) {
+ if (!rear_flag) {
+ rear_flag = 1; /* only do front or rear backplane */
+ if (l_state->ib_tbl.config.type_hdr[i].text_len != 0) {
+ (void) fprintf(stdout, "\t %s",
+ l_state->ib_tbl.config.text[i]);
+ }
+
+ /*
+ * Check global status and if not all O.K.
+ * then print individually.
+ */
+ (void) bcopy((const void *)&l_state->ib_tbl.p2_s.element[i],
+ (void *)&temp, sizeof (temp));
+ for (j = 0; j <
+ (int)l_state->ib_tbl.config.type_hdr[i].num; j++) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + j],
+ (void *)&temp, sizeof (temp));
+
+ if ((j == 0) && (temp.code == S_OK) &&
+ (!(temp.ot_fail || temp.ot_warn ||
+ temp.ut_fail || temp.ut_warn))) {
+ (void) fprintf(stdout, "\n\t %d", j);
+ } else if ((j == 6) && (temp.code == S_OK) &&
+ all_ok) {
+ (void) fprintf(stdout, "\n\t %d", j);
+ } else if (last_ok && (temp.code == S_OK)) {
+ (void) fprintf(stdout, "%d", j);
+ } else {
+ (void) fprintf(stdout, "\n\t\t%d", j);
+ }
+ if (temp.code == S_OK) {
+ disp_degree(&temp);
+ if (temp.ot_fail || temp.ot_warn ||
+ temp.ut_fail || temp.ut_warn) {
+ temp_decode(&temp);
+ all_ok = 0;
+ last_ok = 0;
+ } else {
+ last_ok++;
+ }
+ } else if (temp.code == S_CRITICAL) {
+ (void) fprintf(stdout,
+ MSGSTR(122, "Critical failure"));
+ last_ok = 0;
+ all_ok = 0;
+ } else if (temp.code == S_NONCRITICAL) {
+ (void) fprintf(stdout,
+ MSGSTR(89, "Non-Critical Failure"));
+ last_ok = 0;
+ all_ok = 0;
+ } else if (temp.code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(30, "Not Installed"));
+ last_ok = 0;
+ all_ok = 0;
+ } else if (temp.code == S_NOT_AVAILABLE) {
+ (void) fprintf(stdout,
+ MSGSTR(34, "Disabled"));
+ last_ok = 0;
+ all_ok = 0;
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(4, "Unknown status"));
+ last_ok = 0;
+ all_ok = 0;
+ }
+ }
+ if (all_ok) {
+ (void) fprintf(stdout,
+ MSGSTR(2136, " (All temperatures are "
+ "NORMAL.)"));
+ }
+ all_ok = 1;
+ (void) fprintf(stdout, "\n");
+ } else {
+ rear_flag = 0;
+ }
+ }
+ elem_index += l_state->ib_tbl.config.type_hdr[i].num;
+ }
+}
+
+
+
+/*
+ * ib_decode() Display IB byte 3 state.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+ib_decode(Ctlr_elem_st *ctlr)
+{
+ if (ctlr->overtemp_alart) {
+ (void) fprintf(stdout, MSGSTR(2137,
+ " - IB Over Temperature Alert "));
+ }
+ if (ctlr->ib_loop_1_fail) {
+ (void) fprintf(stdout, MSGSTR(2138,
+ " - IB Loop 1 has failed "));
+ }
+ if (ctlr->ib_loop_0_fail) {
+ (void) fprintf(stdout, MSGSTR(2139,
+ " - IB Loop 0 has failed "));
+ }
+}
+
+
+
+/*
+ * mb_messages() Display motherboard
+ * (interconnect assembly) messages.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+mb_messages(struct l_state_struct *l_state, int index, int elem_index)
+{
+int j;
+Interconnect_st interconnect;
+
+ if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) {
+ (void) fprintf(stdout, "%s\n",
+ l_state->ib_tbl.config.text[index]);
+ }
+ for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num;
+ j++) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + j],
+ (void *)&interconnect, sizeof (interconnect));
+ (void) fprintf(stdout, "\t");
+
+ if (interconnect.code == S_OK) {
+ (void) fprintf(stdout,
+ MSGSTR(29, "O.K."));
+ revision_msg(l_state, elem_index + j);
+ } else if (interconnect.code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(30, "Not Installed"));
+ } else if (interconnect.code == S_CRITICAL) {
+ if (interconnect.eprom_fail != NULL) {
+ (void) fprintf(stdout, MSGSTR(2140,
+ "Critical Failure: EEPROM failure"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2141,
+ "Critical Failure: Unknown failure"));
+ }
+ revision_msg(l_state, elem_index + j);
+ } else if (interconnect.code == S_NONCRITICAL) {
+ if (interconnect.eprom_fail != NULL) {
+ (void) fprintf(stdout, MSGSTR(2142,
+ "Non-Critical Failure: EEPROM failure"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2143,
+ "Non-Critical Failure: Unknown failure"));
+ }
+ revision_msg(l_state, elem_index + j);
+ } else if (interconnect.code == S_NOT_AVAILABLE) {
+ (void) fprintf(stdout,
+ MSGSTR(34, "Disabled"));
+ revision_msg(l_state, elem_index + j);
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(4, "Unknown status"));
+ }
+ (void) fprintf(stdout, "\n");
+ }
+
+
+}
+
+
+
+/*
+ * back_plane_messages() Display back_plane messages
+ * including the temperature's.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+back_plane_messages(struct l_state_struct *l_state, int index, int elem_index)
+{
+Bp_elem_st bp;
+int j;
+char status_string[MAXPATHLEN];
+
+ if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) {
+ (void) fprintf(stdout, "%s\n",
+ l_state->ib_tbl.config.text[index]);
+ }
+ for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num;
+ j++) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + j],
+ (void *)&bp, sizeof (bp));
+ if (j == 0) {
+ (void) fprintf(stdout,
+ MSGSTR(2144, "\tFront Backplane: "));
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(2145, "\tRear Backplane: "));
+ }
+
+ (void) l_element_msg_string(bp.code, status_string);
+ (void) fprintf(stdout, "%s", status_string);
+
+ if (bp.code != S_NOT_INSTALLED) {
+ revision_msg(l_state, elem_index + j);
+ if ((bp.byp_a_enabled || bp.en_bypass_a) &&
+ !(bp.byp_b_enabled || bp.en_bypass_b)) {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(130, "Bypass A enabled"));
+ (void) fprintf(stdout, ")");
+ } else if ((bp.byp_b_enabled || bp.en_bypass_b) &&
+ !(bp.byp_a_enabled || bp.en_bypass_a)) {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(129, "Bypass B enabled"));
+ (void) fprintf(stdout, ")");
+ /* This case covers where a and b are bypassed */
+ } else if (bp.byp_b_enabled || bp.en_bypass_b) {
+ (void) fprintf(stdout,
+ MSGSTR(2146, " (Bypass's A & B enabled)"));
+ }
+ (void) fprintf(stdout, "\n");
+ temperature_messages(l_state, j);
+ } else {
+ (void) fprintf(stdout, "\n");
+ }
+ }
+}
+
+
+/*
+ * dpm_SSC100_messages() Display SSC100 messages
+ * including the temperature's.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+dpm_SSC100_messages(struct l_state_struct *l_state, int index, int elem_index)
+{
+Bp_elem_st bp;
+int j;
+char status_string[MAXPATHLEN];
+
+ if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) {
+ (void) fprintf(stdout, "%s\n",
+ l_state->ib_tbl.config.text[index]);
+ }
+ for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num;
+ j++) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + j],
+ (void *)&bp, sizeof (bp));
+ (void) fprintf(stdout, MSGSTR(2246, " SSC100 #%d: "), j);
+
+ (void) l_element_msg_string(bp.code, status_string);
+ (void) fprintf(stdout, "%s", status_string);
+
+ if (bp.code != S_NOT_INSTALLED) {
+ revision_msg(l_state, elem_index + j);
+ if ((bp.byp_a_enabled || bp.en_bypass_a) &&
+ !(bp.byp_b_enabled || bp.en_bypass_b)) {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(130, "Bypass A enabled"));
+ (void) fprintf(stdout, ")");
+ } else if ((bp.byp_b_enabled || bp.en_bypass_b) &&
+ !(bp.byp_a_enabled || bp.en_bypass_a)) {
+ (void) fprintf(stdout, " (");
+ (void) fprintf(stdout,
+ MSGSTR(129, "Bypass B enabled"));
+ (void) fprintf(stdout, ")");
+ /* This case covers where a and b are bypassed */
+ } else if (bp.byp_b_enabled || bp.en_bypass_b) {
+ (void) fprintf(stdout,
+ MSGSTR(2146, " (Bypass's A & B enabled)"));
+ }
+ (void) fprintf(stdout, "\n");
+ } else {
+ (void) fprintf(stdout, "\n");
+ }
+ }
+ temperature_messages(l_state, 0);
+}
+
+
+
+
+/*
+ * loop_messages() Display loop messages.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+loop_messages(struct l_state_struct *l_state, int index, int elem_index)
+{
+Loop_elem_st loop;
+int j;
+
+ if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) {
+ (void) fprintf(stdout, "%s\n",
+ l_state->ib_tbl.config.text[index]);
+ }
+ for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num;
+ j++) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + j],
+ (void *)&loop, sizeof (loop));
+
+ (void) fprintf(stdout, "\t");
+ if (j == 0) {
+ if (loop.code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(2147, "Loop A is not installed"));
+ } else {
+ if (loop.split) {
+ (void) fprintf(stdout, MSGSTR(2148,
+ "Loop A is configured as two separate loops."));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2149,
+ "Loop A is configured as a single loop."));
+ }
+ }
+ } else {
+ if (loop.code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(2150, "Loop B is not installed"));
+ } else {
+ if (loop.split) {
+ (void) fprintf(stdout, MSGSTR(2151,
+ "Loop B is configured as two separate loops."));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2152,
+ "Loop B is configured as a single loop."));
+ }
+ }
+ }
+ (void) fprintf(stdout, "\n");
+ }
+}
+
+
+
+/*
+ * ctlr_messages() Display ESI Controller status.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+ctlr_messages(struct l_state_struct *l_state, int index, int elem_index)
+{
+Ctlr_elem_st ctlr;
+int j;
+int ib_a_flag = 1;
+
+ if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) {
+ (void) fprintf(stdout, "%s\n",
+ l_state->ib_tbl.config.text[index]);
+ }
+ for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num;
+ j++) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + j],
+ (void *)&ctlr, sizeof (ctlr));
+ if (j == 0) {
+ (void) fprintf(stdout, MSGSTR(2153, "\tA: "));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2154, "\tB: "));
+ ib_a_flag = 0;
+ }
+ if (ctlr.code == S_OK) {
+ (void) fprintf(stdout, MSGSTR(29, "O.K."));
+ /* If any byte 3 bits set display */
+ ib_decode(&ctlr);
+ /* Display Version message */
+ revision_msg(l_state, elem_index + j);
+ /*
+ * Display the tranciver module state for this
+ * IB.
+ */
+ trans_messages(l_state, ib_a_flag);
+ } else if (ctlr.code == S_CRITICAL) {
+ (void) fprintf(stdout,
+ MSGSTR(122, "Critical failure"));
+ ib_decode(&ctlr);
+ (void) fprintf(stdout, "\n");
+ } else if (ctlr.code == S_NONCRITICAL) {
+ (void) fprintf(stdout,
+ MSGSTR(89, "Non-Critical Failure"));
+ ib_decode(&ctlr);
+ (void) fprintf(stdout, "\n");
+ } else if (ctlr.code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(30, "Not Installed"));
+ (void) fprintf(stdout, "\n");
+ } else if (ctlr.code == S_NOT_AVAILABLE) {
+ (void) fprintf(stdout,
+ MSGSTR(34, "Disabled"));
+ (void) fprintf(stdout, "\n");
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(4, "Unknown status"));
+ (void) fprintf(stdout, "\n");
+ }
+ }
+}
+
+
+
+/*
+ * fan_decode() Display Fans bytes 1-3 state.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+fan_decode(Fan_elem_st *fan)
+{
+ if (fan->fail) {
+ (void) fprintf(stdout, MSGSTR(2155,
+ ":Yellow LED is on"));
+ }
+ if (fan->speed == 0) {
+ (void) fprintf(stdout, MSGSTR(2156,
+ ":Fan stopped"));
+ } else if (fan->speed < S_HI_SPEED) {
+ (void) fprintf(stdout, MSGSTR(2157,
+ ":Fan speed Low"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2158,
+ ":Fan speed Hi"));
+ }
+}
+
+/*
+ * fan_messages() Display Fan status.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+fan_messages(struct l_state_struct *l_state, int hdr_index, int elem_index)
+{
+Fan_elem_st fan;
+int j;
+
+ /* Get and print messages */
+ if (l_state->ib_tbl.config.type_hdr[hdr_index].text_len != 0) {
+ (void) fprintf(stdout, "%s\n",
+ l_state->ib_tbl.config.text[hdr_index]);
+ }
+ for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[hdr_index].num;
+ j++) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + j],
+ (void *)&fan, sizeof (fan));
+ (void) fprintf(stdout, "\t%d ", j);
+ if (fan.code == S_OK) {
+ (void) fprintf(stdout, MSGSTR(29, "O.K."));
+ revision_msg(l_state, elem_index + j);
+ } else if (fan.code == S_CRITICAL) {
+ (void) fprintf(stdout,
+ MSGSTR(122, "Critical failure"));
+ fan_decode(&fan);
+ revision_msg(l_state, elem_index + j);
+ } else if (fan.code == S_NONCRITICAL) {
+ (void) fprintf(stdout,
+ MSGSTR(89, "Non-Critical Failure"));
+ fan_decode(&fan);
+ revision_msg(l_state, elem_index + j);
+ } else if (fan.code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(30, "Not Installed"));
+ } else if (fan.code == S_NOT_AVAILABLE) {
+ (void) fprintf(stdout,
+ MSGSTR(34, "Disabled"));
+ revision_msg(l_state, elem_index + j);
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(4, "Unknown status"));
+ }
+ }
+ (void) fprintf(stdout, "\n");
+}
+
+
+
+/*
+ * ps_decode() Display Power Supply bytes 1-3 state.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+ps_decode(Ps_elem_st *ps)
+{
+ if (ps->dc_over) {
+ (void) fprintf(stdout, MSGSTR(2159,
+ ": DC Voltage too high"));
+ }
+ if (ps->dc_under) {
+ (void) fprintf(stdout, MSGSTR(2160,
+ ": DC Voltage too low"));
+ }
+ if (ps->dc_over_i) {
+ (void) fprintf(stdout, MSGSTR(2161,
+ ": DC Current too high"));
+ }
+ if (ps->ovrtmp_fail || ps->temp_warn) {
+ (void) fprintf(stdout, MSGSTR(2162,
+ ": Temperature too high"));
+ }
+ if (ps->ac_fail) {
+ (void) fprintf(stdout, MSGSTR(2163,
+ ": AC Failed"));
+ }
+ if (ps->dc_fail) {
+ (void) fprintf(stdout, MSGSTR(2164,
+ ": DC Failed"));
+ }
+}
+
+
+
+/*
+ * revision_msg() Print the revision message from page 7.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+revision_msg(struct l_state_struct *l_state, int index)
+{
+ if (strlen((const char *)
+ l_state->ib_tbl.p7_s.element_desc[index].desc_string)) {
+ (void) fprintf(stdout, "(%s)",
+ l_state->ib_tbl.p7_s.element_desc[index].desc_string);
+ }
+}
+
+
+
+/*
+ * ps_messages() Display Power Supply status.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+ps_messages(struct l_state_struct *l_state, int index, int elem_index)
+{
+Ps_elem_st ps;
+int j;
+
+ /* Get and print Power Supply messages */
+
+ if (l_state->ib_tbl.config.type_hdr[index].text_len != 0) {
+ (void) fprintf(stdout, "%s\n",
+ l_state->ib_tbl.config.text[index]);
+ }
+
+ for (j = 0; j < (int)l_state->ib_tbl.config.type_hdr[index].num;
+ j++) {
+ (void) bcopy((const void *)
+ &l_state->ib_tbl.p2_s.element[elem_index + j],
+ (void *)&ps, sizeof (ps));
+ (void) fprintf(stdout, "\t%d ", j);
+ if (ps.code == S_OK) {
+ (void) fprintf(stdout, MSGSTR(29, "O.K."));
+ revision_msg(l_state, elem_index + j);
+ } else if (ps.code == S_CRITICAL) {
+ (void) fprintf(stdout,
+ MSGSTR(122, "Critical failure"));
+ ps_decode(&ps);
+ revision_msg(l_state, elem_index + j);
+ } else if (ps.code == S_NONCRITICAL) {
+ (void) fprintf(stdout,
+ MSGSTR(89, "Non-Critical Failure"));
+ ps_decode(&ps);
+ revision_msg(l_state, elem_index + j);
+ } else if (ps.code == S_NOT_INSTALLED) {
+ (void) fprintf(stdout,
+ MSGSTR(30, "Not Installed"));
+ } else if (ps.code == S_NOT_AVAILABLE) {
+ (void) fprintf(stdout,
+ MSGSTR(34, "Disabled"));
+ revision_msg(l_state, elem_index + j);
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(4, "Unknown status"));
+ }
+
+ }
+ (void) fprintf(stdout, "\n");
+}
+
+
+
+/*
+ * abnormal_condition() Display any abnormal condition messages.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+abnormal_condition_display(struct l_state_struct *l_state)
+{
+
+ (void) fprintf(stdout, "\n");
+ if (l_state->ib_tbl.p2_s.ui.crit) {
+ (void) fprintf(stdout,
+ MSGSTR(2165, " "
+ "CRITICAL CONDITION DETECTED\n"));
+ }
+ if (l_state->ib_tbl.p2_s.ui.non_crit) {
+ (void) fprintf(stdout,
+ MSGSTR(2166, " "
+ "WARNING: NON-CRITICAL CONDITION DETECTED\n"));
+ }
+ if (l_state->ib_tbl.p2_s.ui.invop) {
+ (void) fprintf(stdout,
+ MSGSTR(2167, " "
+ "WARNING: Invalid Operation bit set.\n"
+ "\tThis means an Enclosure Control page"
+ " or an Array Control page with an invalid\n"
+ "\tformat has previously been transmitted to the"
+ " Enclosure Services card by a\n\tSend Diagnostic"
+ " SCSI command.\n"));
+ }
+ (void) fprintf(stdout, "\n");
+}
+
+
+
+
+
+/*
+ * adm_start() Spin up the given list
+ * of SENA devices.
+ *
+ * RETURNS:
+ * none.
+ */
+int
+adm_start(char **argv)
+{
+char *path_phys = NULL;
+Path_struct *path_struct;
+int err = 0, retval = 0;
+
+ while (*argv != NULL) {
+ if ((err = l_convert_name(*argv, &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr, MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ *argv);
+ if (err != -1) {
+ (void) print_errString(err, *argv);
+ }
+ (argv)++;
+ retval++;
+ continue;
+ }
+ VERBPRINT(MSGSTR(101, "Issuing start to:\n %s\n"), *argv);
+ if (err = g_start(path_phys)) {
+ (void) print_errString(err, *argv);
+ (argv)++;
+ retval++;
+ continue;
+ }
+ (argv)++;
+ }
+ return (retval);
+}
+
+
+
+/*
+ * adm_stop() Spin down a
+ * given list of SENA devices.
+ *
+ * RETURNS:
+ * none.
+ */
+int
+adm_stop(char **argv)
+{
+char *path_phys = NULL;
+Path_struct *path_struct;
+int err = 0, retval = 0;
+
+ while (*argv != NULL) {
+ if ((err = l_convert_name(*argv, &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ *argv);
+ if (err != -1) {
+ (void) print_errString(err, *argv);
+ }
+ (argv)++;
+ retval++;
+ continue;
+ }
+
+ /*
+ * scsi stop is not supported for tape drives.
+ * The scsi unload op code for tape is the same as a
+ * scsi stop for disk so this command will eject the tape.
+ * If an eject is the desired behavior then remove the
+ * following if block. ('mt offline' will give you
+ * the same eject functionality).
+ */
+ if (strstr(path_phys, SLSH_DRV_NAME_ST)) {
+ errno = ENOTSUP;
+ (void) print_errString(0, path_phys);
+ (argv)++;
+ continue;
+ }
+
+ VERBPRINT(MSGSTR(100, "Issuing stop to:\n %s\n"), *argv);
+ if (err = g_stop(path_phys, 1)) {
+ (void) print_errString(err, *argv);
+ (argv)++;
+ retval++;
+ continue;
+ }
+ (argv)++;
+ }
+ return (retval);
+}
+
+
+/*
+ * On a SOC+ chip, the port is either put into (offline) or pulled out
+ * of (online) a loopback mode since the laser cannot be turned on or off.
+ * As of this writing, this feature is yet to be supported by the ifp
+ * driver on a QLogic card.
+ *
+ * INPUT :
+ * Command line args and flag - LUX_P_ONLINE or LUX_P_OFFLINE
+ * The path that is passed has to be the physical path to the port.
+ * For example :
+ * /devices/sbus@2,0/SUNW,socal@2,0:0
+ * /devices/io-unit@f,e0200000/sbi@0,0/SUNW,socal@2,0:0
+ * /devices/pci@1f,4000/SUNW,ifp@2:devctl
+ * RETURNS :
+ * Nothing
+ */
+int
+adm_port_offline_online(char *argv[], int flag)
+{
+ int err, retval = 0;
+ char *path_phys = NULL;
+ char *nexus_path_ptr = NULL;
+ Path_struct *path_struct = NULL;
+
+ while (*argv != NULL) {
+ if ((err = l_convert_name(*argv, &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ *argv);
+ if (err != -1) {
+ (void) print_errString(err, *argv);
+ }
+ argv++;
+ retval++;
+ continue;
+ }
+
+ /* Get the nexus path - need this to print messages */
+ if ((err = g_get_nexus_path(path_phys, &nexus_path_ptr)) != 0) {
+ (void) print_errString(err, *argv);
+ retval++;
+ goto cleanup_and_go;
+ }
+
+ if (flag == LUX_P_OFFLINE) {
+ if ((err = g_port_offline(nexus_path_ptr))) {
+ (void) print_errString(err, nexus_path_ptr);
+ retval++;
+ goto cleanup_and_go;
+ }
+ fprintf(stdout,
+ MSGSTR(2223, "Port %s has been disabled\n"),
+ nexus_path_ptr);
+ } else if (flag == LUX_P_ONLINE) {
+ if ((err = g_port_online(nexus_path_ptr))) {
+ (void) print_errString(err, nexus_path_ptr);
+ retval++;
+ goto cleanup_and_go;
+ }
+ fprintf(stdout,
+ MSGSTR(2224, "Port %s has been enabled\n"),
+ nexus_path_ptr);
+ } else {
+ (void) fprintf(stderr,
+ MSGSTR(2225,
+ "Unknown action requested "
+ "on port - %d\nIgnoring."),
+ flag);
+ retval++;
+ }
+cleanup_and_go:
+ free(path_phys);
+ free(path_struct);
+ free(nexus_path_ptr);
+ argv++;
+ }
+ return (retval);
+}
+
+/*
+ * Expert level subcommand 'luxadm -e port'
+ * which displays all FC ports on a host and state information for
+ * connectivity (CONNECTED or NOT CONNECTED) indicating whether there
+ * are devices attached to the port.
+ *
+ * Sample output for ifp:
+ *
+ * /devices/pci@1f,4000/SUNW,ifp@2:devctl CONNECTED
+ * /devices/pci@1f,2000/SUNW,ifp@1:devctl NOT CONNECTED
+ *
+ * Sample output for socal:
+ *
+ * /devices/sbus@2,0/SUNW,socal@d,10000:0 CONNECTED
+ * /devices/sbus@2,0/SUNW,socal@d,10000:1 NOT CONNECTED
+ * /devices/sbus@2,0/SUNW,socal@2,0:0 NOT CONNECTED
+ * /devices/sbus@2,0/SUNW,socal@2,0:1 CONNECTED
+ *
+ * Note: for socal the path returned is not a devctl path as there is no
+ * devctl path for socal.
+ *
+ * Sample output for fp:
+ *
+ * /devices/sbus@2,0/SUNW,qlc@5/fp@0,0:devctl CONNECTED
+ * /devices/sbus@2,0/SUNW,qlc@4/fp@1,0:devctl CONNECTED
+ */
+int
+adm_display_port(int verbose)
+{
+ /*
+ * If another port driver needs to be searched, add it here
+ */
+ static char *portdrvr_list[] = {"socal",
+ "fp",
+ "ifp",
+ NULL};
+ portlist_t portlist;
+ int x = 0, err = 0, retval = 0;
+ int port_state;
+
+ portlist.hbacnt = 0;
+
+ /*
+ * Look for all HBA ports as listed in portdrvr_list[]
+ */
+ while (portdrvr_list[x]) {
+ if (err = g_get_port_path(portdrvr_list[x], &portlist)) {
+ if (err != L_PORT_DRIVER_NOT_FOUND &&
+ err != L_PHYS_PATH_NOT_FOUND) {
+ (void) print_errString(err, portdrvr_list[x]);
+ retval++;
+ }
+ }
+ x++;
+ }
+
+
+ /*
+ * For each port path found get the connection state.
+ * If there are devices attached the state is considered connected.
+ */
+ for (x = 0; x < portlist.hbacnt; x++) {
+ if (err = g_get_port_state(portlist.physpath[x],
+ &port_state, verbose)) {
+ (void) print_errString(err, portlist.physpath[x]);
+ retval++;
+ } else {
+ fprintf(stdout, "%-65s ", portlist.physpath[x]);
+ if (port_state == PORT_CONNECTED) {
+ (void) fprintf(stdout,
+ MSGSTR(2233,
+ "CONNECTED\n"));
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(2234,
+ "NOT CONNECTED\n"));
+ }
+ }
+ }
+ g_free_portlist(&portlist);
+ return (retval);
+}
+
+/*
+ * Expert level subcommand 'luxadm -e external_loopback <portpath>
+ * internal_loopback
+ * no_loopback
+ * Does just what you would think. Sets port in designated loopback
+ * mode.
+ * INPUT: portpath - path to device on which to set loopback mode
+ * flag - loopback mode to set. Values are:
+ * EXT_LOOPBACK
+ * INT_LOOPBACK
+ * NO_LOOPBACK
+ *
+ * RETURN: 0 on success
+ * non-zero on failure
+ */
+int
+adm_port_loopback(char *portpath, int flag)
+{
+ int err;
+ char *path_phys = NULL;
+ Path_struct *path_struct = NULL;
+ int cmd;
+
+ if ((err = l_convert_name(portpath, &path_phys,
+ &path_struct, Options & PVERBOSE)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ portpath);
+ if (err != -1) {
+ (void) print_errString(err, portpath);
+ }
+ return (-1);
+ }
+
+ switch (flag) {
+ case EXT_LOOPBACK:
+ cmd = EXT_LPBACK;
+ break;
+ case INT_LOOPBACK:
+ cmd = INT_LPBACK;
+ break;
+ case NO_LOOPBACK:
+ cmd = NO_LPBACK;
+ break;
+ default:
+ (void) fprintf(stderr,
+ MSGSTR(2225,
+ "Unknown action requested "
+ "on port - %d\nIgnoring."),
+ flag);
+ free(path_phys);
+ free(path_struct);
+ return (-1);
+ }
+
+
+ if ((err = g_loopback_mode(path_phys, cmd)) != 0) {
+ (void) print_errString(err, portpath);
+ free(path_phys);
+ free(path_struct);
+ return (-1);
+ } else {
+ switch (flag) {
+ case EXT_LOOPBACK:
+ (void) fprintf(stdout,
+ MSGSTR(2230,
+ "External loopback mode set "
+ "on:\n%s\n"),
+ portpath);
+ break;
+ case INT_LOOPBACK:
+ (void) fprintf(stdout,
+ MSGSTR(2231,
+ "Internal loopback mode set "
+ "on:\n%s\n"),
+ portpath);
+ break;
+ case NO_LOOPBACK:
+ (void) fprintf(stdout,
+ MSGSTR(2232,
+ "Loopback mode unset "
+ "on:\n%s\n"),
+ portpath);
+ break;
+ default:
+ fprintf(stderr,
+ MSGSTR(2248, "Undefined command\n"));
+ break;
+ }
+ }
+ free(path_phys);
+ free(path_struct);
+ return (0);
+}
+
+
+
+/*
+ * To print the pathlist and mpxio path attributes
+ */
+void
+adm_print_pathlist(char *dev_path)
+{
+ int i, pathcnt = 1;
+ mp_pathlist_t pathlist;
+ int retval = 0;
+ char temppath[MAXPATHLEN];
+ char wwns[(WWN_SIZE *2) +1];
+ uchar_t wwn_data[WWN_SIZE];
+ int err;
+ int state, ext_state = 0;
+ char *path_state[5];
+
+ path_state[0] = MSGSTR(2400, "INIT");
+ path_state[1] = MSGSTR(2401, "ONLINE");
+ path_state[2] = MSGSTR(2402, "STANDBY");
+ path_state[3] = MSGSTR(2403, "FAULT");
+ path_state[4] = MSGSTR(2404, "OFFLINE");
+
+ (void) strcpy(temppath, dev_path);
+ retval = g_get_pathlist(temppath, &pathlist);
+ if (retval != 0) {
+ (void) print_errString(retval, NULL);
+ exit(-1);
+ }
+ pathcnt = pathlist.path_count;
+ for (i = 0; i < pathcnt; i++) {
+ (void) fprintf(stdout,
+ MSGSTR(2303, " Controller \t%s\n"),
+ pathlist.path_info[i].path_hba);
+
+ (void) fprintf(stdout,
+ MSGSTR(2304, " Device Address\t\t%s\n"),
+ pathlist.path_info[i].path_addr);
+
+ if ((err = get_host_controller_pwwn(
+ pathlist.path_info[i].path_hba,
+ (uchar_t *)&wwn_data)) != 0) {
+ if (err != ENOTSUP) {
+ (void) print_errString(err,
+ pathlist.path_info[i].path_hba);
+ exit(1);
+ }
+ }
+
+ if (!err) {
+ copy_wwn_data_to_str(wwns, wwn_data);
+ (void) fprintf(stdout,
+ MSGSTR(2326, " Host controller port WWN\t%s\n"),
+ wwns);
+ }
+
+ (void) fprintf(stdout,
+ MSGSTR(2305, " Class\t\t\t%s\n"),
+ pathlist.path_info[i].path_class);
+ if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
+ (void) fprintf(stdout,
+ MSGSTR(2306, " State\t\t\t%s\n"),
+ path_state[pathlist.path_info[i].path_state]);
+ }
+ if ((err = g_stms_get_path_state(dev_path,
+ pathlist.path_info[i].path_hba, &state,
+ &ext_state)) != 0) {
+ (void) print_errString(err,
+ pathlist.path_info[i].path_hba);
+ exit(1);
+ } else {
+ if ((ext_state & MDI_PATHINFO_STATE_USER_DISABLE)
+ == MDI_PATHINFO_STATE_USER_DISABLE) {
+ ext_state = 0;
+ fprintf(stdout,
+ MSGSTR(2327,
+ " I/Os disabled on this %s path\n\n"),
+ path_state[pathlist.path_info[i].path_state]);
+ }
+ }
+ }
+ /* Free memory for per path info properties */
+ free(pathlist.path_info);
+}
+
+/*
+ * compare_multipath
+ * compares path with all paths in pathlist
+ * If there is a match, 0 is returned, otherwise 1 is returned
+ */
+int
+compare_multipath(char *path, struct mplist_struct *pathlist)
+{
+
+ while (pathlist != NULL) {
+ if (strncmp(path, pathlist->devpath, MAXPATHLEN) == 0) {
+ return (0);
+ }
+ pathlist = pathlist->next;
+ }
+ return (1);
+}
+
+/*
+ * lun_display() Prints the
+ * information for an individual lun.
+ *
+ * RETURNS:
+ * none.
+ */
+static int
+lun_display(Path_struct *path_struct, L_inquiry inq_struct, int verbose)
+{
+
+char phys_path[MAXPATHLEN], last_logical_path[MAXPATHLEN];
+uchar_t *pg_buf = NULL;
+L_disk_state l_disk_state;
+struct dlist *mlist;
+int offset, mode_data_avail, err = 0;
+Mode_header_10 *mode_header_ptr;
+struct mode_page *pg_hdr;
+WWN_list *wwn_list, *list_start, *wwn_list_ptr;
+WWN_list *wwn_list_find;
+int found = 0;
+int argpwwn = 0, argnwwn = 0;
+struct mplist_struct *mplistp, *mpl, *mpln;
+struct dlist *dlist;
+
+
+
+ strcpy(phys_path, path_struct->p_physical_path);
+ strcpy(last_logical_path, phys_path);
+
+ mplistp = mpl = mpln = (struct mplist_struct *)NULL;
+ /*
+ * Get path to all the FC disk and tape devices.
+ * If there is no slash in the argument in this routine, we assume
+ * it is a wwn argument.
+ */
+ if (strstr(path_struct->argv, "/") != NULL) {
+ if ((err = g_devices_get_all(&wwn_list)) != 0) {
+ return (err);
+ }
+ } else {
+ if ((err = g_get_wwn_list(&wwn_list, verbose)) != 0) {
+ return (err);
+ }
+ }
+
+ g_sort_wwn_list(&wwn_list);
+
+ list_start = wwn_list;
+
+ for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
+ wwn_list_ptr = wwn_list_ptr->wwn_next) {
+ if (strcasecmp(wwn_list_ptr->port_wwn_s,
+ path_struct->argv) == 0) {
+ list_start = wwn_list_ptr;
+ argpwwn = 1;
+ break;
+ } else if (strcasecmp(wwn_list_ptr->node_wwn_s,
+ path_struct->argv) == 0) {
+ list_start = wwn_list_ptr;
+ argnwwn = 1;
+ break;
+ }
+ }
+
+ for (wwn_list_ptr = list_start; wwn_list_ptr != NULL;
+ wwn_list_ptr = wwn_list_ptr->wwn_next) {
+
+
+ if (argpwwn) {
+ if (strcasecmp(wwn_list_ptr->port_wwn_s,
+ path_struct->argv) != 0) {
+ continue;
+ }
+ (void) strcpy(phys_path, wwn_list_ptr->physical_path);
+ } else if (argnwwn) {
+ if (strstr(wwn_list_ptr->logical_path,
+ last_logical_path) != NULL) {
+ continue;
+ }
+ if (strcasecmp(wwn_list_ptr->node_wwn_s,
+ path_struct->argv) != 0) {
+ continue;
+ }
+ (void) strcpy(phys_path, wwn_list_ptr->physical_path);
+ (void) strcpy(last_logical_path,
+ wwn_list_ptr->logical_path);
+ }
+
+ if (argnwwn || argpwwn) {
+ if (compare_multipath(wwn_list_ptr->logical_path,
+ mplistp) == 0) {
+ continue;
+ }
+ }
+
+ mode_data_avail = 0;
+
+ (void) memset(&l_disk_state, 0, sizeof (struct l_disk_state_struct));
+
+ /*
+ * Don't call g_get_multipath if this is a SCSI_VHCI device
+ * dlist gets alloc'ed here to retain the free at the end
+ */
+ if (strstr(phys_path, SCSI_VHCI) == NULL) {
+ if ((err = g_get_multipath(phys_path,
+ &(l_disk_state.g_disk_state.multipath_list),
+ wwn_list, verbose)) != 0) {
+ return (err);
+ }
+
+ mlist = l_disk_state.g_disk_state.multipath_list;
+ if (mlist == NULL) {
+ l_disk_state.l_state_flag = L_NO_PATH_FOUND;
+ N_DPRINTF(" lun_display: Error finding"
+ " multiple paths to the disk.\n");
+ (void) g_free_wwn_list(&wwn_list);
+ return (L_NO_VALID_PATH);
+ }
+ } else {
+ /* Search for match on physical path name */
+ for (wwn_list_find = list_start; wwn_list_find != NULL;
+ wwn_list_find = wwn_list_find->wwn_next) {
+ if (strncmp(wwn_list_find->physical_path, phys_path,
+ strlen(wwn_list_find->physical_path))
+ == 0) {
+ found++;
+ break;
+ }
+ }
+
+ if (!found) {
+ return (L_NO_VALID_PATH);
+ } else {
+ found = 0;
+ }
+
+ if ((dlist = (struct dlist *)
+ calloc(1, sizeof (struct dlist))) == NULL) {
+ return (L_MALLOC_FAILED);
+ }
+ if ((dlist->logical_path = (char *)calloc(1,
+ strlen(wwn_list_find->logical_path) + 1)) == NULL) {
+ return (L_MALLOC_FAILED);
+ }
+ if ((dlist->dev_path = (char *)calloc(1,
+ strlen(phys_path) + 1)) == NULL) {
+ return (L_MALLOC_FAILED);
+ }
+ strncpy(dlist->logical_path, wwn_list_find->logical_path,
+ strlen(wwn_list_find->logical_path));
+ strncpy(dlist->dev_path, phys_path, strlen(phys_path));
+ l_disk_state.g_disk_state.multipath_list = dlist;
+ }
+
+ if (argnwwn || argpwwn) {
+ for (mlist = l_disk_state.g_disk_state.multipath_list;
+ mlist != NULL; mlist = mlist->next) {
+ /* add the path to the list for compare */
+ if ((mpl = (struct mplist_struct *)
+ calloc(1, sizeof (struct mplist_struct)))
+ == NULL) {
+ adm_mplist_free(mplistp);
+ return (L_MALLOC_FAILED);
+ }
+
+ mpl->devpath = (char *)calloc(1, MAXPATHLEN+1);
+ if (mpl->devpath == NULL) {
+ adm_mplist_free(mplistp);
+ return (L_MALLOC_FAILED);
+ }
+ strncpy(mpl->devpath, mlist->logical_path,
+ strlen(mlist->logical_path));
+ if (mplistp == NULL) {
+ mplistp = mpln = mpl;
+ } else {
+ mpln->next = mpl;
+ mpln = mpl;
+ }
+ }
+ }
+
+ /* get mode page information for FC device */
+ if (l_get_mode_pg(phys_path, &pg_buf, verbose) == 0) {
+ mode_header_ptr = (struct mode_header_10_struct *)
+ (void *)pg_buf;
+ offset = sizeof (struct mode_header_10_struct) +
+ mode_header_ptr->bdesc_length;
+ pg_hdr = (struct mode_page *)&pg_buf[offset];
+
+ while (offset < (mode_header_ptr->length +
+ sizeof (mode_header_ptr->length)) &&
+ !mode_data_avail) {
+ if (pg_hdr->code == MODEPAGE_CACHING) {
+ mode_data_avail++;
+ break;
+ }
+ offset += pg_hdr->length + sizeof (struct mode_page);
+ pg_hdr = (struct mode_page *)&pg_buf[offset];
+ }
+ }
+
+ switch ((inq_struct.inq_dtype & DTYPE_MASK)) {
+ case DTYPE_DIRECT:
+ fprintf(stdout, MSGSTR(121, "DEVICE PROPERTIES for disk: %s\n"),
+ path_struct->argv);
+ break;
+ case DTYPE_SEQUENTIAL: /* Tape */
+ fprintf(stdout, MSGSTR(2249, "DEVICE PROPERTIES for tape: %s\n"),
+ path_struct->argv);
+ break;
+ default:
+ fprintf(stdout, MSGSTR(2250, "DEVICE PROPERTIES for: %s\n"),
+ path_struct->argv);
+ break;
+ }
+
+ (void) display_lun_info(l_disk_state, path_struct, pg_hdr,
+ mode_data_avail, wwn_list, phys_path);
+
+ (void) g_free_multipath(l_disk_state.g_disk_state.multipath_list);
+
+ if (!(argpwwn || argnwwn)) {
+ break;
+ }
+
+ } /* End for wwn_list_ptr = list_start... */
+
+ (void) g_free_wwn_list(&wwn_list);
+ adm_mplist_free(mplistp);
+ return (0);
+}
+
+/*
+ * display_lun_info() Prints the device specific information
+ * for a lun.
+ *
+ * RETURNS:
+ * none.
+ */
+void
+display_lun_info(L_disk_state l_disk_state, Path_struct *path_struct,
+ struct mode_page *pg_hdr, int mode_data_avail, WWN_list
+ *wwn_list, char *phys_path)
+{
+float lunMbytes;
+struct scsi_capacity_16 cap_data;
+struct dlist *mlist;
+struct my_mode_caching *pg8_buf;
+int err;
+L_inquiry inq;
+hrtime_t start_time, end_time;
+char *envdb = NULL;
+int peripheral_qual;
+L_inquiry80 inq80;
+size_t serial_len = sizeof (inq80.inq_serial);
+
+ if ((envdb = getenv("_LUX_T_DEBUG")) != NULL) {
+ start_time = gethrtime();
+ }
+
+ memset(&cap_data, 0, sizeof (cap_data));
+
+ if (err = g_get_inquiry(phys_path, &inq)) {
+ fprintf(stderr, "\n");
+ print_errString(err, phys_path);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+
+ if (err = g_get_serial_number(phys_path, inq80.inq_serial,
+ &serial_len)) {
+ fprintf(stderr, "\n");
+ print_errString(err, phys_path);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ /*
+ * check to see if the peripheral qualifier is zero
+ * if it is non-zero, we will return with an error.
+ */
+ peripheral_qual = inq.inq_dtype & ~DTYPE_MASK;
+ if (peripheral_qual != DPQ_POSSIBLE) {
+ fprintf(stderr, MSGSTR(2254, "\n Error: Logical Unit "
+ "(%s) is not available.\n"), phys_path);
+ exit(1);
+ }
+
+ fprintf(stdout, " ");
+ fprintf(stdout, MSGSTR(3, "Vendor:"));
+ fprintf(stdout, "\t\t");
+ print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
+ fprintf(stdout, MSGSTR(2115, "\n Product ID:\t\t"));
+ print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
+
+ fprintf(stdout, "\n ");
+ fprintf(stdout, MSGSTR(2119, "Revision:"));
+ fprintf(stdout, "\t\t");
+ print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
+
+ fprintf(stdout, "\n ");
+ fprintf(stdout, MSGSTR(17, "Serial Num:"));
+ fprintf(stdout, "\t\t");
+ print_chars(inq80.inq_serial, serial_len, 0);
+
+ if ((inq.inq_dtype & DTYPE_MASK) == DTYPE_DIRECT) {
+ if ((err = get_lun_capacity(phys_path, &cap_data)) != 0) {
+ print_errString(err, phys_path);
+ exit(1);
+ }
+
+ if (cap_data.sc_capacity > 0 && cap_data.sc_lbasize > 0) {
+ lunMbytes = cap_data.sc_capacity + 1;
+ lunMbytes *= cap_data.sc_lbasize;
+ lunMbytes /= (float)(1024*1024);
+ fprintf(stdout, "\n ");
+ fprintf(stdout, MSGSTR(60,
+ "Unformatted capacity:\t%6.3f MBytes"), lunMbytes);
+ }
+ }
+
+ fprintf(stdout, "\n");
+
+ if ((mode_data_avail) && (pg_hdr->code == MODEPAGE_CACHING)) {
+ pg8_buf = (struct my_mode_caching *)(void *)pg_hdr;
+ if (pg8_buf->wce) {
+ fprintf(stdout, MSGSTR(2122, " Write Cache:\t\t"
+ "Enabled\n"));
+ }
+ if (pg8_buf->rcd == 0) {
+ fprintf(stdout, MSGSTR(2123, " Read Cache:\t\t"
+ "Enabled\n"));
+ fprintf(stdout, MSGSTR(2124, " Minimum prefetch:"
+ "\t0x%x\n Maximum prefetch:\t0x%x\n"),
+ pg8_buf->min_prefetch,
+ pg8_buf->max_prefetch);
+ }
+ }
+
+ fprintf(stdout, " %s\t\t%s\n", MSGSTR(35, "Device Type:"),
+ dtype[inq.inq_dtype & DTYPE_MASK]);
+
+
+ fprintf(stdout, MSGSTR(2128, " Path(s):\n"));
+ fprintf(stdout, "\n");
+
+ if ((mlist = l_disk_state.g_disk_state.multipath_list) == NULL) {
+ fprintf(stderr, MSGSTR(2323, "Error: No paths found (%s)"),
+ path_struct->argv);
+ exit(1);
+ }
+
+
+ if (strstr(mlist->dev_path, SCSI_VHCI) != NULL) {
+ fprintf(stdout, " %s\n %s\n",
+ mlist->logical_path, mlist->dev_path);
+ adm_print_pathlist(mlist->dev_path);
+ } else {
+ /*
+ * first display user's requested path
+ * This will avoid duplicate inquiries as well
+ */
+ for (mlist = l_disk_state.g_disk_state.multipath_list;
+ mlist != NULL; mlist = mlist->next) {
+ if ((strcmp(mlist->dev_path, path_struct->p_physical_path))
+ == 0) {
+ display_path_info(mlist->dev_path, mlist->logical_path,
+ wwn_list);
+ break;
+ }
+ }
+
+ /*
+ * Now display rest of paths
+ * skipping one already displayed
+ */
+ for (mlist = l_disk_state.g_disk_state.multipath_list;
+ mlist != NULL; mlist = mlist->next) {
+ if ((strcmp(mlist->dev_path, path_struct->p_physical_path))
+ == 0) {
+ continue;
+ }
+ if (err = g_get_inquiry(mlist->dev_path, &inq)) {
+ fprintf(stderr, "\n");
+ print_errString(err, mlist->dev_path);
+ fprintf(stderr, "\n");
+ exit(1);
+ }
+ display_path_info(mlist->dev_path, mlist->logical_path,
+ wwn_list);
+ }
+ }
+ fprintf(stdout, "\n");
+
+ if (envdb != NULL) {
+ end_time = gethrtime();
+ fprintf(stdout, " display_lun_info: "
+ "\t\tTime = %lld millisec\n",
+ (end_time - start_time)/1000000);
+ }
+}
+
+/*
+ * display_path_info() Prints the path specific information
+ * for a lun.
+ * Note: Only applies to ssd nodes currently
+ *
+ * RETURNS:
+ * none.
+ */
+static void
+display_path_info(char *devpath, char *logicalpath, WWN_list *wwn_list)
+{
+WWN_list *wwn_list_walk;
+int err;
+uchar_t wwn_data[WWN_SIZE];
+char wwns[(WWN_SIZE *2) +1];
+char drvr_path[MAXPATHLEN];
+char *cptr;
+int status;
+
+ fprintf(stdout, " %s\n", logicalpath);
+ fprintf(stdout, " %s\n", devpath);
+ fprintf(stdout, " %s\t\t", MSGSTR(2321, "LUN path port WWN:"));
+
+ /*
+ * Walk the wwn list passed in and print the
+ * port wwn matching the device path
+ */
+ for (wwn_list_walk = wwn_list; wwn_list_walk != NULL;
+ wwn_list_walk = wwn_list_walk->wwn_next) {
+ if (strcmp(wwn_list_walk->physical_path, devpath) == 0) {
+ fprintf(stdout, "%s", wwn_list_walk->port_wwn_s);
+ break;
+ }
+ }
+ /*
+ * newline here in case port wwn not found
+ */
+ fprintf(stdout, "\n");
+
+ drvr_path[0] = '\0';
+ (void) strcat(drvr_path, devpath);
+ if (((cptr = strstr(drvr_path, SLSH_DRV_NAME_SSD)) != NULL) ||
+ ((cptr = strstr(drvr_path, SLSH_DRV_NAME_ST)) != NULL)) {;
+ *cptr = '\0';
+ } else {
+ fprintf(stderr, MSGSTR(2324, "Error: Incorrect path (%s)\n"),
+ drvr_path);
+ exit(1);
+ }
+ *cptr = '\0';
+
+ if ((err = get_host_controller_pwwn(drvr_path,
+ (uchar_t *)&wwn_data)) != 0) {
+ print_errString(err, drvr_path);
+ exit(1);
+ }
+
+ copy_wwn_data_to_str(wwns, wwn_data);
+ fprintf(stdout, " %s\t%s\n",
+ MSGSTR(2322, "Host controller port WWN:"), wwns);
+
+ /*
+ * Determine path status
+ */
+ if ((err = get_path_status(devpath, &status)) != 0) {
+ print_errString(err, devpath);
+ exit(1);
+ } else {
+ fprintf(stdout, " %s\t\t", MSGSTR(2329, "Path status:"));
+ display_port_status(status);
+ }
+}
+
+/*
+ * Retrieves the lun capacity
+ */
+static int
+get_lun_capacity(char *devpath, struct scsi_capacity_16 *cap_data)
+{
+int fd;
+
+ if (devpath == NULL || cap_data == NULL) {
+ return (L_INVALID_PATH);
+ }
+
+ if ((fd = g_object_open(devpath, O_RDONLY | O_NDELAY)) == -1) {
+ return (L_OPEN_PATH_FAIL);
+ } else {
+ (void) g_scsi_read_capacity_1016_cmd(fd, cap_data,
+ sizeof (struct scsi_capacity_16));
+ close(fd);
+ }
+ return (0);
+}
+
+/*
+ * Retrieves the reservation status
+ */
+static int
+get_path_status(char *devpath, int *status)
+{
+int fd, mystatus = 0;
+
+
+ if (devpath == NULL || status == NULL) {
+ return (L_INVALID_PATH);
+ }
+
+ *status = 0;
+ if ((fd = g_object_open(devpath, O_RDONLY | O_NDELAY)) == -1) {
+ return (L_OPEN_PATH_FAIL);
+ } else {
+ if ((mystatus = g_scsi_tur(fd)) != 0) {
+ if ((mystatus & L_SCSI_ERROR) &&
+ ((mystatus & ~L_SCSI_ERROR) == STATUS_CHECK)) {
+ *status = L_NOT_READY;
+ } else if ((mystatus & L_SCSI_ERROR) &&
+ ((mystatus & ~L_SCSI_ERROR) ==
+ STATUS_RESERVATION_CONFLICT)) {
+ *status = L_RESERVED;
+ } else {
+ *status = L_SCSI_ERR;
+ }
+ }
+ }
+ close(fd);
+ return (0);
+}
+
+/*
+ * Description:
+ * Retrieves the port wwn associated with the hba node
+ *
+ * hba_path: /devices/pci@8,600000/SUNW,qlc@4/fp@0,0
+ * pwwn: ptr to a uchar_t array of size WWN_SIZE
+ */
+static int
+get_host_controller_pwwn(char *hba_path, uchar_t *pwwn)
+{
+char *cptr, *portptr;
+int found = 0, err, devlen;
+char my_hba_path[MAXPATHLEN];
+di_node_t node;
+di_prom_prop_t promprop;
+uchar_t *port_wwn_data = NULL;
+int di_ret;
+di_prom_handle_t ph;
+char *promname;
+uchar_t *promdata;
+uint_t path_type;
+fc_port_dev_t hba_port;
+
+ if (hba_path == NULL || pwwn == NULL) {
+ return (L_INVALID_PATH);
+ }
+
+ if ((path_type = g_get_path_type(hba_path)) == 0) {
+ return (L_INVALID_PATH);
+ }
+
+ /*
+ * ifp nodes do not have a port-wwn prom property
+ * so handle them via FC4 device map
+ */
+ if (path_type & FC4_XPORT_MASK) {
+ if ((err = get_FC4_host_controller_pwwn(hba_path, pwwn)) != 0) {
+ return (err);
+ } else {
+ return (0);
+ }
+ /* For Leadville path get the port wwn through g_get_host param. */
+ } else if ((path_type & FC_GEN_XPORT) &&
+ ((path_type & FC_FCA_MASK) == FC_FCA_MASK)) {
+ /*
+ * For Leadville path, get the port wwn through
+ * g_get_host param. This is a general solution
+ * to support 3rd party vendor Leadville FCA.
+ */
+ my_hba_path[0] = '\0';
+ (void) strlcat(my_hba_path, hba_path, sizeof (my_hba_path));
+ (void) snprintf(my_hba_path, sizeof (my_hba_path), "%s%s",
+ hba_path, FC_CTLR);
+ if ((err = g_get_host_params(
+ my_hba_path, &hba_port, 0)) != 0) {
+ return (err);
+ } else {
+ (void) memcpy(pwwn, &hba_port.dev_pwwn.raw_wwn[0],
+ WWN_SIZE);
+ return (0);
+ }
+ } else if ((path_type & FC_FCA_MASK) == FC_PCI_FCA) {
+ /*
+ * Get port WWN through prom property
+ */
+ my_hba_path[0] = '\0';
+ (void) strlcat(my_hba_path, hba_path, sizeof (my_hba_path));
+ /*
+ * sanity check for /devices mount point
+ */
+ if (strlen(my_hba_path) > (devlen = strlen("/devices"))) {
+ cptr = &my_hba_path[devlen];
+ } else {
+ return (L_INVALID_PATH);
+ }
+
+ /*
+ * Now strip off the trailing "/fp@"
+ */
+ if ((portptr = strstr(cptr, "/fp@")) != NULL) {
+ *portptr = '\0';
+ }
+
+ if ((node = di_init(cptr, DINFOCPYALL)) == DI_NODE_NIL) {
+ return (L_DEV_SNAPSHOT_FAILED);
+ }
+
+ if (di_nodeid(node) == DI_SID_NODEID) {
+ di_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
+ "port-wwn", &port_wwn_data);
+ if (di_ret == -1 || port_wwn_data == NULL) {
+ di_fini(node);
+ return (L_NO_WWN_PROP_FOUND);
+ } else {
+ (void) memcpy(pwwn, port_wwn_data, WWN_SIZE);
+ found++;
+ }
+ } else if (di_nodeid(node) == DI_PROM_NODEID) {
+ if ((ph = di_prom_init()) == DI_PROM_HANDLE_NIL) {
+ di_fini(node);
+ return (L_PROM_INIT_FAILED);
+ }
+
+ for (promprop = di_prom_prop_next(ph, node,
+ DI_PROM_PROP_NIL);
+ promprop != DI_PROM_PROP_NIL;
+ promprop = di_prom_prop_next(ph, node, promprop)) {
+ if (((promname = di_prom_prop_name(
+ promprop)) != NULL) &&
+ (strcmp(promname, "port-wwn") == 0) &&
+ (di_prom_prop_data(promprop,
+ &promdata) == WWN_SIZE)) {
+ /* Found port-wwn */
+ (void) memcpy(pwwn, promdata, WWN_SIZE);
+ found++;
+ break;
+ }
+ }
+ di_prom_fini(ph);
+ }
+
+ di_fini(node);
+ if (found) {
+ return (0);
+ } else {
+ return (L_INVALID_PATH);
+ }
+ } else {
+ return (L_INVALID_PATH_TYPE);
+ }
+}
+
+
+/*
+ * Description:
+ * Retrieve pwwn via SFIOCGMAP
+ */
+static int
+get_FC4_host_controller_pwwn(char *hba_path, uchar_t *pwwn)
+{
+sf_al_map_t sf_map;
+char my_hba_path[MAXPATHLEN];
+int fd;
+
+ if (hba_path == NULL || pwwn == NULL) {
+ return (L_INVALID_PATH);
+ }
+
+ (void) snprintf(my_hba_path, sizeof (my_hba_path), "%s%s",
+ hba_path, FC_CTLR);
+
+ if ((fd = g_object_open(my_hba_path, O_NDELAY | O_RDONLY)) == -1) {
+ return (errno);
+ }
+
+ memset(&sf_map, 0, sizeof (sf_al_map_t));
+
+ if (ioctl(fd, SFIOCGMAP, &sf_map) != 0) {
+ close(fd);
+ return (L_SFIOCGMAP_IOCTL_FAIL);
+ }
+
+ close(fd);
+
+ if (sf_map.sf_count == 0) {
+ close(fd);
+ return (L_SFIOCGMAP_IOCTL_FAIL);
+ }
+
+ (void) memcpy(pwwn, &sf_map.sf_hba_addr.sf_port_wwn[0], WWN_SIZE);
+
+ return (0);
+}
+
+/*
+ * from_ptr: ptr to uchar_t array of size WWN_SIZE
+ * to_ptr: char ptr to string of size WWN_SIZE*2+1
+ */
+void
+copy_wwn_data_to_str(char *to_ptr, const uchar_t *from_ptr)
+{
+ if ((to_ptr == NULL) || (from_ptr == NULL))
+ return;
+
+ sprintf(to_ptr, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3],
+ from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]);
+}
+
+/*
+ * Frees a previously allocated mplist_struct
+ */
+void
+adm_mplist_free(struct mplist_struct *mplistp)
+{
+struct mplist_struct *mplistn;
+
+ while (mplistp != NULL) {
+ mplistn = mplistp->next;
+ if (mplistp->devpath != NULL) {
+ free(mplistp->devpath);
+ mplistp->devpath = NULL;
+ }
+ free(mplistp);
+ mplistp = mplistn;
+ }
+}
+
+int
+adm_reserve(char *path)
+{
+ char *path_phys = NULL;
+ int err;
+ if ((path_phys =
+ g_get_physical_name(path)) == NULL) {
+
+ (void) fprintf(stderr, "%s: ", whoami);
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ path);
+ (void) fprintf(stderr, "\n");
+ return (1);
+ }
+
+ if ((err = g_reserve(path_phys)) != 0) {
+ (void) print_errString(err, path);
+ return (1);
+ }
+ return (0);
+}
+
+int
+adm_release(char *path)
+{
+ char *path_phys = NULL;
+ int err;
+ if ((path_phys =
+ g_get_physical_name(path)) == NULL) {
+
+ (void) fprintf(stderr, "%s: ", whoami);
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ path);
+ (void) fprintf(stderr, "\n");
+ return (1);
+ }
+
+ if ((err = g_release(path_phys)) != 0) {
+ (void) print_errString(err, path);
+ return (1);
+ }
+ return (0);
+}
+
+void
+i18n_catopen() {
+ (void) g_i18n_catopen();
+}
+
+int adm_check_file(char **path, int flag) {
+ int err;
+ if (err = l_check_file(*path, flag)) {
+ (void) print_errString(err, *path);
+ return (-1);
+ }
+
+ (void) fprintf(stdout, MSGSTR(2212, "Download file O.K. \n\n"));
+ return (0);
+}
+
+/*
+ * Print out private loop dev dtype
+ */
+void
+print_private_loop_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
+ uchar_t dtype_prop)
+{
+ if ((dtype_prop & DTYPE_MASK) < 0x10) {
+ (void) fprintf(stdout, " 0x%-2x (%s",
+ (dtype_prop & DTYPE_MASK), dtype[(dtype_prop & DTYPE_MASK)]);
+ } else if ((dtype_prop & DTYPE_MASK) < 0x1f) {
+ (void) fprintf(stdout,
+ MSGSTR(2243, " 0x%-2x (Reserved"),
+ (dtype_prop & DTYPE_MASK));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2245,
+ " 0x%-2x (Unknown Type"), (dtype_prop & DTYPE_MASK));
+ }
+ /* Check to see if this is the HBA */
+ if (wwnConversion(hba_port_wwn) == wwnConversion(port_wwn)) {
+ /* MATCH */
+ (void) fprintf(stdout, MSGSTR(2244,
+ ",Host Bus Adapter)\n"));
+ } else {
+ (void) fprintf(stdout, ")\n");
+ }
+}
diff --git a/usr/src/cmd/luxadm/hot.h b/usr/src/cmd/luxadm/hot.h
new file mode 100644
index 0000000000..66d1e3dd49
--- /dev/null
+++ b/usr/src/cmd/luxadm/hot.h
@@ -0,0 +1,78 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * PHOTON CONFIGURATION MANAGER
+ * Common definitions
+ */
+
+/*
+ * I18N message number ranges
+ * This file: 13000 - 13499
+ * Shared common messages: 1 - 1999
+ */
+
+#ifndef _HOT_H
+#define _HOT_H
+
+
+
+
+/*
+ * Include any headers you depend on.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define NODE_CREATION_TIME 60 /* # seconds */
+/*
+ * Version 0.16 of the SES firmware powers up disks in front/back pairs.
+ * However, the first disk inserted is usually spun up by itself, so
+ * we need to calculate a timeout for 22/2 + 1 = 12 disks.
+ *
+ * Measured times are about 40s/disk for a total of 40*12=8 min total
+ * The timeout assumes 10s/iteration or 4*12*10=8 min
+ */
+#define PHOTON_SPINUP_TIMEOUT (4*12)
+#define PHOTON_SPINUP_DELAY 10
+
+#define QLC_LIP_DELAY 17
+
+#define TARGET_ID(box_id, f_r, slot) \
+ ((box_id | ((f_r == 'f' ? 0 : 1) << 4)) | (slot + 2))
+
+#define NEWER(time1, time2) (time1.tv_sec > time2.tv_sec)
+
+extern int Options;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HOT_H */
diff --git a/usr/src/cmd/luxadm/hotplug.c b/usr/src/cmd/luxadm/hotplug.c
new file mode 100644
index 0000000000..2efec8ba3c
--- /dev/null
+++ b/usr/src/cmd/luxadm/hotplug.c
@@ -0,0 +1,3037 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*LINTLIBRARY*/
+
+
+/*
+ * Hotplug program for SENA, RSM and SSA
+ * subsystems and individual FC_AL devices.
+ */
+
+/* #define _POSIX_SOURCE 1 */
+
+/*
+ * I18N message number ranges
+ * This file: 5500 - 5999
+ * Shared common messages: 1 - 1999
+ */
+
+
+/* Includes */
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/sunddi.h>
+#include <sys/ddi.h> /* for min */
+#include <sys/scsi/scsi.h>
+#include <nl_types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <l_common.h>
+#include <l_error.h>
+#include <stgcom.h>
+#include <a_state.h>
+#include <a5k.h>
+#include <rom.h>
+#include "hot.h"
+#include "common.h"
+#include "luxadm.h"
+
+
+/* Internal variables. */
+static char *cmdStrg[][4] = {
+ { "disks", "-C", 0, 0 },
+ { "disks", 0, 0, 0 },
+ { "drvconfig", "-i", "ssd", 0 },
+ { "drvconfig", 0, 0, 0 },
+ { "devlinks", 0, 0, 0 },
+ { "tapes", "-C", 0, 0 }
+};
+
+/* External variables */
+extern char *dtype[]; /* From adm.c */
+extern int Options;
+extern const int OPTION_CAPF;
+
+/* Internal functions */
+/* SENA and Individual FC device Hotplug */
+static int h_pre_insert_encl_dev(timestruc_t *, timestruc_t *,
+ timestruc_t *);
+static int h_post_insert_dev(timestruc_t, timestruc_t);
+static int h_pre_remove_dev(Hotplug_Devlist *,
+ WWN_list *wwn_list, int, int);
+static int h_post_remove_dev(Hotplug_Devlist *, int, int);
+static int h_pre_hotplug(Hotplug_Devlist **,
+ WWN_list *, int, int, int);
+static int h_post_hotplug(Hotplug_Devlist *,
+ WWN_list *, int, int, int, int);
+static int h_post_insert_encl(timestruc_t);
+static int h_pre_hotplug_sena(Hotplug_Devlist *,
+ WWN_list *, int, int, int);
+static int h_post_hotplug_sena(Hotplug_Devlist *,
+ WWN_list *, int, int, int, int);
+static int h_remove_ses_nodes(struct dlist *);
+static int h_print_list_warn(Hotplug_Devlist *, int, int);
+static int h_display_logical_nodes(struct dlist *);
+static void h_print_logical_nodes(struct dlist *);
+static int h_remove_nodes(struct dlist *);
+static int h_print_list(Hotplug_Devlist *, int *, int);
+static int h_get_fcdev_state(char *, char *, int, int *, int *, int);
+static int h_chk_dev_busy(Hotplug_Devlist *,
+ WWN_list *, int *, int, int);
+static int h_execCmnd(char **, int);
+int hotplug(int, char **, int, int);
+int h_insertSena_fcdev();
+static int h_find_new_device_link(char *, timestruc_t);
+
+
+
+/*
+ * Assists the user in hot inserting FC_AL
+ * individual device(s) and SENA enclosure(s).
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+int
+h_insertSena_fcdev()
+{
+timestruc_t ses_time, dsk_time, rmt_time;
+int err;
+struct stat ses_stat;
+
+ if ((err = h_pre_insert_encl_dev(&ses_time, &dsk_time,
+ &rmt_time)) != 0) {
+ return (err);
+ }
+ (void) fprintf(stdout, MSGSTR(5500,
+ "Please hit <RETURN> when you have finished"
+ " adding Fibre Channel Enclosure(s)/Device(s): "));
+ (void) getchar();
+
+ if ((err = h_post_insert_dev(dsk_time, rmt_time)) != 0) {
+ return (err);
+ }
+
+ if (stat(SES_DIR, &ses_stat) < 0) {
+ /*
+ * Non existence of /dev/es dir indicates
+ * no ses devices inserted.
+ * No need to call h_post_insert_encl().
+ */
+ if (errno == ENOENT) {
+ (void) fprintf(stdout, MSGSTR(5662,
+ " No new enclosure(s) were added!!\n\n"));
+ return (0);
+ } else {
+ return (L_LSTAT_ES_DIR_ERROR);
+ }
+ }
+
+ /*
+ * if the latest mod time of /dev/es is not newer than
+ * the original mod time no need to call
+ * h_post_insert_encl().
+ */
+ if ((&ses_time != (timestruc_t *)NULL) &&
+ !(NEWER(ses_stat.st_ctim, ses_time))) {
+ (void) fprintf(stdout, MSGSTR(5662,
+ " No new enclosure(s) were added!!\n\n"));
+ return (0);
+ }
+ if ((err = h_post_insert_encl(ses_time)) != 0) {
+ return (err);
+ }
+ return (0);
+}
+
+
+
+/*
+ * gets the devices state - check for disk's reservations.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static int
+h_get_fcdev_state(char *fc_dev, char *path_phys, int force_flag,
+ int *busy_flag, int *reserve_flag, int verbose_flag)
+{
+int err;
+L_inquiry inq;
+L_disk_state l_disk_state;
+
+
+ if ((err = g_get_inquiry(path_phys, &inq)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(5501,
+ "Inquiry failed for %s\n"),
+ path_phys);
+ return (err);
+ }
+ if (inq.inq_port) {
+ if ((err = l_get_disk_port_status(path_phys, &l_disk_state,
+ FC_PORT_B, verbose_flag)) != 0) {
+ return (err);
+ }
+ } else {
+ if ((err = l_get_disk_port_status(path_phys, &l_disk_state,
+ FC_PORT_A, verbose_flag)) != 0) {
+ return (err);
+ }
+ }
+
+ /*
+ * Don't print error msg. if disk is reserved
+ * and tried to be removed from the same port.
+ * If force flag is set, remove the disk without
+ * checking the disk reservations.
+ */
+ if (!force_flag) {
+ if (((inq.inq_port) &&
+ (l_disk_state.g_disk_state.d_state_flags[FC_PORT_B] &
+ L_RESERVED)) ||
+ ((!inq.inq_port) &&
+ (l_disk_state.g_disk_state.d_state_flags[FC_PORT_A] &
+ L_RESERVED))) {
+ *reserve_flag = 1;
+ }
+ }
+ return (0);
+}
+
+
+/*
+ * Forks a child process and let the child to
+ * execute a given command string by calling the
+ * the execvp() function. Then, the parent process
+ * waits for the child to exit. Once the parent process
+ * is notified by the kernel with the termination of
+ * the child, then the parent checks for the exit
+ * status of the child and return to the caller with -1 in case
+ * of error and zero otherwise.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+int
+h_execCmnd(char *argStr[], int nArg)
+{
+pid_t pid;
+int ix, status;
+
+ if ((pid = fork()) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(133,
+ "Error: Failed to fork a process.\n"));
+ return (-1);
+ } else if (pid == 0) {
+ /* child process */
+ if (execvp(argStr[0], argStr) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(5502,
+ " Error: execvp() failed to run "
+ "the command:"));
+ for (ix = 0; ix < nArg; ix++) {
+ (void) fprintf(stderr,
+ " %s", argStr[ix]);
+ }
+ (void) fprintf(stderr, "\n");
+ /* let parent know about the error. */
+ exit(ENOEXEC);
+ }
+ }
+
+ /* parent executes the following. */
+ if (waitpid(pid, &status, 0) != pid) {
+ (void) fprintf(stderr,
+ MSGSTR(5503,
+ "Error: waitpid() failed.\n"));
+ return (-1);
+ }
+ if (WIFEXITED(status) &&
+ WEXITSTATUS(status) == ENOEXEC) {
+ /* child failed to run the command string. */
+ return (-1);
+ }
+ return (0);
+
+}
+
+
+
+
+/*
+ * frees the hotplug disk list structure.
+ *
+ * RETURNS:
+ * N/A
+ */
+void
+h_free_hotplug_dlist(Hotplug_Devlist **hotplug_dlist)
+{
+Hotplug_Devlist *list = NULL;
+
+ while (*hotplug_dlist != NULL) {
+ list = *hotplug_dlist;
+ *hotplug_dlist = (*hotplug_dlist)->next;
+ (void) g_free_multipath(list->seslist);
+ (void) g_free_multipath(list->dlhead);
+ (void) free((void *)list);
+ }
+}
+
+
+/*
+ * finds whether device (SENA or an FCAL device) is busy or not.
+ *
+ * OUTPUT:
+ * busy_flag = 1 (if device busy)
+ *
+ * RETURNS:
+ * 0 if O.K.
+ * non-zero otherwise
+ */
+static int
+h_chk_dev_busy(Hotplug_Devlist *hotplug_dev, WWN_list *wwn_list,
+ int *busy_flag, int force_flag, int verbose_flag)
+{
+int err;
+struct dlist *dlist;
+
+ if (hotplug_dev->dev_type == DTYPE_ESI) {
+ if ((err = l_offline_photon(hotplug_dev, wwn_list,
+ force_flag, verbose_flag)) != 0) {
+ if (err == L_DEV_BUSY) {
+ *busy_flag = 1;
+ } else {
+ return (err);
+ }
+ }
+ for (dlist = hotplug_dev->dlhead;
+ dlist != NULL; dlist = dlist->next) {
+ (void) g_online_drive(dlist->multipath,
+ force_flag);
+ }
+ } else {
+ if ((err = g_offline_drive(hotplug_dev->dlhead,
+ force_flag)) != 0) {
+ if (err == L_DEV_BUSY) {
+ *busy_flag = 1;
+ } else {
+ return (err);
+ }
+ }
+ (void) g_online_drive(hotplug_dev->dlhead, force_flag);
+ }
+ return (0);
+}
+
+
+
+/*
+ * prints the given list to stdout,
+ * gets the input from user whether
+ * to skip the busy devices or quit
+ * and passes that input to the calling
+ * function.
+ *
+ * OUTPUT:
+ * int *action
+ * s = Skip
+ * q = Quit
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static int
+h_print_list(Hotplug_Devlist *bsyRsrv_disk_list, int *action, int enc_type)
+{
+Hotplug_Devlist *list;
+int i = 1;
+char choice[2];
+
+ (void) fprintf(stdout,
+ MSGSTR(5504, "The list of devices being used"
+ " (either busy or reserved) by the host:\n"));
+ for (list = bsyRsrv_disk_list; list != NULL; list = list->next, i++) {
+ if ((list->dev_type == DTYPE_DIRECT) &&
+ (list->dev_location == SENA)) {
+ if (list->f_flag != NULL) {
+ if (enc_type == DAK_ENC_TYPE) {
+ (void) fprintf(stdout, MSGSTR(5663,
+ " %d: Box Name: \"%s\" slot %d\n"),
+ i, list->box_name, list->slot);
+ } else {
+ (void) fprintf(stdout, MSGSTR(137,
+ " %d: Box Name: \"%s\" front slot %d\n"),
+ i, list->box_name, list->slot);
+ }
+ } else {
+ if (enc_type == DAK_ENC_TYPE) {
+ (void) fprintf(stdout, MSGSTR(5663,
+ " %d: Box Name: \"%s\" slot %d\n"),
+ i, list->box_name, list->slot + (MAX_DRIVES_DAK/2));
+ } else {
+ (void) fprintf(stdout, MSGSTR(136,
+ " %d: Box Name: \"%s\" rear slot %d\n"),
+ i, list->box_name, list->slot);
+ }
+ }
+ } else if (((list->dev_type == DTYPE_DIRECT) ||
+ (list->dev_type == DTYPE_SEQUENTIAL)) &&
+ (list->dev_location == NON_SENA)) {
+ (void) fprintf(stdout, MSGSTR(5505,
+ " %d: Device %s\n"),
+ i, list->dev_name);
+ } else if (list->dev_type == DTYPE_ESI) {
+ (void) fprintf(stdout, MSGSTR(5506,
+ " %d: Box: %s\n"),
+ i, list->box_name);
+ }
+ }
+
+ /* Get the user input and continue accordingly. */
+ (void) fprintf(stdout,
+ MSGSTR(5507,
+ "\n\nPlease enter 's' or <CR> to Skip the \"busy/reserved\""
+ " device(s) or\n'q' to Quit and run the"
+ " subcommand with\n-F (force) option. [Default: s]: "));
+ for (;;) {
+ (void) gets(choice);
+ if (choice[0] == 'q' || choice[0] == 'Q' ||
+ choice[0] == 's' || choice[0] == 'S' ||
+ choice[0] == '\0') {
+ break;
+ }
+ (void) fprintf(stdout, MSGSTR(5508,
+ " Enter an appropriate option [s,<CR>,q]: "));
+ }
+ if (choice[0] == 'q' || choice[0] == 'Q') {
+ *action = QUIT;
+ } else {
+ *action = SKIP;
+ }
+ (void) fprintf(stdout, "\n\n");
+ return (0);
+}
+
+
+
+/*
+ * prints the warning message.
+ *
+ * RETURNS:
+ * None.
+ */
+static void
+h_prt_warning()
+{
+ (void) fprintf(stderr,
+ MSGSTR(5509,
+ "\n WARNING!!! Please ensure that no"
+ " filesystems are mounted on these device(s).\n"
+ " All data on these devices should have been"
+ " backed up.\n\n\n"));
+}
+
+
+
+/*
+ * handle helper-mode hotplug commands
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+int
+hotplug(int todo, char **argv, int verbose_flag, int force_flag)
+{
+char ses_path[MAXPATHLEN], dev_path[MAXPATHLEN];
+char *path_phys = NULL, code, node_wwn_s[WWN_S_LEN];
+char inq_path[MAXNAMELEN], *ptr = NULL;
+uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE];
+int tid, slot, path_index, dtype, f_r, err = 0;
+int al_pa, i, dev_location, found_nullwwn = 0;
+int busy_flag = 0, reserve_flag = 0, action = 0;
+int pathcnt = 1;
+L_state l_state;
+gfc_map_t map;
+Path_struct *path_struct;
+WWN_list *wwn_list = NULL;
+Box_list *box_list;
+Hotplug_Devlist *disk_list, *disk_list_head, *disk_list_tail;
+Hotplug_Devlist *bsyRsrv_dskLst_head, *bsyRsrv_dskLst_tail;
+int enc_type;
+L_inquiry inq;
+char *physpath;
+Path_struct *p_pathstruct;
+char temp2path[MAXPATHLEN];
+mp_pathlist_t pathlist;
+int p_pw = 0, p_on = 0, p_st = 0;
+
+ /* Initialize structures and pointers here */
+ disk_list_head = disk_list_tail = (Hotplug_Devlist *)NULL;
+ bsyRsrv_dskLst_head = (Hotplug_Devlist *)NULL;
+ bsyRsrv_dskLst_tail = (Hotplug_Devlist *)NULL;
+ map.dev_addr = NULL;
+
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "DEBUG: luxadm: hotplug() entering for \"%s\" ...\n",
+ argv[0] ? argv[0] : "<null ptr>");
+#endif
+ if ((err = l_get_box_list(&box_list, 0)) != 0) {
+ return (err);
+ }
+
+ if (todo == REMOVE_DEVICE) {
+ (void) h_prt_warning();
+ }
+
+ /*
+ * At this point user want to insert or remove
+ * one or more pathnames they've specified.
+ */
+ if ((err = g_get_wwn_list(&wwn_list, verbose_flag)) != 0) {
+ (void) l_free_box_list(&box_list);
+ return (err);
+ }
+ for (path_index = 0; argv[path_index] != NULL; path_index++) {
+ if ((err = l_convert_name(argv[path_index], &path_phys,
+ &path_struct, verbose_flag)) != 0) {
+ /* Make sure we have a device path. */
+ (void) strcpy(inq_path, argv[path_index]);
+ if (((ptr = strstr(inq_path, ",")) != NULL) &&
+ ((*(ptr + 1) == 'f') || (*(ptr + 1) == 'r') ||
+ (*(ptr +1) == 's')) &&
+ todo == REMOVE_DEVICE) {
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ err = 0;
+ continue;
+ }
+ *ptr = NULL;
+ slot = path_struct->slot;
+ f_r = path_struct->f_flag;
+ if ((err = l_convert_name(inq_path, &path_phys,
+ &path_struct, verbose_flag)) != 0) {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ }
+ err = 0;
+ continue;
+ }
+ if ((err = print_devState(argv[path_index],
+ path_struct->p_physical_path,
+ f_r, slot, verbose_flag)) != 0) {
+ err = 0;
+ continue;
+ }
+ }
+ if (path_struct->ib_path_flag) {
+ path_phys = path_struct->p_physical_path;
+ } else {
+ if (err != -1) {
+ (void) print_errString(err,
+ argv[path_index]);
+ } else {
+ (void) fprintf(stderr, "\n");
+ (void) fprintf(stderr,
+ MSGSTR(33,
+ " Error: converting"
+ " %s to physical path.\n"
+ " Invalid pathname.\n"),
+ argv[path_index]);
+ }
+ err = 0;
+ continue;
+ }
+ }
+ if (path_struct->slot_valid ||
+ strstr(path_phys, DRV_NAME_SSD)) {
+ dtype = DTYPE_DIRECT;
+ } else if (strstr(path_phys, SLSH_DRV_NAME_ST)) {
+ dtype = DTYPE_SEQUENTIAL;
+ } else {
+ dtype = DTYPE_ESI;
+ }
+
+ if (strstr(path_phys, SCSI_VHCI) != NULL) {
+ /* obtain phci */
+ (void) strcpy(temp2path, path_phys);
+ if (err = g_get_pathlist(temp2path, &pathlist)) {
+ (void) print_errString(err, NULL);
+ exit(-1);
+ }
+ pathcnt = pathlist.path_count;
+ p_pw = p_on = p_st = 0;
+ for (i = 0; i < pathcnt; i++) {
+ if (pathlist.path_info[i].path_state <
+ MAXPATHSTATE) {
+ if (strstr(pathlist.path_info[i].
+ path_addr,
+ path_struct->argv) != NULL) {
+ p_pw = i;
+ break;
+ }
+ if (pathlist.path_info[i].path_state ==
+ MDI_PATHINFO_STATE_ONLINE) {
+ p_on = i;
+ }
+ if (pathlist.path_info[i].path_state ==
+ MDI_PATHINFO_STATE_STANDBY) {
+ p_st = i;
+ }
+ }
+ }
+ if (strstr(pathlist.path_info[p_pw].path_addr,
+ path_struct->argv) != NULL) {
+ /* matching input pwwn */
+ (void) strcpy(temp2path,
+ pathlist.path_info[p_pw].path_hba);
+ } else if (pathlist.path_info[p_on].path_state ==
+ MDI_PATHINFO_STATE_ONLINE) {
+ /* on_line path */
+ (void) strcpy(temp2path,
+ pathlist.path_info[p_on].path_hba);
+ } else {
+ /* standby or path0 */
+ (void) strcpy(temp2path,
+ pathlist.path_info[p_st].path_hba);
+ }
+ free(pathlist.path_info);
+ (void) strcat(temp2path, FC_CTLR);
+ } else {
+ (void) strcpy(temp2path, path_phys);
+ }
+
+ if ((err = g_get_dev_map(temp2path, &map, verbose_flag))
+ != 0) {
+ return (err);
+ }
+
+ if ((map.hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) ||
+ (map.hba_addr.port_topology == FC_TOP_FABRIC)) {
+ /* public or fabric loop device */
+ free((void *)map.dev_addr);
+ (void) fprintf(stderr, MSGSTR(5540,
+ "This operation is not "
+ "supported in this topology.\n"));
+ exit(-1);
+ }
+
+ if (todo == REPLACE_DEVICE) {
+ (void) fprintf(stderr,
+ MSGSTR(5511,
+ "Error:"
+ " replace_device is not supported"
+ " on this subsystem.\n"));
+ exit(-1);
+ }
+
+ if ((todo == REMOVE_DEVICE) &&
+ (dtype == DTYPE_DIRECT ||
+ dtype == DTYPE_SEQUENTIAL ||
+ dtype == DTYPE_UNKNOWN)) {
+ if (l_chk_null_wwn(path_struct, ses_path,
+ &l_state, verbose_flag) == 1) {
+ found_nullwwn = 1;
+ /*
+ * set dev_path to NULL,
+ * if disk has null wwn.
+ */
+ *dev_path = NULL;
+ dev_location = SENA;
+ goto getinfo;
+ }
+ }
+
+ (void) strcpy(ses_path, path_phys);
+
+ if (strstr(ses_path, "ses") == NULL &&
+ l_get_ses_path(path_phys, ses_path, &map,
+ verbose_flag) != 0) {
+
+ /* Could be a non-photon disk device */
+ if ((todo == REMOVE_DEVICE) &&
+ (dtype == DTYPE_DIRECT ||
+ dtype == DTYPE_SEQUENTIAL)) {
+ dev_location = NON_SENA;
+
+ if ((err = h_get_fcdev_state(argv[path_index],
+ path_phys, force_flag,
+ &busy_flag, &reserve_flag,
+ verbose_flag)) != 0) {
+ goto done;
+ }
+ (void) strcpy(dev_path, path_phys);
+ if ((err = g_get_wwn(dev_path, port_wwn,
+ node_wwn, &al_pa,
+ verbose_flag)) != 0) {
+ goto done;
+ }
+ (void) sprintf(node_wwn_s,
+ "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ node_wwn[0], node_wwn[1], node_wwn[2],
+ node_wwn[3], node_wwn[4], node_wwn[5],
+ node_wwn[6], node_wwn[7]);
+ tid = g_sf_alpa_to_switch[al_pa];
+ goto loop;
+ }
+ continue;
+ }
+
+ if (strstr(ses_path, "ses") != NULL) {
+ dev_location = SENA;
+ if ((err = l_convert_name(ses_path, &physpath,
+ &p_pathstruct, 0)) != 0) {
+ free(physpath);
+ free(p_pathstruct);
+ goto done;
+
+ }
+ if ((err = g_get_inquiry(physpath, &inq)) != 0) {
+ free(physpath);
+ free(p_pathstruct);
+ goto done;
+ }
+ enc_type = l_get_enc_type(inq);
+
+ }
+ if ((err = l_get_status(ses_path,
+ &l_state, verbose_flag)) != 0) {
+ goto done;
+ }
+ if (dtype == DTYPE_ESI) {
+ /* could be removing a photon */
+ if (todo == REMOVE_DEVICE) {
+ /*
+ * Need the select ID (tid) for the IB.
+ */
+ if ((err = g_get_wwn(ses_path, port_wwn,
+ node_wwn, &al_pa,
+ verbose_flag)) != 0) {
+ goto done;
+ }
+ (void) sprintf(node_wwn_s,
+ "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ node_wwn[0], node_wwn[1], node_wwn[2],
+ node_wwn[3], node_wwn[4], node_wwn[5],
+ node_wwn[6], node_wwn[7]);
+ tid = g_sf_alpa_to_switch[al_pa];
+ *dev_path = '\0';
+ /*
+ * Check if any disk in this photon
+ * is reserved by another host
+ */
+ if (!force_flag) {
+ for (
+ i = 0;
+ i < l_state.total_num_drv/2;
+ i++) {
+ if ((l_state.drv_front[i].g_disk_state.d_state_flags[PORT_A] &
+ L_RESERVED) ||
+ (l_state.drv_front[i].g_disk_state.d_state_flags[PORT_B] &
+ L_RESERVED) ||
+ (l_state.drv_rear[i].g_disk_state.d_state_flags[PORT_A] &
+ L_RESERVED) ||
+ (l_state.drv_rear[i].g_disk_state.d_state_flags[PORT_B] &
+ L_RESERVED)) {
+ reserve_flag = 1;
+ }
+ }
+ }
+ goto loop;
+ }
+ (void) fprintf(stderr,
+ MSGSTR(5512,
+ "Error: %s already exists!!\n"),
+ argv[path_index]);
+ goto done;
+ }
+getinfo:
+ if (!path_struct->slot_valid) {
+ /* We are passing the disks path */
+ if ((err = l_get_slot(path_struct, &l_state,
+ verbose_flag)) != 0) {
+ goto done;
+ }
+ }
+
+ slot = path_struct->slot;
+ if (path_struct->f_flag) {
+ tid = l_state.drv_front[slot].ib_status.sel_id;
+ code = l_state.drv_front[slot].ib_status.code;
+ (void) strcpy(node_wwn_s,
+ l_state.drv_front[slot].g_disk_state.node_wwn_s);
+ } else {
+ tid = l_state.drv_rear[slot].ib_status.sel_id;
+ code = l_state.drv_rear[slot].ib_status.code;
+ (void) strcpy(node_wwn_s,
+ l_state.drv_rear[slot].g_disk_state.node_wwn_s);
+ }
+
+ if (found_nullwwn) {
+ goto loop;
+ }
+
+ l_make_node(ses_path, tid, dev_path, &map, 0);
+
+ if ((todo == INSERT_DEVICE) &&
+ (g_device_in_map(&map, tid) ||
+ (code != S_NOT_INSTALLED))) {
+ (void) fprintf(stderr,
+ MSGSTR(5513, "\nNotice: %s may "
+ "already be present.\n"),
+ argv[path_index]);
+ if (path_struct->f_flag) {
+ if ((l_state.drv_front[slot].l_state_flag
+ != L_NO_PATH_FOUND) &&
+ (!l_state.drv_front[slot].ib_status.dev_off))
+ continue;
+ } else {
+ if ((l_state.drv_rear[slot].l_state_flag
+ != L_NO_PATH_FOUND) &&
+ (!l_state.drv_rear[slot].ib_status.dev_off))
+ continue;
+ }
+ }
+
+ /* Check if disk is reserved */
+ if ((todo == REMOVE_DEVICE) && (!force_flag)) {
+ if (path_struct->f_flag) {
+ if ((l_state.drv_front[slot].g_disk_state.d_state_flags[PORT_A] &
+ L_RESERVED) ||
+ (l_state.drv_front[slot].g_disk_state.d_state_flags[PORT_B] &
+ L_RESERVED)) {
+ reserve_flag = 1;
+ }
+ } else {
+ if ((l_state.drv_rear[slot].g_disk_state.d_state_flags[PORT_A] &
+ L_RESERVED) ||
+ (l_state.drv_rear[slot].g_disk_state.d_state_flags[PORT_B] &
+ L_RESERVED)) {
+ reserve_flag = 1;
+ }
+ }
+ }
+
+loop:
+ if ((disk_list = (Hotplug_Devlist *)
+ calloc(1, sizeof (Hotplug_Devlist))) == NULL) {
+ (void) print_errString(L_MALLOC_FAILED, NULL);
+ goto done;
+ }
+
+ /*
+ * dev_path is NULL when removing a whole encloser. We
+ * don't want to call g_get_multipath while removing whole
+ * enclosure. Its being taken care later in the code path
+ */
+
+ if ((todo != INSERT_DEVICE) && (dtype != DTYPE_ESI)) {
+ if ((err = g_get_multipath(dev_path,
+ &(disk_list->dlhead),
+ wwn_list, verbose_flag)) != 0) {
+ if (disk_list->dlhead != NULL) {
+ (void) g_free_multipath(
+ disk_list->dlhead);
+ }
+ goto done;
+ }
+ }
+ disk_list->dev_type = dtype;
+ disk_list->dev_location = dev_location;
+ (void) strcpy(disk_list->dev_name,
+ argv[path_index]);
+ disk_list->tid = tid;
+ (void) strcpy(disk_list->node_wwn_s, node_wwn_s);
+ if (dev_location == SENA) {
+ if ((err = l_get_allses(ses_path, box_list,
+ &(disk_list->seslist), 0)) != 0) {
+ if (disk_list->seslist != NULL) {
+ (void) g_free_multipath(disk_list->seslist);
+ }
+ goto done;
+ }
+ (void) strcpy(disk_list->box_name,
+ (char *)l_state.ib_tbl.enclosure_name);
+ disk_list->slot = slot;
+ disk_list->f_flag = path_struct->f_flag;
+ }
+ if (todo == REMOVE_DEVICE && !force_flag && !reserve_flag) {
+ if ((err = h_chk_dev_busy(disk_list, wwn_list,
+ &busy_flag, force_flag, verbose_flag)) != 0) {
+ goto done;
+ }
+ }
+
+ if (reserve_flag || busy_flag) {
+ if (reserve_flag)
+ disk_list->reserve_flag = 1;
+ if (busy_flag)
+ disk_list->busy_flag = 1;
+
+ if (bsyRsrv_dskLst_head == NULL) {
+ bsyRsrv_dskLst_head =
+ bsyRsrv_dskLst_tail = disk_list;
+ } else {
+ disk_list->prev = bsyRsrv_dskLst_tail;
+ bsyRsrv_dskLst_tail->next = disk_list;
+ bsyRsrv_dskLst_tail = disk_list;
+ }
+ reserve_flag = 0;
+ busy_flag = 0;
+
+ } else if (disk_list_head == NULL) {
+ disk_list_head = disk_list_tail = disk_list;
+ } else {
+ disk_list->prev = disk_list_tail;
+ disk_list_tail->next = disk_list;
+ disk_list_tail = disk_list;
+ }
+ }
+
+ if (bsyRsrv_dskLst_head != NULL) {
+ if ((err = h_print_list(bsyRsrv_dskLst_head,
+ &action, enc_type)) != 0) {
+ goto done;
+ }
+ if (action == SKIP) {
+ (void) h_free_hotplug_dlist(&bsyRsrv_dskLst_head);
+ } else if (action == QUIT) {
+ goto done;
+ }
+ }
+ if (disk_list_head != NULL) {
+ if ((h_print_list_warn(disk_list_head, todo, enc_type)) != 0) {
+ goto done;
+ }
+ if ((err = h_pre_hotplug(&disk_list_head, wwn_list, todo, verbose_flag,
+ force_flag)) != 0) {
+ goto done;
+ }
+ if (disk_list_head != NULL) {
+ if (todo == REMOVE_DEVICE) {
+ (void) fprintf(stdout, MSGSTR(5514,
+ "\nHit <Return> after "
+ "removing the device(s)."));
+ } else {
+ (void) fprintf(stdout, MSGSTR(5515,
+ "\nHit <Return> after "
+ "inserting the device(s)."));
+ }
+ (void) getchar();
+ (void) fprintf(stdout, "\n");
+ if ((err = h_post_hotplug(disk_list_head, wwn_list,
+ todo, verbose_flag, force_flag,
+ enc_type)) != 0) {
+ goto done;
+ }
+ }
+ }
+done:
+ (void) l_free_box_list(&box_list);
+ (void) g_free_wwn_list(&wwn_list);
+ if (err && err != -1) {
+ return (err);
+ }
+ free((void *)map.dev_addr);
+ return (0);
+}
+
+
+
+
+/*
+ * Internal routine to clean up ../'s in paths.
+ * returns 0 if no "../" are left.
+ *
+ * Wouldn't it be nice if there was a standard system library
+ * routine to do this...?
+ */
+static int
+cleanup_dotdot_path(char *path)
+{
+ char holder[MAXPATHLEN];
+ char *dotdot;
+ char *previous_slash;
+
+ /* Find the first "/../" in the string */
+ dotdot = strstr(path, "/../");
+ if (dotdot == NULL) {
+ return (0);
+ }
+
+
+ /*
+ * If the [0] character is '/' and "../" immediatly
+ * follows it, then we can strip the ../
+ *
+ * /../../foo/bar == /foo/bar
+ *
+ */
+ if (dotdot == path) {
+ strcpy(holder, &path[3]); /* strip "/.." */
+ strcpy(path, holder);
+ return (1);
+ }
+
+ /*
+ * Now look for the LAST "/" before the "/../"
+ * as this is the parent dir we can get rid of.
+ * We do this by temporarily truncating the string
+ * at the '/' just before "../" using the dotdot pointer.
+ */
+ *dotdot = '\0';
+ previous_slash = strrchr(path, '/');
+ if (previous_slash == NULL) {
+ /*
+ * hmm, somethings wrong. path looks something
+ * like "foo/../bar/" so we can't really deal with it.
+ */
+ return (0);
+ }
+ /*
+ * Now truncate the path just after the previous '/'
+ * and slam everything after the "../" back on
+ */
+ *(previous_slash+1) = '\0';
+ (void) strcat(path, dotdot+4);
+ return (1); /* We may have more "../"s */
+}
+
+
+/*
+ * Follow symbolic links from the logical device name to
+ * the /devfs physical device name. To be complete, we
+ * handle the case of multiple links. This function
+ * either returns NULL (no links, or some other error),
+ * or the physical device name, alloc'ed on the heap.
+ *
+ * For S10 the physical path may be non-existent.
+ *
+ * NOTE: If the path is relative, it will be forced into
+ * an absolute path by pre-pending the pwd to it.
+ */
+char *
+h_get_physical_name_from_link(char *path)
+{
+ struct stat stbuf;
+ char source[MAXPATHLEN];
+ char scratch[MAXPATHLEN];
+ char pwd[MAXPATHLEN];
+ char *tmp;
+ int cnt;
+
+ /* return NULL if path is NULL */
+ if (path == NULL) {
+ return (NULL);
+ }
+
+ strcpy(source, path);
+ for (;;) {
+
+ /*
+ * First make sure the path is absolute. If not, make it.
+ * If it's already an absolute path, we have no need
+ * to determine the cwd, so the program should still
+ * function within security-by-obscurity directories.
+ */
+ if (source[0] != '/') {
+ tmp = getcwd(pwd, MAXPATHLEN);
+ if (tmp == NULL) {
+ O_DPRINTF("getcwd() failed - %s\n",
+ strerror(errno));
+ return (NULL);
+ }
+ /*
+ * Handle special case of "./foo/bar"
+ */
+ if (source[0] == '.' && source[1] == '/') {
+ strcpy(scratch, source+2);
+ } else { /* no "./" so just take everything */
+ strcpy(scratch, source);
+ }
+ strcpy(source, pwd);
+ (void) strcat(source, "/");
+ (void) strcat(source, scratch);
+ }
+
+ /*
+ * Clean up any "../"s that are in the path
+ */
+ while (cleanup_dotdot_path(source));
+
+ /*
+ * source is now an absolute path to the link we're
+ * concerned with
+ *
+ * S10: Do NOT ignore dangling links, pointing to devfs nodes.
+ */
+ if (strstr(source, "/devices")) {
+ return (g_alloc_string(source));
+ }
+
+ if (lstat(source, &stbuf) == -1) {
+ O_DPRINTF("lstat() failed for - %s\n",
+ source, strerror(errno));
+ return (NULL);
+ }
+ /*
+ * If the file is not a link, we're done one
+ * way or the other. If there were links,
+ * return the full pathname of the resulting
+ * file.
+ *
+ * Note: All of our temp's are on the stack,
+ * so we have to copy the final result to the heap.
+ */
+ if (!S_ISLNK(stbuf.st_mode)) {
+ return (g_alloc_string(source));
+ }
+ cnt = readlink(source, scratch, sizeof (scratch));
+ if (cnt < 0) {
+ O_DPRINTF("readlink() failed - %s\n",
+ strerror(errno));
+ return (NULL);
+ }
+ /*
+ * scratch is on the heap, and for some reason readlink
+ * doesn't always terminate things properly so we have
+ * to make certain we're properly terminated
+ */
+ scratch[cnt] = '\0';
+
+ /*
+ * Now check to see if the link is relative. If so,
+ * then we have to append it to the directory
+ * which the source was in. (This is non trivial)
+ */
+ if (scratch[0] != '/') {
+ tmp = strrchr(source, '/');
+ if (tmp == NULL) { /* Whoa! Something's hosed! */
+ O_DPRINTF("Internal error... corrupt path.\n");
+ return (NULL);
+ }
+ /* Now strip off just the directory path */
+ *(tmp+1) = '\0'; /* Keeping the last '/' */
+ /* and append the new link */
+ (void) strcat(source, scratch);
+ /*
+ * Note: At this point, source should have "../"s
+ * but we'll clean it up in the next pass through
+ * the loop.
+ */
+ } else {
+ /* It's an absolute link so no worries */
+ strcpy(source, scratch);
+ }
+ }
+ /* Never reach here */
+}
+
+/*
+ * Function for getting physical pathnames
+ *
+ * For S10 the physical path may not exist at the time devctl calls
+ * are made. So we should not return error if stat fails on /devices path.
+ *
+ * This function can handle 2 different inputs.
+ *
+ * 1) Inputs of the form /dev/rdsk/cNtNdNsN
+ * These are identified by being a link
+ * The physical path they are linked to is returned.
+ *
+ * 2) Inputs of the form /devices/...
+ * These are actual physical names.
+ * They are not converted.
+ */
+char *
+h_get_physical_name(char *path)
+{
+ struct stat stbuf;
+ char s[MAXPATHLEN];
+ char savedir[MAXPATHLEN];
+ char *result = NULL;
+ int status = 0;
+
+ /* return invalid path if path NULL */
+ if (path == NULL) {
+ return (NULL);
+ }
+
+ (void) strcpy(s, path);
+
+ status = lstat(s, &stbuf);
+
+ /*
+ * S10: If string is devfs node we allow failed lstat.
+ */
+ if ((status == -1) || !S_ISLNK(stbuf.st_mode)) {
+ /* Make sure a full path as that is required. */
+ if (strstr(s, "/devices")) {
+ result = g_alloc_string(s);
+ } else {
+ if (getcwd(savedir,
+ sizeof (savedir)) == NULL) {
+ return (NULL);
+ }
+ /*
+ * Check for this format:
+ * ./ssd@0,1:g,raw
+ */
+ if (s[0] == '.') {
+ (void) strcat(savedir, &s[1]);
+ } else {
+ (void) strcat(savedir, "/");
+ (void) strcat(savedir, s);
+ }
+ if ((status != -1) || strstr(s, "/devices")) {
+ result = g_alloc_string(savedir);
+ }
+ }
+ } else {
+ /*
+ * Entry is linked file
+ * so follow link to physical name
+ */
+ result = h_get_physical_name_from_link(path);
+ }
+
+exit:
+ return (result);
+}
+
+
+/*
+ * handle expert-mode hotplug commands
+ *
+ * return 0 iff all is okay
+ */
+int
+hotplug_e(int todo, char **argv, int verbose_flag, int force_flag)
+{
+char *path_phys = NULL;
+char bus_path[MAXPATHLEN];
+char *ptr;
+int exit_code;
+devctl_hdl_t dcp;
+uint_t devstate;
+int i = 0, pathcnt = 1;
+mp_pathlist_t pathlist;
+int p_pw = 0, p_on = 0, p_st = 0;
+
+
+ switch (todo) {
+ case DEV_ONLINE:
+ case DEV_OFFLINE:
+ case DEV_GETSTATE:
+ case DEV_RESET:
+ /* get physical name */
+ if ((path_phys = h_get_physical_name(argv[0])) == NULL) {
+
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[0]);
+ (void) fprintf(stderr, "\n");
+ return (1);
+ }
+
+ if (verbose_flag) {
+ (void) fprintf(stdout,
+ MSGSTR(5516,
+ "phys path = \"%s\"\n"),
+ path_phys);
+ }
+
+ /* acquire rights to hack on device */
+ if ((dcp = devctl_device_acquire(path_phys,
+ force_flag ? 0 : DC_EXCL)) == NULL) {
+
+ (void) fprintf(stderr, MSGSTR(5517,
+ "Error: can't acquire \"%s\": %s\n"),
+ path_phys, strerror(errno));
+ return (1);
+ }
+
+ switch (todo) {
+ case DEV_ONLINE:
+ exit_code = devctl_device_online(dcp);
+ break;
+ case DEV_OFFLINE:
+ exit_code = devctl_device_offline(dcp);
+ break;
+ case DEV_GETSTATE:
+ if ((exit_code = devctl_device_getstate(dcp,
+ &devstate)) == 0) {
+ print_dev_state(argv[0], devstate);
+ }
+ break;
+ case DEV_RESET:
+ exit_code = devctl_device_reset(dcp);
+ break;
+ }
+
+ if (exit_code != 0) {
+ perror(MSGSTR(5518, "devctl"));
+ }
+
+ /* all done now -- release device */
+ devctl_release(dcp);
+ break;
+
+ /* for hotplugging bus operations */
+ case BUS_QUIESCE:
+ case BUS_UNQUIESCE:
+ case BUS_GETSTATE:
+ case BUS_RESET:
+ case BUS_RESETALL:
+ /* get physical name */
+ if ((path_phys = h_get_physical_name(argv[0])) ==
+ NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ argv[0]);
+ (void) fprintf(stderr, "\n");
+ return (1);
+ }
+ if (verbose_flag) {
+ printf(MSGSTR(5519, "phys path = \"%s\"\n"), path_phys);
+ }
+
+ /* acquire rights to hack on device */
+ /* delete leaf part from path_phys. */
+ if (strstr(path_phys, SCSI_VHCI) != NULL) {
+ /* obtain phci */
+ (void) strcpy(bus_path, path_phys);
+ if (g_get_pathlist(bus_path, &pathlist)) {
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ path_phys);
+ (void) fprintf(stderr, "\n");
+ return (1);
+ }
+ pathcnt = pathlist.path_count;
+ p_pw = p_on = p_st = 0;
+ for (i = 0; i < pathcnt; i++) {
+ if (pathlist.path_info[i].path_state <
+ MAXPATHSTATE) {
+ if (strstr(pathlist.path_info[i].
+ path_addr,
+ argv[0]) != NULL) {
+ p_pw = i;
+ break;
+ }
+ if (pathlist.path_info[i].path_state ==
+ MDI_PATHINFO_STATE_ONLINE) {
+ p_on = i;
+ }
+ if (pathlist.path_info[i].path_state ==
+ MDI_PATHINFO_STATE_STANDBY) {
+ p_st = i;
+ }
+ }
+ }
+ if (strstr(pathlist.path_info[p_pw].path_addr,
+ argv[0]) != NULL) {
+ /* matching input pwwn */
+ (void) strcpy(bus_path,
+ pathlist.path_info[p_pw].path_hba);
+ } else if (pathlist.path_info[p_on].path_state ==
+ MDI_PATHINFO_STATE_ONLINE) {
+ /* on_line path */
+ (void) strcpy(bus_path,
+ pathlist.path_info[p_on].path_hba);
+ } else {
+ /* standby or path0 */
+ (void) strcpy(bus_path,
+ pathlist.path_info[p_st].path_hba);
+ }
+ free(pathlist.path_info);
+ } else {
+
+ (void) strcpy(bus_path, path_phys);
+ ptr = strrchr(bus_path, '/');
+ if (ptr) {
+ *ptr = '\0';
+ } else {
+ (void) fprintf(stderr,
+ MSGSTR(112, "Error: Invalid pathname (%s)"),
+ path_phys);
+ (void) fprintf(stderr, "\n");
+ return (1);
+ }
+ }
+
+ if ((dcp = devctl_bus_acquire(bus_path,
+ force_flag ? 0 : DC_EXCL)) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(5521,
+ " Error: can't acquire bus node from"
+ " the path \"%s\": %s\n"),
+ bus_path, strerror(errno));
+ return (1);
+ }
+
+ switch (todo) {
+ case BUS_QUIESCE:
+ exit_code = devctl_bus_quiesce(dcp);
+ break;
+ case BUS_UNQUIESCE:
+ exit_code = devctl_bus_unquiesce(dcp);
+ break;
+ case BUS_GETSTATE:
+ if ((exit_code = devctl_bus_getstate(dcp,
+ &devstate)) == 0) {
+ print_bus_state(argv[0], devstate);
+ }
+ break;
+ case BUS_RESET:
+ exit_code = devctl_bus_reset(dcp);
+ break;
+ case BUS_RESETALL:
+ exit_code = devctl_bus_resetall(dcp);
+ break;
+ }
+
+ if (exit_code != 0) {
+ perror(MSGSTR(5522, "devctl"));
+ }
+
+ /* all done now -- release device */
+ devctl_release(dcp);
+ break;
+ }
+
+ return (exit_code);
+}
+
+
+
+/*
+ * Prepares an individual FC_AL device
+ * to be removed from the specified
+ * slot.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise.
+ */
+static int
+h_pre_remove_dev(Hotplug_Devlist *hotplug_disk, WWN_list *wwn_list,
+ int verbose_flag, int force_flag)
+{
+char *dev_path, device_name[MAXNAMELEN];
+int err;
+
+ /* Initialize pointers */
+ dev_path = NULL;
+
+ if (hotplug_disk->dlhead != NULL) {
+ dev_path = hotplug_disk->dlhead->dev_path;
+ (void) strcpy(device_name, (hotplug_disk->dlhead)->logical_path);
+ }
+ (void) fprintf(stdout,
+ MSGSTR(157,
+ "stopping: %s...."), device_name);
+ if (!(strstr(dev_path, SLSH_DRV_NAME_ST))) {
+ if ((err = g_dev_stop(dev_path, wwn_list, verbose_flag)) != 0)
+ return (err);
+ }
+ (void) fprintf(stdout, MSGSTR(156, "Done\n"));
+
+ (void) fprintf(stdout,
+ MSGSTR(158, "offlining: %s...."), device_name);
+ if ((err = g_offline_drive(hotplug_disk->dlhead,
+ force_flag)) != 0) {
+ (void) fprintf(stdout,
+ MSGSTR(160,
+ "\nonlining: %s\n"), device_name);
+
+ (void) g_online_drive(hotplug_disk->dlhead, force_flag);
+ (void) fprintf(stdout,
+ MSGSTR(159, "starting: %s...."),
+ device_name);
+ if ((err = g_dev_start(dev_path, 0)) != 0) {
+ return (err);
+ }
+ (void) fprintf(stdout, MSGSTR(156, "Done\n"));
+ return (err);
+ }
+ (void) fprintf(stdout, MSGSTR(156, "Done\n"));
+ return (0);
+}
+
+
+
+/*
+ * Prepares a SENA enclosure or SENA FC_AL device
+ * to be inserted/removed from a specified slot.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise.
+ */
+static int
+h_pre_hotplug_sena(Hotplug_Devlist *hotplug_dev,
+ WWN_list *wwn_list, int todo,
+ int verbose_flag, int force_flag)
+{
+int slot, f_r, i, found_null_wwn = 0, err;
+char *ses_path, *dev_path, code;
+char node_wwn_s[WWN_SIZE], device_name[MAXNAMELEN];
+struct l_state_struct l_state;
+struct dlist *dl;
+
+
+ if (hotplug_dev->dev_type == DTYPE_ESI) {
+ /* entire photon is being removed */
+ if ((err = l_offline_photon(hotplug_dev, wwn_list,
+ force_flag, verbose_flag)) != 0) {
+ return (err);
+ }
+ return (0);
+ }
+
+ /* if device is an individual sena disk */
+ dl = hotplug_dev->seslist;
+ while (dl) {
+ ses_path = dl->dev_path;
+ if ((err = l_get_status(ses_path, &l_state,
+ verbose_flag)) == 0)
+ break;
+ dl = dl->next;
+ }
+ if (dl == NULL) {
+ return (L_GET_STATUS_FAILED);
+ }
+
+ f_r = hotplug_dev->f_flag;
+ slot = hotplug_dev->slot;
+ (void) l_get_drive_name(device_name, slot, f_r, hotplug_dev->box_name);
+
+ /* check if disk has null wwn */
+ if (f_r) {
+ (void) strncpy(node_wwn_s,
+ l_state.drv_front[slot].g_disk_state.node_wwn_s, WWN_SIZE);
+ } else {
+ (void) strncpy(node_wwn_s,
+ l_state.drv_rear[slot].g_disk_state.node_wwn_s, WWN_SIZE);
+ }
+ for (i = 0; i < WWN_SIZE; i++) {
+ if (node_wwn_s[i] != '0')
+ break;
+ found_null_wwn = 1;
+ }
+
+ switch (todo) {
+ case INSERT_DEVICE:
+ if (hotplug_dev->f_flag) {
+ code =
+ l_state.drv_front[slot].ib_status.code;
+ } else {
+ code =
+ l_state.drv_rear[slot].ib_status.code;
+ }
+ if (code & S_NOT_INSTALLED) {
+ /*
+ * At this point we know that the drive is not
+ * there. Turn on the RQST INSERT bit to make
+ * the LED blink
+ */
+ if ((err = l_encl_status_page_funcs
+ (SET_RQST_INSRT, 0, todo,
+ ses_path, &l_state, f_r, slot,
+ verbose_flag)) != 0) {
+ (void) print_errString(err,
+ device_name);
+ (void) fprintf(stderr,
+ MSGSTR(5530,
+ " %s: could not turn "
+ "on LED\n"),
+ device_name);
+ }
+ } else {
+ /*
+ * Drive is there so start it.
+ */
+ if ((err = l_encl_status_page_funcs
+ (SET_DRV_ON, 0, todo,
+ ses_path, &l_state, f_r, slot,
+ verbose_flag)) != 0) {
+ (void) print_errString(err,
+ device_name);
+ (void) fprintf(stderr,
+ MSGSTR(5531,
+ " could not enable"
+ " %s\n"),
+ device_name);
+ }
+ }
+ break;
+
+ case REMOVE_DEVICE:
+ /*
+ * if disk has null wwn, then
+ * there is no need to check the
+ * disk/loop status.
+ */
+ if (found_null_wwn == 1) {
+ if (getenv("_LUX_W_DEBUG") != NULL) {
+ (void) fprintf(stdout,
+ "Device %s has "
+ "null WWN.\n",
+ device_name);
+ }
+ goto rmv;
+ }
+ if (hotplug_dev->f_flag) {
+ if (
+ l_state.drv_front[slot].ib_status.code
+ == S_NOT_INSTALLED) {
+ (void) fprintf(stderr,
+ MSGSTR(86,
+ " Notice: %s may already"
+ " be removed.\n"),
+ device_name);
+ return (0);
+ }
+ } else if (
+ l_state.drv_rear[slot].ib_status.code
+ == S_NOT_INSTALLED) {
+ (void) fprintf(stderr,
+ MSGSTR(86,
+ " Notice: %s may already"
+ " be removed.\n"),
+ device_name);
+ return (0);
+ }
+
+rmv:
+ if (hotplug_dev->dlhead == NULL) {
+ dev_path = NULL;
+ } else {
+ dev_path = hotplug_dev->dlhead->dev_path;
+ }
+
+ (void) fprintf(stdout,
+ MSGSTR(157,
+ "stopping: %s...."), device_name);
+ if ((err = g_dev_stop(dev_path, wwn_list, 0)) != 0) {
+ return (err);
+ }
+ (void) fprintf(stdout, MSGSTR(156, "Done\n"));
+
+ (void) fprintf(stdout,
+ MSGSTR(158, "offlining: %s...."),
+ device_name);
+ if ((err = g_offline_drive(hotplug_dev->dlhead,
+ force_flag)) != 0) {
+ (void) fprintf(stdout,
+ MSGSTR(160,
+ "\nonlining: %s\n"), device_name);
+ (void) g_online_drive(hotplug_dev->dlhead, force_flag);
+
+ (void) fprintf(stdout,
+ MSGSTR(159, "starting: %s...."),
+ device_name);
+ (void) g_dev_start(dev_path, 0);
+ (void) fprintf(stdout, MSGSTR(156, "Done\n"));
+ return (err);
+ }
+ (void) fprintf(stdout, MSGSTR(156, "Done\n"));
+
+ /*
+ * Take the drive off the loop
+ * and blink the LED.
+ */
+ if (hotplug_dev->dev_location == SENA) {
+ if ((err = l_encl_status_page_funcs(SET_RQST_RMV, 0,
+ todo, ses_path, &l_state, f_r,
+ slot, verbose_flag)) != 0) {
+ (void) print_errString(err, device_name);
+ (void) fprintf(stderr,
+ MSGSTR(5539,
+ " %s: could not blink"
+ " the yellow LED\n"),
+ device_name);
+ }
+ }
+ break;
+ }
+ return (0);
+}
+
+
+
+/*
+ * Performs the post removal operations for
+ * a SENA enclosure or a SENA FC_AL disk.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static int
+h_post_hotplug_sena(Hotplug_Devlist *hotplug_dev,
+ WWN_list *wwn_list, int todo,
+ int verbose_flag, int force_flag, int enc_type)
+{
+char *ses_path, *dev_path = NULL, device_name[MAXNAMELEN];
+int tid, slot, f_r, al_pa, timeout = 0;
+uchar_t port_wwn[WWN_SIZE], node_wwn[WWN_SIZE];
+char code;
+int wait_spinup_flag = 0, wait_map_flag = 0;
+int wait_node_flag = 0, err = 0, nArg;
+gfc_map_t map;
+WWN_list *newWwn_list = NULL;
+struct dlist *dl, *dl1;
+struct l_state_struct l_state;
+
+
+ dl = hotplug_dev->seslist;
+ slot = hotplug_dev->slot;
+ f_r = hotplug_dev->f_flag;
+ tid = hotplug_dev->tid;
+
+ if (hotplug_dev->dev_type == DTYPE_ESI) {
+ /*
+ * See if photon has really been removed. If not,
+ * try onlining the devices if applicable
+ */
+ H_DPRINTF(" post_hotplug_sena: Seeing if enclosure "
+ "has really been removed:\n"
+ " tid=0x%x, ses_path %s\n",
+ tid, dl->dev_path);
+
+ while (dl) {
+ ses_path = dl->dev_path;
+ if ((err = g_get_dev_map(ses_path, &map, 0)) == 0) {
+ if ((map.hba_addr.port_topology ==
+ FC_TOP_PUBLIC_LOOP) ||
+ (map.hba_addr.port_topology ==
+ FC_TOP_FABRIC)) {
+ /* public or fabric loop device */
+ free((void *)map.dev_addr);
+ (void) fprintf(stdout, MSGSTR(5540,
+ "This operation is not "
+ "supported in this topology.\n"));
+ return (0);
+ }
+ if ((err = g_get_wwn(ses_path, port_wwn,
+ node_wwn, &al_pa, verbose_flag)) == 0) {
+ tid = g_sf_alpa_to_switch[al_pa];
+ if (g_device_in_map(&map, tid)) {
+ free((void *)map.dev_addr);
+ break;
+ }
+ }
+ FREE_DEV_ADDR(map.dev_addr);
+ }
+
+ dl = dl->next;
+ }
+ FREE_DEV_ADDR(map.dev_addr);
+ if (dl) {
+ (void) fprintf(stdout, MSGSTR(5640,
+ "Photon \"%s\" not removed."
+ " Onlining Drives in enclosure.\n"),
+ hotplug_dev->box_name);
+ for (dl = hotplug_dev->dlhead; dl; ) {
+ (void) g_online_drive(dl->multipath,
+ force_flag);
+ (void) g_free_multipath(dl->multipath);
+ dl1 = dl;
+ dl = dl->next;
+ (void) free(dl1);
+ }
+ hotplug_dev->dlhead = NULL;
+ return (0);
+ }
+ /*
+ * Remove logical nodes for this
+ * photon, this includes ses and
+ * /dev/dsk entries.
+ * In Solaris7, disks with -C option
+ * removes the /dev/dsk entries.
+ * The -C option is available
+ * only for Solaris7. From Solaris8
+ * or higher releases, the "disks"
+ * program will be replaced by the
+ * devfsadm program.
+ */
+ /* pass "disks -C" as cmdStrg. */
+ nArg = 2;
+ if (h_execCmnd(cmdStrg[0], nArg) != 0) {
+ for (dl = hotplug_dev->dlhead;
+ dl != NULL; dl = dl->next) {
+ if ((err = h_remove_nodes(dl->multipath))
+ != 0) {
+ return (err);
+ }
+ }
+ } else {
+ (void) fprintf(stdout,
+ MSGSTR(5541,
+ " Logical Nodes being removed"
+ " under /dev/dsk/ and /dev/rdsk:\n"));
+ for (dl = hotplug_dev->dlhead;
+ dl != NULL; dl = dl->next) {
+ (void) h_print_logical_nodes(dl->multipath);
+ }
+ }
+
+ for (dl = hotplug_dev->dlhead; dl != NULL; ) {
+ (void) g_free_multipath(dl->multipath);
+ dl1 = dl;
+ dl = dl->next;
+ (void) free(dl1);
+ }
+ hotplug_dev->dlhead = NULL;
+ if ((err = h_remove_ses_nodes(hotplug_dev->seslist)) != 0) {
+ return (err);
+ }
+ return (0);
+ }
+
+ /* post hotplug operations for a SENA disk. */
+ if (enc_type == DAK_ENC_TYPE) {
+ (void) sprintf(device_name, MSGSTR(5664,
+ " Drive in Box Name \"%s\" slot %d"),
+ hotplug_dev->box_name,
+ f_r ? slot : slot + (MAX_DRIVES_DAK/2));
+ } else {
+ if (tid & 0x10) {
+ (void) sprintf(device_name, MSGSTR(5542,
+ " Drive in Box Name \"%s\" rear slot %d"),
+ hotplug_dev->box_name, slot);
+ } else {
+ (void) sprintf(device_name, MSGSTR(5543,
+ " Drive in Box Name \"%s\" front slot %d"),
+ hotplug_dev->box_name, slot);
+ }
+ }
+ (void) fprintf(stdout, "%s\n", device_name);
+
+ dl = hotplug_dev->seslist;
+ while (dl) {
+ ses_path = dl->dev_path;
+ if ((err = l_get_status(ses_path, &l_state,
+ verbose_flag)) == 0)
+ break;
+ dl = dl->next;
+ }
+ if (dl == NULL) {
+ print_errString(err, ses_path);
+ return (L_GET_STATUS_FAILED);
+ }
+
+ code = 0;
+ while (((err = l_encl_status_page_funcs(OVERALL_STATUS,
+ &code, todo, ses_path, &l_state, f_r, slot,
+ verbose_flag)) != 0) || (code != 0)) {
+ if (err) {
+ (void) print_errString(err, ses_path);
+ } else if (todo == REMOVE_DEVICE) {
+ if (code == S_OK) {
+ (void) fprintf(stderr,
+ MSGSTR(5544,
+ "\n Warning: Device has not been"
+ " removed from the enclosure\n"
+ " and is still on the loop."));
+ return (0);
+ } else {
+ (void) fprintf(stderr,
+ MSGSTR(5545,
+ " Notice: Device has not been"
+ " removed from the enclosure.\n"
+ " It has been removed from the"
+ " loop and is ready to be\n"
+ " removed"
+ " from the enclosure, and"
+ " the LED is blinking.\n\n"));
+ }
+ goto loop2;
+ } else if ((todo == INSERT_DEVICE) &&
+ ((code != S_NOT_AVAILABLE) ||
+ (timeout >
+ PHOTON_SPINUP_TIMEOUT) ||
+ err)) {
+ (void) fprintf(stderr,
+ MSGSTR(5546,
+ "\n Warning: Disk status is"
+ " Not OK!\n\n"));
+ return (0);
+ }
+ (void) sleep(PHOTON_SPINUP_DELAY);
+ if (wait_spinup_flag++ == 0) {
+ (void) fprintf(stdout, MSGSTR(5547,
+ " Waiting for the disk to spin up:"));
+ } else {
+ (void) fprintf(stdout, ".");
+ }
+ timeout++;
+ }
+ if (wait_spinup_flag) {
+ (void) fprintf(stdout, "\n");
+ }
+loop2:
+ switch (todo) {
+ case INSERT_DEVICE:
+ /* check loop map that drive is present */
+ for (;;) {
+ dl = hotplug_dev->seslist;
+ map.dev_addr = (gfc_port_dev_info_t *)NULL;
+ while (dl) {
+ ses_path = dl->dev_path;
+ if ((err = g_get_dev_map(ses_path,
+ &map, verbose_flag)) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(5548,
+ " Error: Could not get"
+ " map for %s.\n"),
+ ses_path);
+ return (err);
+ }
+ if (g_device_in_map(&map, tid)) {
+ goto loop3;
+ }
+ FREE_DEV_ADDR(map.dev_addr);
+ dl = dl->next;
+ }
+ if (timeout > PHOTON_SPINUP_TIMEOUT) {
+ (void) fprintf(stderr,
+ MSGSTR(5549,
+ " Warning: Device not in"
+ " loop map.\n"));
+ FREE_DEV_ADDR(map.dev_addr);
+ return (0);
+ }
+ if (wait_map_flag++ == 0) {
+ (void) fprintf(stdout,
+ MSGSTR(5550,
+ " Waiting for the device "
+ "to appear in the loop map:"));
+ } else {
+ (void) fprintf(stdout, ".");
+ }
+ timeout++;
+ (void) sleep(PHOTON_SPINUP_DELAY);
+ }
+loop3:
+ if (wait_map_flag) {
+ (void) fprintf(stdout, "\n");
+ }
+
+ /*
+ * Run drvconfig and disks to create
+ * logical nodes
+ */
+ for (;;) {
+ /* pass "disks" as cmdStrg */
+ nArg = 3;
+ if (h_execCmnd(cmdStrg[2], nArg) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(5551,
+ " Could not "
+ "run drvconfig.\n"));
+ FREE_DEV_ADDR(map.dev_addr);
+ return (L_DRVCONFIG_ERROR);
+ }
+
+ if (l_device_present(ses_path, tid, &map,
+ verbose_flag, &dev_path) == 1)
+ break;
+ if (timeout > PHOTON_SPINUP_TIMEOUT) {
+ (void) fprintf(stderr,
+ MSGSTR(5552,
+ " Warning: Could not find "
+ "any node for inserted "
+ "device\n"));
+ FREE_DEV_ADDR(map.dev_addr);
+ return (0);
+ }
+ if (wait_node_flag++ == 0) {
+ (void) fprintf(stdout,
+ MSGSTR(5553,
+ " Waiting for the logical "
+ "node to be created:"));
+ } else {
+ (void) fprintf(stdout, ".");
+ }
+ timeout++;
+ (void) sleep(PHOTON_SPINUP_DELAY);
+ }
+ FREE_DEV_ADDR(map.dev_addr);
+ if (wait_node_flag) {
+ (void) fprintf(stdout, "\n");
+ }
+ /*
+ * In Solaris7, disks with -C
+ * option creates the new links
+ * and removes any stale links.
+ * In pre-Solaris7 releases, just
+ * disks should do it all.
+ */
+ /* pass "disks -C" as cmdStrg */
+ nArg = 2;
+ if (h_execCmnd(cmdStrg[0], nArg) != 0) {
+ return (L_DISKS_ERROR);
+ }
+ /*
+ * Get a new wwn list here in order to
+ * get the multiple paths to a newly added
+ * device.
+ */
+ if ((err = g_get_wwn_list(&newWwn_list,
+ verbose_flag)) != 0) {
+ return (err);
+ }
+ if ((err = g_get_multipath(dev_path, &dl,
+ newWwn_list, 0)) != 0) {
+ return (err);
+ }
+ if ((err = h_display_logical_nodes(dl)) != 0) {
+ return (err);
+ }
+ break;
+
+ case REMOVE_DEVICE:
+/*
+ * TBD
+ * Need to check all loops.
+ */
+ /* check whether device is still in loop map */
+ if ((err = g_get_dev_map(ses_path, &map,
+ verbose_flag)) != 0) {
+ return (err);
+ }
+
+ if ((map.hba_addr.port_topology ==
+ FC_TOP_PUBLIC_LOOP) ||
+ (map.hba_addr.port_topology ==
+ FC_TOP_FABRIC)) {
+ /* public or fabric loop device */
+ free((void *)map.dev_addr);
+ (void) fprintf(stderr, MSGSTR(5540,
+ "This operation is not "
+ "supported in this topology.\n"));
+ /*
+ * calling routine expects a 0 return code
+ * or a pre-defined luxadm error code.
+ * Here we do not have a pre-defined error
+ * code, a 0 is returned.
+ */
+ return (0);
+ }
+
+ if (g_device_in_map(&map, tid)) {
+ (void) fprintf(stderr, MSGSTR(5554,
+ " Warning: Device still in the loop map.\n"));
+ FREE_DEV_ADDR(map.dev_addr);
+ return (0);
+ }
+ FREE_DEV_ADDR(map.dev_addr);
+ /*
+ * In Solaris7, "disks -C" program
+ * removes the /dev/{r}dsk entries.
+ * The -C option is available only
+ * for Solaris7. From Solaris8 or
+ * higher releases, the "disks" program
+ * will be replaced by devfsadm.
+ */
+ /* pass "disks -C" as cmdStrg */
+ nArg = 2;
+ if (h_execCmnd(cmdStrg[0], nArg) != 0) {
+ return (L_DISKS_ERROR);
+ }
+ (void) fprintf(stdout,
+ MSGSTR(5555,
+ " Logical Nodes being removed"
+ " under /dev/dsk/ and /dev/rdsk:\n"));
+ (void) h_print_logical_nodes(
+ hotplug_dev->dlhead);
+ break;
+ }
+ return (0);
+}
+
+
+
+
+/*
+ * Creates new ses entries under /dev/es
+ * directory for the newly added
+ * enclosures.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static int
+h_post_insert_encl(timestruc_t ses_lastmtim)
+{
+struct stat ses_stat;
+char lname[MAXPATHLEN];
+int err, found_newlink = 0;
+DIR *dir;
+struct dirent *dirent;
+Box_list *bl1, *box_list = NULL;
+
+
+ if ((dir = opendir(SES_DIR)) == NULL) {
+ return (L_OPEN_ES_DIR_FAILED);
+ }
+ if ((err = l_get_box_list(&box_list, 0)) != 0) {
+ closedir(dir);
+ return (err);
+ }
+
+ /*
+ * The mod time of /dev/es was newer than the mod time prior to
+ * insert so dir entry is checked at this time.
+ */
+ while ((dirent = readdir(dir)) != (struct dirent *)NULL) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0)
+ continue;
+
+ (void) sprintf(lname, SES_DIR"/%s", dirent->d_name);
+ if (lstat(lname, &ses_stat) < 0) {
+ (void) print_errString(L_LSTAT_ES_DIR_ERROR,
+ lname);
+ continue;
+ }
+
+ for (bl1 = box_list; bl1; bl1 = bl1->box_next) {
+ if (strstr(lname, bl1->b_physical_path))
+ break;
+ }
+
+ if (box_list && bl1)
+ continue;
+
+ if (NEWER(ses_stat.st_ctim, ses_lastmtim)) {
+ /* New enclosure was detected. */
+ found_newlink++;
+ if (found_newlink == 1) {
+ (void) fprintf(stdout, MSGSTR(5556,
+ " New Logical Nodes under /dev/es:\n"));
+ }
+ (void) fprintf(stdout, "\t%s\n",
+ dirent->d_name);
+ }
+ }
+ if (!found_newlink) {
+ (void) fprintf(stdout, MSGSTR(5662,
+ " No new enclosure(s) were added!!\n\n"));
+ }
+
+ closedir(dir);
+
+ (void) l_free_box_list(&box_list);
+ return (0);
+}
+
+
+
+/*
+ * performs the post removal of individual
+ * FC_AL disks.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static int
+h_post_remove_dev(Hotplug_Devlist *hotplug_disk,
+ int todo, int verbose_flag)
+{
+char device_name[MAXNAMELEN], *dev_path = NULL;
+int tid, err;
+gfc_map_t map;
+int nArg;
+
+
+ tid = hotplug_disk->tid;
+ (void) sprintf(device_name,
+ MSGSTR(5557,
+ "\n Device: %s"),
+ (hotplug_disk->dlhead)->logical_path);
+
+ (void) fprintf(stdout, "%s\n", device_name);
+
+ dev_path = (hotplug_disk->dlhead)->dev_path;
+
+ /*
+ * On qlc, after a forcelip on a FC combo box, it sometimes takes 17
+ * seconds for the loop to come back online. During this 17 seconds,
+ * g_get_dev_map * will return L_NO_DEVICES_FOUND. This delay
+ * has been added to assure that the L_NO_DEVICES_FOUND returned from
+ * g_get_dev_map is not the result of the 17 second delay on FC combo.
+ * This only affects qlc.
+ */
+ if ((err = g_get_dev_map(dev_path, &map, verbose_flag)) != 0) {
+ if ((err == L_NO_DEVICES_FOUND) &&
+ (strstr(dev_path, "SUNW,qlc@") != NULL)) {
+ sleep(QLC_LIP_DELAY);
+ if ((err = g_get_dev_map(dev_path, &map, verbose_flag))
+ != 0) {
+ if (err != L_NO_DEVICES_FOUND)
+ return (err);
+ }
+ } else if (err != L_NO_DEVICES_FOUND)
+ return (err);
+ }
+
+ /*
+ * if g_get_dev_map returns L_NO_DEVICES_FOUND, then there are not
+ * devices attached to the HBA and there is no sense in calling
+ * g_device_in_map().
+ */
+ if (err != L_NO_DEVICES_FOUND) {
+ if ((map.hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) ||
+ (map.hba_addr.port_topology == FC_TOP_FABRIC)) {
+ /* public or fabric loop device */
+ free((void *)map.dev_addr);
+ (void) fprintf(stderr, MSGSTR(5540,
+ "This operation is not "
+ "supported in this topology.\n"));
+ return (0);
+ }
+
+ if (g_device_in_map(&map, tid) != 0) {
+ (void) fprintf(stderr,
+ MSGSTR(5558,
+ " Warning: Device has"
+ " not been removed from\n"
+ " the slot and is still"
+ " in the loop map.\n\n"));
+ free((void *)map.dev_addr);
+ return (0);
+ }
+ free((void *)map.dev_addr);
+ }
+ /*
+ * In Solaris7, "disks -C" program
+ * removes the /dev/{r}dsk entries.
+ * The -C option is available only
+ * for Solaris7. From Solaris8 or
+ * higher releases, the "disks" program
+ * will be replaced by devfsadm.
+ */
+ /* pass "disks -C" as cmdStrg. */
+ nArg = 2;
+ if (h_execCmnd(cmdStrg[0], nArg) != 0) {
+ return (L_DISKS_ERROR);
+ }
+ /* pass "tapes -C as cmdStrg. */
+ if (h_execCmnd(cmdStrg[5], nArg) != 0) {
+ return (L_TAPES_ERROR);
+ }
+ (void) h_print_logical_nodes(hotplug_disk->dlhead);
+
+ return (0);
+}
+
+
+
+/*
+ * Gets the last modification time for
+ * /dev/es/ and /dev/rdsk directories
+ * and passes these values to the caller.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero in case of error
+ */
+static int
+h_pre_insert_encl_dev(timestruc_t *ses_time, timestruc_t *dsk_time,
+ timestruc_t *rmt_time)
+{
+struct stat ses_stat, dsk_stat, rmt_stat;
+
+ if (stat(SES_DIR, &ses_stat) < 0) {
+ /*
+ * Even if there exists no /dev/es don't fail it.
+ * The host doesn't have to have any enclosure device
+ * configured.
+ */
+ if (errno == ENOENT) {
+ ses_time = (timestruc_t *)NULL;
+ } else {
+ return (L_LSTAT_ES_DIR_ERROR);
+ }
+ } else {
+ *ses_time = ses_stat.st_mtim;
+ }
+
+ if (stat(DEV_DSK_DIR, &dsk_stat) < 0) {
+ return (L_STAT_DEV_DIR_ERROR);
+ } else {
+ *dsk_time = dsk_stat.st_mtim;
+ }
+ if (stat(DEV_TAPE_DIR, &rmt_stat) < 0) {
+ /*
+ * Even if there exists no /dev/rmt don't fail it.
+ * The host doesn't have to have any tape device
+ * configured.
+ */
+ if (errno == ENOENT) {
+ rmt_time = (timestruc_t *)NULL;
+ } else {
+ return (L_STAT_RMT_DIR_ERROR);
+ }
+ } else {
+ *rmt_time = rmt_stat.st_mtim;
+ }
+
+ return (0);
+}
+
+
+
+/*
+ * Waits for loop intialization to complete
+ * and runs drvconfig, disks and devlinks to create device nodes
+ * for devices that are being added and prints the newly created
+ * /dev/rdsk entries.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero in case of error
+ */
+
+static int
+h_post_insert_dev(timestruc_t dsk_lastmtim, timestruc_t rmt_lastmtim)
+{
+int found_newlink = 0, nArg;
+
+ (void) fprintf(stdout,
+ MSGSTR(5560,
+ "\nWaiting for Loop Initialization to complete...\n"));
+
+ /*
+ * We sleep here to let the system create nodes. Not sleeping
+ * could cause the drvconfig below to run too soon.
+ */
+
+ (void) sleep(NODE_CREATION_TIME);
+
+ /*
+ * Run drvconfig and disks to create
+ * logical nodes
+ */
+ /* pass "drvconfig" as cmdStrg */
+ nArg = 1;
+ if (h_execCmnd(cmdStrg[3], nArg) != 0) {
+ return (L_DRVCONFIG_ERROR);
+ }
+
+ /*
+ * In 2.7, disks with the -C
+ * option should be used to
+ * create new links and remove
+ * any stale links.
+ * In pre-2.7 releases, just
+ * disks should do it all.
+ */
+
+ /* pass "disks -C" as cmdStrg */
+ nArg = 2;
+ if (h_execCmnd(cmdStrg[0], nArg) != 0) {
+ return (L_DISKS_ERROR);
+ }
+ /* pass "tapes -C as cmdStrg */
+ if (h_execCmnd(cmdStrg[5], nArg) != 0) {
+ return (L_TAPES_ERROR);
+ }
+
+ /* pass "devlinks" as cmdStrg */
+ nArg = 1;
+ if (h_execCmnd(cmdStrg[4], nArg) != 0) {
+ return (L_DEVLINKS_ERROR);
+ }
+
+ /* check /dev/dsk and /dev/rmt for new links */
+ found_newlink = h_find_new_device_link(DEV_DSK_DIR, dsk_lastmtim) +
+ h_find_new_device_link(DEV_TAPE_DIR, rmt_lastmtim);
+
+ if (!found_newlink) {
+ (void) fprintf(stdout, MSGSTR(5562,
+ " No new device(s) were added!!\n\n"));
+ }
+
+ return (0);
+}
+
+
+
+/*
+ * Performs the pre hotplug operations on SENA enclosure(s),
+ * SENA disk(s) and individual fcal disk(s).
+ * If the device is failed to remove, then it removes the device from the
+ * hotplug list and continues with the next device in the list.
+ *
+ * RETURNS:
+ * 0 if OK
+ * prints an error message to stderr and returns 0
+ */
+static int
+h_pre_hotplug(Hotplug_Devlist **disk_list_head_ptr,
+ WWN_list *wwn_list, int todo,
+ int verbose_flag, int force_flag)
+{
+Hotplug_Devlist *list, *disk_list;
+int err = 0;
+
+ disk_list = *disk_list_head_ptr;
+ while (disk_list != NULL) {
+ if ((disk_list->dev_type == DTYPE_ESI) ||
+ (disk_list->dev_location == SENA)) {
+ if ((err = h_pre_hotplug_sena(disk_list, wwn_list,
+ todo, verbose_flag, force_flag)) != 0) {
+ (void) print_errString(err,
+ disk_list->dev_name);
+ goto delete;
+ }
+ } else if (disk_list->dev_location == NON_SENA) {
+ if ((err = h_pre_remove_dev(disk_list, wwn_list,
+ verbose_flag, force_flag)) != 0) {
+ (void) print_errString(err,
+ disk_list->dev_name);
+ goto delete;
+ }
+ }
+ disk_list = disk_list->next;
+ continue;
+delete:
+ list = disk_list->prev;
+ if (list != NULL) {
+ list->next = disk_list->next;
+ if (list->next != NULL)
+ list->next->prev = list;
+ }
+ list = disk_list;
+ disk_list = disk_list->next;
+ if (list == *disk_list_head_ptr)
+ *disk_list_head_ptr = disk_list;
+ (void) g_free_multipath(list->seslist);
+ (void) g_free_multipath(list->dlhead);
+ (void) free(list);
+ }
+ return (0);
+}
+
+
+
+/*
+ * Performs the post removal of a list of SENA enclosure(s),
+ * SENA disk(s) and individual fcal disk(s).
+ *
+ * RETURNS:
+ * 0 O.K.
+ * non-zero otherwise
+ */
+static int
+h_post_hotplug(Hotplug_Devlist *hotplug_dlist,
+ WWN_list *wwn_list, int todo,
+ int verbose_flag, int force_flag, int enc_type)
+{
+Hotplug_Devlist *list;
+int err;
+
+ /* Do a lip on every loop so that we get the latest loop maps */
+ if (todo != INSERT_DEVICE) {
+ if ((err = g_forcelip_all(hotplug_dlist)) != 0) {
+ return (err);
+ }
+ }
+
+ while (hotplug_dlist != NULL) {
+ if ((hotplug_dlist->dev_location == SENA) ||
+ (hotplug_dlist->dev_type == DTYPE_ESI)) {
+ if ((err = h_post_hotplug_sena(hotplug_dlist, wwn_list, todo,
+ verbose_flag, force_flag, enc_type)) != 0)
+ (void) print_errString(err, hotplug_dlist->dev_name);
+ } else if (hotplug_dlist->dev_location == NON_SENA) {
+ if ((err = h_post_remove_dev(hotplug_dlist,
+ todo, verbose_flag)) != 0)
+ (void) print_errString(err,
+ hotplug_dlist->dev_name);
+ }
+ list = hotplug_dlist;
+ hotplug_dlist = hotplug_dlist->next;
+ (void) g_free_multipath(list->seslist);
+ (void) g_free_multipath(list->dlhead);
+ (void) free(list);
+ }
+ return (0);
+}
+
+
+/*
+ * removes the device's logical paths.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static int
+h_remove_nodes(struct dlist *dl)
+{
+char link[MAXPATHLEN], path[MAXPATHLEN];
+char lname[MAXPATHLEN], *ptr;
+DIR *dir;
+struct dirent *dirent;
+struct dlist *dlist;
+
+ if ((dir = opendir(DEV_DSK_DIR)) == NULL) {
+ return (L_READ_DEV_DIR_ERROR);
+ }
+ if (dl == NULL) {
+ /* pass "disks" as cmdStrg */
+ if (h_execCmnd(cmdStrg[1], 1) != 0) {
+ return (L_DISKS_ERROR);
+ }
+ }
+
+ (void) fprintf(stdout,
+ MSGSTR(5563,
+ " Removing Logical Nodes: \n"));
+
+ while ((dirent = readdir(dir)) != (struct dirent *)NULL) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0) {
+ continue;
+ }
+ (void) sprintf(lname, DEV_DSK_DIR"/%s", dirent->d_name);
+ if (readlink((const char *)lname, (char *)link,
+ (size_t)MAXPATHLEN) <= 0) {
+ (void) fprintf(stderr,
+ MSGSTR(5564,
+ " Error: Could not read %s\n"),
+ lname);
+ continue;
+ }
+ for (dlist = dl; dlist != NULL; dlist = dlist->next) {
+ (void) strcpy(path, dlist->dev_path);
+ ptr = strrchr(path, ':');
+ if (ptr)
+ *ptr = '\0';
+ if (strstr(link, path)) {
+ (void) unlink(lname);
+ (void) sprintf(lname, "/dev/rdsk/%s",
+ dirent->d_name);
+ (void) fprintf(stdout,
+ MSGSTR(5565,
+ "\tRemoving %s\n"),
+ dirent->d_name);
+ (void) unlink(lname);
+ }
+ }
+ }
+ closedir(dir);
+ return (0);
+}
+
+
+
+/*
+ * removes the SENA's ses paths.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static int
+h_remove_ses_nodes(struct dlist *dlist)
+{
+char link[MAXPATHLEN], lname[MAXPATHLEN];
+DIR *dir;
+struct dirent *dirent;
+struct dlist *dl;
+
+
+ if ((dir = opendir(SES_DIR)) == NULL) {
+ return (L_READ_DEV_DIR_ERROR);
+ }
+
+ (void) fprintf(stdout, MSGSTR(5566, " Removing Ses Nodes:\n"));
+
+ /*
+ * Remove the ses entries
+ * of the form ses<#>
+ * from the /dev/es directory.
+ */
+
+ while ((dirent = readdir(dir)) != (struct dirent *)NULL) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0)
+ continue;
+
+ (void) sprintf(lname, SES_DIR"/%s", dirent->d_name);
+ if (readlink((const char *)lname, (char *)link,
+ (size_t)MAXPATHLEN) <= 0) {
+ (void) fprintf(stderr,
+ MSGSTR(5564,
+ " Error: Could not read %s\n"),
+ lname);
+ continue;
+ }
+ for (dl = dlist; dl != NULL; dl = dl->next) {
+ if (strstr(link, dl->dev_path)) {
+ (void) fprintf(stdout,
+ MSGSTR(5568,
+ "\tRemoving %s\n"),
+ lname);
+ (void) unlink(lname);
+ }
+ }
+ }
+ closedir(dir);
+ (void) g_free_multipath(dlist);
+ return (0);
+}
+
+
+/*
+ * prints the device's logical
+ * paths for disks to stdout.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static void
+h_print_logical_nodes(struct dlist *disk_list)
+{
+char *lpath, *ptr, *buf_ptr, buf[MAXNAMELEN], dev[MAXNAMELEN];
+struct dlist *dlist;
+int i, found_dev = 0;
+char *tape_entries[] = { "", "b", "bn", "c", "cb", "cbn", "cn",
+ "h", "hb", "hbn", "hn", "l", "lb",
+ "lbn", "ln", "m", "mb", "mbn", "mn",
+ "n", "u", "ub", "ubn", "un", NULL};
+
+ for (dlist = disk_list; dlist != NULL; dlist = dlist->next) {
+ lpath = dlist->logical_path;
+ if ((ptr = strrchr(lpath, 'c')) == NULL)
+ continue;
+ (void) strcpy(buf, ptr);
+ if ((ptr = strrchr(buf, 's')) == NULL)
+ continue;
+ *(++ptr) = NULL;
+ found_dev++;
+ if (found_dev == 1)
+ (void) fprintf(stdout,
+ MSGSTR(5559, " Logical Nodes being "
+ "removed under /dev/dsk/ and "
+ "/dev/rdsk:\n"));
+ for (i = 0; i <= 7; i++) {
+ (void) sprintf(dev, "%s%d", buf, i);
+ (void) fprintf(stdout, "\t%s\n", dev);
+ }
+ }
+ found_dev = 0;
+ for (dlist = disk_list; dlist != NULL; dlist = dlist->next) {
+ lpath = dlist->logical_path;
+ if (strstr(lpath, DEV_TAPE_DIR)) {
+ if ((ptr = strrchr(lpath, '/')) == NULL)
+ continue;
+ found_dev++;
+ if (found_dev == 1)
+ (void) fprintf(stdout, "Logical Nodes being "
+ "removed under /dev/rmt:\n");
+ ptr++;
+ buf_ptr = ptr;
+ while (*ptr >= '0' && *ptr <= '9')
+ ptr++;
+ *ptr = NULL;
+ for (i = 0, ptr = tape_entries[0];
+ ptr != NULL;
+ i++, ptr = tape_entries[i]) {
+ (void) sprintf(dev, "%s%s", buf_ptr, ptr);
+ (void) fprintf(stdout, "\t%s\n", dev);
+ }
+ }
+ }
+}
+
+/*
+ * displays logical paths to a
+ * device to stdout.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static int
+h_display_logical_nodes(struct dlist *dlist)
+{
+char link[MAXPATHLEN], path[MAXPATHLEN];
+char lname[MAXPATHLEN], *d1;
+DIR *dir;
+struct dirent *dirent;
+struct dlist *dl;
+
+
+ if ((dir = opendir(DEV_DSK_DIR)) == NULL) {
+ return (L_READ_DEV_DIR_ERROR);
+ }
+ (void) fprintf(stdout,
+ MSGSTR(5569,
+ " Logical Nodes under /dev/dsk and /dev/rdsk :\n"));
+
+ while ((dirent = readdir(dir)) != (struct dirent *)NULL) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0) {
+ continue;
+ }
+ (void) sprintf(lname, DEV_DSK_DIR"/%s", dirent->d_name);
+ if (readlink((const char *)lname, (char *)link,
+ (size_t)MAXPATHLEN) <= 0) {
+ (void) print_errString(L_SYMLINK_ERROR, lname);
+ continue;
+ }
+ for (dl = dlist; dl; dl = dl->next) {
+ (void) strcpy(path, dl->dev_path);
+ d1 = strrchr(path, ':');
+ if (d1)
+ *d1 = '\0';
+ if (strstr(link, path)) {
+ (void) fprintf(stdout,
+ "\t%s\n",
+ dirent->d_name);
+ }
+ }
+ }
+
+ closedir(dir);
+ return (0);
+}
+
+
+
+/*
+ * prints a list of devices which
+ * will be inserted or removed
+ * to the stdout and asks for
+ * the user's confirmation.
+ *
+ * RETURNS:
+ * 0 if OK
+ * non-zero otherwise
+ */
+static int
+h_print_list_warn(Hotplug_Devlist *disk_list_head, int todo, int enc_type)
+{
+int i;
+char choice[2];
+struct dlist *dl_ses, *dl_multi;
+Hotplug_Devlist *disk_list = disk_list_head;
+
+ (void) fprintf(stdout,
+ MSGSTR(5570, "The list of devices which will be "));
+ switch (todo) {
+ case INSERT_DEVICE:
+ (void) fprintf(stdout,
+ MSGSTR(5571, "inserted is:\n"));
+ break;
+ case REMOVE_DEVICE:
+ (void) fprintf(stdout,
+ MSGSTR(5572, "removed is:\n"));
+ break;
+ }
+
+ for (i = 1; disk_list; i++, disk_list = disk_list->next) {
+ if ((disk_list->dev_type == DTYPE_DIRECT) &&
+ (disk_list->dev_location == SENA)) {
+ if (disk_list->f_flag != NULL) {
+ if (enc_type == DAK_ENC_TYPE) {
+ (void) fprintf(stdout, MSGSTR(5665,
+ " %d: Box Name: \"%s\" slot %d\n"),
+ i, disk_list->box_name, disk_list->slot);
+ } else {
+ (void) fprintf(stdout, MSGSTR(137,
+ " %d: Box Name: \"%s\" front slot %d\n"),
+ i, disk_list->box_name, disk_list->slot);
+ }
+ } else {
+ if (enc_type == DAK_ENC_TYPE) {
+ (void) fprintf(stdout, MSGSTR(5665,
+ " %d: Box Name: \"%s\" slot %d\n"),
+ i, disk_list->box_name,
+ disk_list->slot + (MAX_DRIVES_DAK/2));
+ } else {
+ (void) fprintf(stdout, MSGSTR(136,
+ " %d: Box Name: \"%s\" rear slot %d\n"),
+ i, disk_list->box_name, disk_list->slot);
+ }
+ }
+ } else if (((disk_list->dev_type == DTYPE_DIRECT) ||
+ (disk_list->dev_type == DTYPE_SEQUENTIAL)) &&
+ (disk_list->dev_location == NON_SENA)) {
+ (void) fprintf(stdout, MSGSTR(5573,
+ " %d: Device name: %s\n"),
+ i, disk_list->dev_name);
+ } else if (disk_list->dev_type == DTYPE_ESI) {
+ (void) fprintf(stdout, MSGSTR(5574,
+ " %d: Box name: %s\n"),
+ i, disk_list->box_name);
+ }
+ if (getenv("_LUX_H_DEBUG") != NULL) {
+ if (disk_list->dev_location == SENA) {
+ (void) fprintf(stdout,
+ " Select ID:\t0x%x\n",
+ disk_list->tid);
+ if (disk_list->dev_type != DTYPE_ESI) {
+ if (enc_type == DAK_ENC_TYPE) {
+ (void) fprintf(stdout,
+ " Location: \tSlot %d \n",
+ disk_list->f_flag
+ ? disk_list->slot
+ : disk_list->slot
+ +MAX_DRIVES_DAK/2);
+ } else {
+ (void) fprintf(stdout,
+ " Location: \tSlot %d %s \n",
+ disk_list->slot, disk_list->f_flag
+ ? "front" : "rear");
+ }
+ }
+ }
+ }
+ if (todo == REMOVE_DEVICE) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(90, "Node WWN:"));
+ (void) fprintf(stdout, " %s\n",
+ disk_list->node_wwn_s);
+
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(35, "Device Type:"));
+ if (disk_list->dev_type == DTYPE_ESI) {
+ (void) fprintf(stdout, MSGSTR(5581,
+ " SENA (%s)\n"),
+ dtype[disk_list->dev_type]);
+ } else {
+ (void) fprintf(stdout, "%s\n",
+ dtype[disk_list->dev_type]);
+ }
+
+ if (disk_list->dev_type == DTYPE_ESI) {
+ dl_ses = disk_list->seslist;
+ (void) fprintf(stdout, MSGSTR(5575,
+ " SES Paths:\n"));
+ while (dl_ses) {
+ (void) fprintf(stdout, MSGSTR(5576,
+ " %s\n"), dl_ses->dev_path);
+ dl_ses = dl_ses->next;
+ }
+ } else {
+ dl_multi = disk_list->dlhead;
+ (void) fprintf(stdout, MSGSTR(5577,
+ " Device Paths:\n"));
+ while (dl_multi) {
+ (void) fprintf(stdout, MSGSTR(5578,
+ " %s\n"),
+ dl_multi->logical_path);
+ dl_multi = dl_multi->next;
+ }
+ }
+ }
+ (void) fprintf(stdout, "\n");
+ }
+ (void) fprintf(stdout, MSGSTR(5579,
+ "\nPlease verify the above list of devices"
+ " and\nthen enter 'c' or <CR> to Continue"
+ " or 'q' to Quit. [Default: c]: "));
+
+ /* Get the user input and continue accordingly. */
+ for (;;) {
+ (void) gets(choice);
+ if (choice[0] == 'c' || choice[0] == 'C' ||
+ choice[0] == 'q' || choice[0] == 'Q' ||
+ choice[0] == '\0') {
+ break;
+ }
+ (void) fprintf(stdout, MSGSTR(5580,
+ " Enter an appropriate option [c,<CR>,q]: "));
+ }
+
+ if (choice[0] == 'q' || choice[0] == 'Q') {
+ return (-1);
+ }
+ return (0);
+}
+
+
+static int
+h_find_new_device_link(char *device_dir, timestruc_t lastmtim)
+{
+struct stat dsk_stat;
+char lname[MAXPATHLEN], link[MAXPATHLEN];
+char *link_ptr;
+DIR *dir;
+struct dirent *dirent;
+int found_newlink = 0;
+
+
+ if ((dir = opendir(device_dir)) == NULL) {
+ if (errno == ENOENT) {
+ return (0);
+ } else {
+ return (L_READ_DEV_DIR_ERROR);
+ }
+ }
+
+ while ((dirent = readdir(dir)) != (struct dirent *)NULL) {
+ if (strcmp(dirent->d_name, ".") == 0 ||
+ strcmp(dirent->d_name, "..") == 0) {
+ continue;
+ }
+ (void) sprintf(lname, "%s/%s", device_dir, dirent->d_name);
+ if (lstat(lname, &dsk_stat) < 0) {
+ (void) print_errString(L_LSTAT_ES_DIR_ERROR,
+ lname);
+ continue;
+ }
+ if (readlink((const char *)lname, (char *)link,
+ (size_t)MAXPATHLEN) <= 0) {
+ (void) print_errString(L_SYMLINK_ERROR, lname);
+ continue;
+ }
+
+ /*
+ * "link" can be a relative pathname. But, since
+ * g_get_path_type() only accepts absolute paths, we
+ * will skip to the part where "/devices/" begins and pass a
+ * pointer from there. Since "link" is got from readlink(),
+ * it is unlikely that it will not have /devices string, but
+ * we will check for it anyways.
+ */
+ if (!(link_ptr = strstr(link, "/devices/")))
+ continue;
+ if (!g_get_path_type(link_ptr)) {
+ continue;
+ }
+ if (NEWER(dsk_stat.st_ctim, lastmtim)) {
+ found_newlink++;
+ if (found_newlink == 1) {
+ if (! (strcmp(device_dir, DEV_DSK_DIR))) {
+ (void) fprintf(stdout, MSGSTR(5561,
+ " New Logical Nodes under "
+ "/dev/dsk and /dev/rdsk :\n"));
+ } else { /* device_dir is /dev/rmt */
+ (void) fprintf(stdout, "New Logical "
+ "Node under /dev/rmt:\n");
+ }
+ }
+ (void) fprintf(stdout, "\t%s\n", dirent->d_name);
+ }
+ }
+ closedir(dir);
+ return (found_newlink);
+}
+
+
+/*
+ * prints the device state.
+ *
+ * RETURNS:
+ * None.
+ */
+void
+print_dev_state(char *devname, int state)
+{
+ (void) printf("\t%s: ", devname);
+ if (state & DEVICE_ONLINE) {
+ (void) printf(MSGSTR(3000, "Online"));
+ if (state & DEVICE_BUSY) {
+ (void) printf(" ");
+ (void) printf(MSGSTR(37, "Busy"));
+ }
+ if (state & DEVICE_DOWN) {
+ (void) printf(" ");
+ (void) printf(MSGSTR(118, "Down"));
+ }
+ } else {
+ if (state & DEVICE_OFFLINE) {
+ (void) printf(MSGSTR(3001, "Offline"));
+ if (state & DEVICE_DOWN) {
+ (void) printf(" ");
+ (void) printf(MSGSTR(118, "Down"));
+ }
+ }
+ }
+ (void) printf("\n");
+}
+
+
+/*
+ * prints the bus state.
+ *
+ * RETURNS:
+ * None.
+ */
+void
+print_bus_state(char *devname, int state)
+{
+ (void) printf("\t%s: ", devname);
+ if (state == BUS_QUIESCED) {
+ (void) printf(MSGSTR(3002, "Quiesced"));
+ } else if (state == BUS_ACTIVE) {
+ (void) printf(MSGSTR(39, "Active"));
+ } else if (state == BUS_SHUTDOWN) {
+ (void) printf(MSGSTR(3003, "Shutdown"));
+ }
+ (void) printf("\n");
+}
diff --git a/usr/src/cmd/luxadm/ibfirmware b/usr/src/cmd/luxadm/ibfirmware
new file mode 100644
index 0000000000..d3e17f4f42
--- /dev/null
+++ b/usr/src/cmd/luxadm/ibfirmware
Binary files differ
diff --git a/usr/src/cmd/luxadm/lux_util.c b/usr/src/cmd/luxadm/lux_util.c
new file mode 100644
index 0000000000..41e8ce7aa2
--- /dev/null
+++ b/usr/src/cmd/luxadm/lux_util.c
@@ -0,0 +1,1594 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/scsi/impl/uscsi.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/impl/commands.h>
+#include <sys/scsi/generic/sense.h>
+#include <sys/scsi/generic/mode.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/generic/inquiry.h>
+#include <sys/scsi/adapters/scsi_vhci.h>
+#include <sys/byteorder.h>
+#include "common.h"
+#include "errorcodes.h"
+
+#define MAX_MODE_SENSE_LEN 0xffff
+#define MAXLEN 1000
+
+#define RETRY_PATHLIST 1
+#define BYTES_PER_LINE 16
+#define SCMD_UNKNOWN 0xff
+
+#define SCSI_VHCI "/devices/scsi_vhci/"
+#define SLASH "/"
+#define DEV_PREFIX "/devices/"
+#define DEV_PREFIX_STRLEN strlen(DEV_PREFIX)
+#define DEVICES_DIR "/devices"
+
+extern char *dtype[]; /* from adm.c */
+extern int rand_r(unsigned int *);
+
+static int cleanup_dotdot_path(char *path);
+static int wait_random_time(void);
+static char *scsi_find_command_name(int cmd);
+static void scsi_printerr(struct uscsi_cmd *ucmd,
+ struct scsi_extended_sense *rq, int rqlen,
+ char msg_string[], char *err_string);
+static void string_dump(char *hdr, uchar_t *src, int nbytes, int format,
+ char msg_string[]);
+static int issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag);
+
+
+static int
+wait_random_time(void)
+{
+time_t timeval;
+struct tm *tmbuf = NULL;
+struct timeval tval;
+unsigned int seed;
+int random;
+pid_t pid;
+
+
+ /*
+ * Get the system time and use "system seconds"
+ * as 'seed' to generate a random number. Then,
+ * wait between 1/10 - 1/2 seconds before retry.
+ * Get the current process id and ex-or it with
+ * the seed so that the random number is always
+ * different even in case of multiple processes
+ * generate a random number at the same time.
+ */
+ if ((timeval = time(NULL)) == -1) {
+ return (errno);
+ }
+ if ((tmbuf = localtime(&timeval)) == NULL) {
+ return (-1); /* L_LOCALTIME_ERROR */
+ }
+
+ pid = getpid();
+
+ /* get a random number. */
+ seed = (unsigned int) tmbuf->tm_sec;
+ seed ^= pid;
+ random = rand_r(&seed);
+
+
+ random = ((random % 500) + 100) * MILLISEC;
+ tval.tv_sec = random / MICROSEC;
+ tval.tv_usec = random % MICROSEC;
+
+ if (select(0, NULL, NULL, NULL, &tval) == -1) {
+ return (-1); /* L_SELECT_ERROR */
+ }
+ return (0);
+}
+
+/*
+ * Special string dump for error message
+ */
+static void
+string_dump(char *hdr, uchar_t *src, int nbytes, int format, char msg_string[])
+{
+ int i;
+ int n;
+ char *p;
+ char s[256];
+
+ assert(format == HEX_ONLY || format == HEX_ASCII);
+
+ (void) strcpy(s, hdr);
+ for (p = s; *p; p++) {
+ *p = ' ';
+ }
+
+ p = hdr;
+ while (nbytes > 0) {
+ (void) sprintf(&msg_string[strlen(msg_string)],
+ "%s", p);
+ p = s;
+ n = MIN(nbytes, BYTES_PER_LINE);
+ for (i = 0; i < n; i++) {
+ (void) sprintf(&msg_string[strlen(msg_string)],
+ "%02x ",
+ src[i] & 0xff);
+ }
+ if (format == HEX_ASCII) {
+ for (i = BYTES_PER_LINE-n; i > 0; i--) {
+ (void) sprintf(&msg_string[strlen(msg_string)],
+ " ");
+ }
+ (void) sprintf(&msg_string[strlen(msg_string)],
+ " ");
+ for (i = 0; i < n; i++) {
+ (void) sprintf(&msg_string[strlen(msg_string)],
+ "%c",
+ isprint(src[i]) ? src[i] : '.');
+ }
+ }
+ (void) sprintf(&msg_string[strlen(msg_string)], "\n");
+ nbytes -= n;
+ src += n;
+ }
+}
+/*
+ * Return a pointer to a string telling us the name of the command.
+ */
+static char *
+scsi_find_command_name(int cmd)
+{
+/*
+ * Names of commands. Must have SCMD_UNKNOWN at end of list.
+ */
+struct scsi_command_name {
+ int command;
+ char *name;
+} scsi_command_names[29];
+
+register struct scsi_command_name *c;
+
+ scsi_command_names[0].command = SCMD_TEST_UNIT_READY;
+ scsi_command_names[0].name = MSGSTR(61, "Test Unit Ready");
+
+ scsi_command_names[1].command = SCMD_FORMAT;
+ scsi_command_names[1].name = MSGSTR(110, "Format");
+
+ scsi_command_names[2].command = SCMD_REASSIGN_BLOCK;
+ scsi_command_names[2].name = MSGSTR(77, "Reassign Block");
+
+ scsi_command_names[3].command = SCMD_READ;
+ scsi_command_names[3].name = MSGSTR(27, "Read");
+
+ scsi_command_names[4].command = SCMD_WRITE;
+ scsi_command_names[4].name = MSGSTR(54, "Write");
+
+ scsi_command_names[5].command = SCMD_READ_G1;
+ scsi_command_names[5].name = MSGSTR(79, "Read(10 Byte)");
+
+ scsi_command_names[6].command = SCMD_WRITE_G1;
+ scsi_command_names[6].name = MSGSTR(51, "Write(10 Byte)");
+
+ scsi_command_names[7].command = SCMD_MODE_SELECT;
+ scsi_command_names[7].name = MSGSTR(97, "Mode Select");
+
+ scsi_command_names[8].command = SCMD_MODE_SENSE;
+ scsi_command_names[8].name = MSGSTR(95, "Mode Sense");
+
+ scsi_command_names[9].command = SCMD_REASSIGN_BLOCK;
+ scsi_command_names[9].name = MSGSTR(77, "Reassign Block");
+
+ scsi_command_names[10].command = SCMD_REQUEST_SENSE;
+ scsi_command_names[10].name = MSGSTR(74, "Request Sense");
+
+ scsi_command_names[11].command = SCMD_READ_DEFECT_LIST;
+ scsi_command_names[11].name = MSGSTR(80, "Read Defect List");
+
+ scsi_command_names[12].command = SCMD_INQUIRY;
+ scsi_command_names[12].name = MSGSTR(102, "Inquiry");
+
+ scsi_command_names[13].command = SCMD_WRITE_BUFFER;
+ scsi_command_names[13].name = MSGSTR(53, "Write Buffer");
+
+ scsi_command_names[14].command = SCMD_READ_BUFFER;
+ scsi_command_names[14].name = MSGSTR(82, "Read Buffer");
+
+ scsi_command_names[15].command = SCMD_START_STOP;
+ scsi_command_names[15].name = MSGSTR(67, "Start/Stop");
+
+ scsi_command_names[16].command = SCMD_RESERVE;
+ scsi_command_names[16].name = MSGSTR(72, "Reserve");
+
+ scsi_command_names[17].command = SCMD_RELEASE;
+ scsi_command_names[17].name = MSGSTR(75, "Release");
+
+ scsi_command_names[18].command = SCMD_MODE_SENSE_G1;
+ scsi_command_names[18].name = MSGSTR(94, "Mode Sense(10 Byte)");
+
+ scsi_command_names[19].command = SCMD_MODE_SELECT_G1;
+ scsi_command_names[19].name = MSGSTR(96, "Mode Select(10 Byte)");
+
+ scsi_command_names[20].command = SCMD_READ_CAPACITY;
+ scsi_command_names[20].name = MSGSTR(81, "Read Capacity");
+
+ scsi_command_names[21].command = SCMD_SYNC_CACHE;
+ scsi_command_names[21].name = MSGSTR(64, "Synchronize Cache");
+
+ scsi_command_names[22].command = SCMD_READ_DEFECT_LIST;
+ scsi_command_names[22].name = MSGSTR(80, "Read Defect List");
+
+ scsi_command_names[23].command = SCMD_GDIAG;
+ scsi_command_names[23].name = MSGSTR(108, "Get Diagnostic");
+
+ scsi_command_names[24].command = SCMD_SDIAG;
+ scsi_command_names[24].name = MSGSTR(69, "Set Diagnostic");
+
+ scsi_command_names[25].command = SCMD_PERS_RESERV_IN;
+ scsi_command_names[25].name = MSGSTR(10500, "Persistent Reserve In");
+
+ scsi_command_names[26].command = SCMD_PERS_RESERV_OUT;
+ scsi_command_names[26].name = MSGSTR(10501, "Persistent Reserve out");
+
+ scsi_command_names[27].command = SCMD_LOG_SENSE;
+ scsi_command_names[27].name = MSGSTR(10502, "Log Sense");
+
+ scsi_command_names[28].command = SCMD_UNKNOWN;
+ scsi_command_names[28].name = MSGSTR(25, "Unknown");
+
+
+ for (c = scsi_command_names; c->command != SCMD_UNKNOWN; c++)
+ if (c->command == cmd)
+ break;
+ return (c->name);
+}
+
+
+/*
+ * Function to create error message containing
+ * scsi request sense information
+ */
+
+static void
+scsi_printerr(struct uscsi_cmd *ucmd, struct scsi_extended_sense *rq,
+ int rqlen, char msg_string[], char *err_string)
+{
+ int blkno;
+
+ switch (rq->es_key) {
+ case KEY_NO_SENSE:
+ (void) sprintf(msg_string, MSGSTR(91, "No sense error"));
+ break;
+ case KEY_RECOVERABLE_ERROR:
+ (void) sprintf(msg_string, MSGSTR(76, "Recoverable error"));
+ break;
+ case KEY_NOT_READY:
+ (void) sprintf(msg_string,
+ MSGSTR(10503,
+ "Device Not ready."
+ " Error: Random Retry Failed: %s\n."),
+ err_string);
+ break;
+ case KEY_MEDIUM_ERROR:
+ (void) sprintf(msg_string, MSGSTR(99, "Medium error"));
+ break;
+ case KEY_HARDWARE_ERROR:
+ (void) sprintf(msg_string, MSGSTR(106, "Hardware error"));
+ break;
+ case KEY_ILLEGAL_REQUEST:
+ (void) sprintf(msg_string, MSGSTR(103, "Illegal request"));
+ break;
+ case KEY_UNIT_ATTENTION:
+ (void) sprintf(msg_string,
+ MSGSTR(10504,
+ "Unit attention."
+ "Error: Random Retry Failed.\n"));
+ break;
+ case KEY_WRITE_PROTECT:
+ (void) sprintf(msg_string, MSGSTR(52, "Write protect error"));
+ break;
+ case KEY_BLANK_CHECK:
+ (void) sprintf(msg_string, MSGSTR(131, "Blank check error"));
+ break;
+ case KEY_VENDOR_UNIQUE:
+ (void) sprintf(msg_string, MSGSTR(58, "Vendor unique error"));
+ break;
+ case KEY_COPY_ABORTED:
+ (void) sprintf(msg_string, MSGSTR(123, "Copy aborted error"));
+ break;
+ case KEY_ABORTED_COMMAND:
+ (void) sprintf(msg_string,
+ MSGSTR(10505,
+ "Aborted command."
+ " Error: Random Retry Failed.\n"));
+ break;
+ case KEY_EQUAL:
+ (void) sprintf(msg_string, MSGSTR(117, "Equal error"));
+ break;
+ case KEY_VOLUME_OVERFLOW:
+ (void) sprintf(msg_string, MSGSTR(57, "Volume overflow"));
+ break;
+ case KEY_MISCOMPARE:
+ (void) sprintf(msg_string, MSGSTR(98, "Miscompare error"));
+ break;
+ case KEY_RESERVED:
+ (void) sprintf(msg_string, MSGSTR(10506,
+ "Reserved value found"));
+ break;
+ default:
+ (void) sprintf(msg_string, MSGSTR(59, "Unknown error"));
+ break;
+ }
+
+ (void) sprintf(&msg_string[strlen(msg_string)],
+ MSGSTR(10507, " during: %s"),
+ scsi_find_command_name(ucmd->uscsi_cdb[0]));
+
+ if (rq->es_valid) {
+ blkno = (rq->es_info_1 << 24) | (rq->es_info_2 << 16) |
+ (rq->es_info_3 << 8) | rq->es_info_4;
+ (void) sprintf(&msg_string[strlen(msg_string)],
+ MSGSTR(49, ": block %d (0x%x)"), blkno, blkno);
+ }
+
+ (void) sprintf(&msg_string[strlen(msg_string)], "\n");
+
+ if (rq->es_add_len >= 6) {
+ (void) sprintf(&msg_string[strlen(msg_string)],
+ MSGSTR(132, " Additional sense: 0x%x ASC Qualifier: 0x%x\n"),
+ rq->es_add_code, rq->es_qual_code);
+ /*
+ * rq->es_add_info[ADD_SENSE_CODE],
+ * rq->es_add_info[ADD_SENSE_QUAL_CODE]);
+ */
+ }
+ if (rq->es_key == KEY_ILLEGAL_REQUEST) {
+ string_dump(MSGSTR(47, " cmd: "), (uchar_t *)ucmd,
+ sizeof (struct uscsi_cmd), HEX_ONLY, msg_string);
+ string_dump(MSGSTR(48, " cdb: "),
+ (uchar_t *)ucmd->uscsi_cdb,
+ ucmd->uscsi_cdblen, HEX_ONLY, msg_string);
+ }
+ string_dump(MSGSTR(43, " sense: "),
+ (uchar_t *)rq, 8 + rq->es_add_len, HEX_ONLY,
+ msg_string);
+ rqlen = rqlen; /* not used */
+}
+
+
+/*
+ * Execute a command and determine the result.
+ */
+static int
+issue_uscsi_cmd(int file, struct uscsi_cmd *command, int flag)
+{
+struct scsi_extended_sense *rqbuf;
+int status, i, retry_cnt = 0, err;
+char errorMsg[MAXLEN];
+
+ /*
+ * Set function flags for driver.
+ *
+ * Set Automatic request sense enable
+ *
+ */
+ command->uscsi_flags = USCSI_RQENABLE;
+ command->uscsi_flags |= flag;
+
+ /* intialize error message array */
+ errorMsg[0] = '\0';
+
+ /* print command for debug */
+ if (getenv("_LUX_S_DEBUG") != NULL) {
+ if ((command->uscsi_cdb == NULL) ||
+ (flag & USCSI_RESET) ||
+ (flag & USCSI_RESET_ALL)) {
+ if (flag & USCSI_RESET) {
+ (void) printf(" Issuing a SCSI Reset.\n");
+ }
+ if (flag & USCSI_RESET_ALL) {
+ (void) printf(" Issuing a SCSI Reset All.\n");
+ }
+
+ } else {
+ (void) printf(" Issuing the following "
+ "SCSI command: %s\n",
+ scsi_find_command_name(command->uscsi_cdb[0]));
+ (void) printf(" fd=0x%x cdb=", file);
+ for (i = 0; i < (int)command->uscsi_cdblen; i++) {
+ (void) printf("%x ", *(command->uscsi_cdb + i));
+ }
+ (void) printf("\n\tlen=0x%x bufaddr=0x%x buflen=0x%x"
+ " flags=0x%x\n",
+ command->uscsi_cdblen,
+ command->uscsi_bufaddr,
+ command->uscsi_buflen, command->uscsi_flags);
+
+ if ((command->uscsi_buflen > 0) &&
+ ((flag & USCSI_READ) == 0)) {
+ (void) dump_hex_data(" Buffer data: ",
+ (uchar_t *)command->uscsi_bufaddr,
+ MIN(command->uscsi_buflen, 512), HEX_ASCII);
+ }
+ }
+ (void) fflush(stdout);
+ }
+
+
+ /*
+ * Default command timeout in case command left it 0
+ */
+ if (command->uscsi_timeout == 0) {
+ command->uscsi_timeout = 60;
+ }
+ /* Issue command - finally */
+
+retry:
+ status = ioctl(file, USCSICMD, command);
+ if (status == 0 && command->uscsi_status == 0) {
+ if (getenv("_LUX_S_DEBUG") != NULL) {
+ if ((command->uscsi_buflen > 0) &&
+ (flag & USCSI_READ)) {
+ (void) dump_hex_data("\tData read:",
+ (uchar_t *)command->uscsi_bufaddr,
+ MIN(command->uscsi_buflen, 512), HEX_ASCII);
+ }
+ }
+ return (status);
+ }
+ if ((status != 0) && (command->uscsi_status == 0)) {
+ if ((getenv("_LUX_S_DEBUG") != NULL) ||
+ (getenv("_LUX_ER_DEBUG") != NULL)) {
+ (void) printf("Unexpected USCSICMD ioctl error: %s\n",
+ strerror(errno));
+ }
+ return (status);
+ }
+
+ /*
+ * Just a SCSI error, create error message
+ * Retry once for Unit Attention,
+ * Not Ready, and Aborted Command
+ */
+ if ((command->uscsi_rqbuf != NULL) &&
+ (((char)command->uscsi_rqlen - (char)command->uscsi_rqresid) > 0)) {
+
+ rqbuf = (struct scsi_extended_sense *)command->uscsi_rqbuf;
+
+ switch (rqbuf->es_key) {
+ case KEY_NOT_READY:
+ if (retry_cnt++ < 1) {
+ ER_DPRINTF("Note: Device Not Ready."
+ " Retrying...\n");
+
+ if ((err = wait_random_time()) == 0) {
+ goto retry;
+ } else {
+ return (err);
+ }
+ }
+ break;
+
+ case KEY_UNIT_ATTENTION:
+ if (retry_cnt++ < 1) {
+ ER_DPRINTF(" cmd():"
+ " UNIT_ATTENTION: Retrying...\n");
+
+ goto retry;
+ }
+ break;
+
+ case KEY_ABORTED_COMMAND:
+ if (retry_cnt++ < 1) {
+ ER_DPRINTF("Note: Command is aborted."
+ " Retrying...\n");
+
+ goto retry;
+ }
+ break;
+ }
+ if ((getenv("_LUX_S_DEBUG") != NULL) ||
+ (getenv("_LUX_ER_DEBUG") != NULL)) {
+ scsi_printerr(command,
+ (struct scsi_extended_sense *)command->uscsi_rqbuf,
+ (command->uscsi_rqlen - command->uscsi_rqresid),
+ errorMsg, strerror(errno));
+ }
+
+ } else {
+
+ /*
+ * Retry 5 times in case of BUSY, and only
+ * once for Reservation-conflict, Command
+ * Termination and Queue Full. Wait for
+ * random amount of time (between 1/10 - 1/2 secs.)
+ * between each retry. This random wait is to avoid
+ * the multiple threads being executed at the same time
+ * and also the constraint in Photon IB, where the
+ * command queue has a depth of one command.
+ */
+ switch ((uchar_t)command->uscsi_status & STATUS_MASK) {
+ case STATUS_BUSY:
+ if (retry_cnt++ < 5) {
+ if ((err = wait_random_time()) == 0) {
+ R_DPRINTF(" cmd(): No. of retries %d."
+ " STATUS_BUSY: Retrying...\n",
+ retry_cnt);
+ goto retry;
+
+ } else {
+ return (err);
+ }
+ }
+ break;
+
+ case STATUS_RESERVATION_CONFLICT:
+ if (retry_cnt++ < 1) {
+ if ((err = wait_random_time()) == 0) {
+ R_DPRINTF(" cmd():"
+ " RESERVATION_CONFLICT:"
+ " Retrying...\n");
+ goto retry;
+
+ } else {
+ return (err);
+ }
+ }
+ break;
+
+ case STATUS_TERMINATED:
+ if (retry_cnt++ < 1) {
+ R_DPRINTF("Note: Command Terminated."
+ " Retrying...\n");
+
+ if ((err = wait_random_time()) == 0) {
+ goto retry;
+ } else {
+ return (err);
+ }
+ }
+ break;
+
+ case STATUS_QFULL:
+ if (retry_cnt++ < 1) {
+ R_DPRINTF("Note: Command Queue is full."
+ " Retrying...\n");
+
+ if ((err = wait_random_time()) == 0) {
+ goto retry;
+ } else {
+ return (err);
+ }
+ }
+ break;
+ }
+
+ }
+ if (((getenv("_LUX_S_DEBUG") != NULL) ||
+ (getenv("_LUX_ER_DEBUG") != NULL)) &&
+ (errorMsg[0] != '\0')) {
+ (void) fprintf(stdout, " %s\n", errorMsg);
+ }
+ return (L_SCSI_ERROR | command->uscsi_status);
+}
+
+/*
+ * MODE SENSE USCSI command
+ *
+ *
+ * pc = page control field
+ * page_code = Pages to return
+ */
+int
+scsi_mode_sense_cmd(int fd,
+ uchar_t *buf_ptr,
+ int buf_len,
+ uchar_t pc,
+ uchar_t page_code)
+{
+struct uscsi_cmd ucmd;
+/* 10 byte Mode Select cmd */
+union scsi_cdb cdb = {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+struct scsi_extended_sense sense;
+int status;
+static int uscsi_count;
+
+ if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
+ return (-1); /* L_INVALID_ARG */
+ }
+
+ (void) memset(buf_ptr, 0, buf_len);
+ (void) memset((char *)&ucmd, 0, sizeof (ucmd));
+ /* Just for me - a sanity check */
+ if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
+ (buf_len > MAX_MODE_SENSE_LEN)) {
+ return (-1); /* L_ILLEGAL_MODE_SENSE_PAGE */
+ }
+ cdb.g1_addr3 = (pc << 6) + page_code;
+ cdb.g1_count1 = buf_len>>8;
+ cdb.g1_count0 = buf_len & 0xff;
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP1;
+ ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
+ ucmd.uscsi_buflen = buf_len;
+ ucmd.uscsi_rqbuf = (caddr_t)&sense;
+ ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
+ ucmd.uscsi_timeout = 120;
+
+ status = issue_uscsi_cmd(fd, &ucmd, USCSI_READ);
+ /* Bytes actually transfered */
+ if (status == 0) {
+ uscsi_count = buf_len - ucmd.uscsi_resid;
+ S_DPRINTF(" Number of bytes read on "
+ "Mode Sense 0x%x\n", uscsi_count);
+ if (getenv("_LUX_D_DEBUG") != NULL) {
+ (void) dump_hex_data(" Mode Sense data: ", buf_ptr,
+ uscsi_count, HEX_ASCII);
+ }
+ }
+ return (status);
+}
+
+int
+scsi_release(char *path)
+{
+struct uscsi_cmd ucmd;
+union scsi_cdb cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0};
+struct scsi_extended_sense sense;
+int fd, status;
+
+ P_DPRINTF(" scsi_release: Release: Path %s\n", path);
+ if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
+ return (1);
+
+ (void) memset((char *)&ucmd, 0, sizeof (ucmd));
+
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP0;
+ ucmd.uscsi_bufaddr = NULL;
+ ucmd.uscsi_buflen = 0;
+ ucmd.uscsi_rqbuf = (caddr_t)&sense;
+ ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
+ ucmd.uscsi_timeout = 60;
+ status = (issue_uscsi_cmd(fd, &ucmd, 0));
+
+ (void) close(fd);
+ return (status);
+}
+
+int
+scsi_reserve(char *path)
+{
+struct uscsi_cmd ucmd;
+union scsi_cdb cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0};
+struct scsi_extended_sense sense;
+int fd, status;
+
+ P_DPRINTF(" scsi_reserve: Reserve: Path %s\n", path);
+ if ((fd = open(path, O_NDELAY | O_RDONLY)) == -1)
+ return (1);
+
+ (void) memset((char *)&ucmd, 0, sizeof (ucmd));
+
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP0;
+ ucmd.uscsi_bufaddr = NULL;
+ ucmd.uscsi_buflen = 0;
+ ucmd.uscsi_rqbuf = (caddr_t)&sense;
+ ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
+ ucmd.uscsi_timeout = 60;
+ status = (issue_uscsi_cmd(fd, &ucmd, 0));
+
+ (void) close(fd);
+ return (status);
+}
+
+/*
+ * Print out fabric dev dtype
+ */
+void
+print_fabric_dtype_prop(uchar_t *hba_port_wwn, uchar_t *port_wwn,
+ uchar_t dtype_prop)
+{
+ if ((dtype_prop & DTYPE_MASK) < 0x10) {
+ (void) fprintf(stdout, " 0x%-2x (%s)\n",
+ (dtype_prop & DTYPE_MASK), dtype[(dtype_prop & DTYPE_MASK)]);
+ } else if ((dtype_prop & DTYPE_MASK) < 0x1f) {
+ (void) fprintf(stdout,
+ MSGSTR(2096, " 0x%-2x (Reserved)\n"),
+ (dtype_prop & DTYPE_MASK));
+ } else {
+ /* Check to see if this is the HBA */
+ if (wwnConversion(hba_port_wwn) != wwnConversion(port_wwn)) {
+ (void) fprintf(stdout, MSGSTR(2097,
+ " 0x%-2x (Unknown Type)\n"), (dtype_prop & DTYPE_MASK));
+ } else {
+ /* MATCH */
+ (void) fprintf(stdout, MSGSTR(2241,
+ " 0x%-2x (Unknown Type,Host Bus Adapter)\n"),
+ (dtype_prop & DTYPE_MASK));
+ }
+ }
+}
+
+
+void
+print_inq_data(char *arg_path, char *path, L_inquiry inq, uchar_t *serial,
+ size_t serial_len)
+{
+char **p;
+uchar_t *v_parm;
+int scsi_3, length;
+char byte_number[MAXNAMELEN];
+static char *scsi_inquiry_labels_2[21];
+static char *scsi_inquiry_labels_3[22];
+static char *ansi_version[4];
+ /*
+ * Intialize scsi_inquiry_labels_2 with i18n strings
+ */
+ scsi_inquiry_labels_2[0] = MSGSTR(138, "Vendor: ");
+ scsi_inquiry_labels_2[1] = MSGSTR(149, "Product: ");
+ scsi_inquiry_labels_2[2] = MSGSTR(139, "Revision: ");
+ scsi_inquiry_labels_2[3] = MSGSTR(143, "Firmware Revision ");
+ scsi_inquiry_labels_2[4] = MSGSTR(144, "Serial Number ");
+ scsi_inquiry_labels_2[5] = MSGSTR(140, "Device type: ");
+ scsi_inquiry_labels_2[6] = MSGSTR(145, "Removable media: ");
+ scsi_inquiry_labels_2[7] = MSGSTR(146, "ISO version: ");
+ scsi_inquiry_labels_2[8] = MSGSTR(147, "ECMA version: ");
+ scsi_inquiry_labels_2[9] = MSGSTR(148, "ANSI version: ");
+ scsi_inquiry_labels_2[10] =
+ MSGSTR(2168, "Async event notification: ");
+ scsi_inquiry_labels_2[11] =
+ MSGSTR(2169, "Terminate i/o process msg: ");
+ scsi_inquiry_labels_2[12] = MSGSTR(150, "Response data format: ");
+ scsi_inquiry_labels_2[13] = MSGSTR(151, "Additional length: ");
+ scsi_inquiry_labels_2[14] = MSGSTR(152, "Relative addressing: ");
+ scsi_inquiry_labels_2[15] =
+ MSGSTR(2170, "32 bit transfers: ");
+ scsi_inquiry_labels_2[16] =
+ MSGSTR(2171, "16 bit transfers: ");
+ scsi_inquiry_labels_2[17] =
+ MSGSTR(2172, "Synchronous transfers: ");
+ scsi_inquiry_labels_2[18] = MSGSTR(153, "Linked commands: ");
+ scsi_inquiry_labels_2[19] = MSGSTR(154, "Command queueing: ");
+ scsi_inquiry_labels_2[20] =
+ MSGSTR(2173, "Soft reset option: ");
+
+ /*
+ * Intialize scsi_inquiry_labels_3 with i18n strings
+ */
+ scsi_inquiry_labels_3[0] = MSGSTR(138, "Vendor: ");
+ scsi_inquiry_labels_3[1] = MSGSTR(149, "Product: ");
+ scsi_inquiry_labels_3[2] = MSGSTR(139, "Revision: ");
+ scsi_inquiry_labels_3[3] = MSGSTR(143, "Firmware Revision ");
+ scsi_inquiry_labels_3[4] = MSGSTR(144, "Serial Number ");
+ scsi_inquiry_labels_3[5] = MSGSTR(140, "Device type: ");
+ scsi_inquiry_labels_3[6] = MSGSTR(145, "Removable media: ");
+ scsi_inquiry_labels_3[7] = MSGSTR(2174, "Medium Changer Element: ");
+ scsi_inquiry_labels_3[8] = MSGSTR(146, "ISO version: ");
+ scsi_inquiry_labels_3[9] = MSGSTR(147, "ECMA version: ");
+ scsi_inquiry_labels_3[10] = MSGSTR(148, "ANSI version: ");
+ scsi_inquiry_labels_3[11] =
+ MSGSTR(2175, "Async event reporting: ");
+ scsi_inquiry_labels_3[12] =
+ MSGSTR(2176, "Terminate task: ");
+ scsi_inquiry_labels_3[13] =
+ MSGSTR(2177, "Normal ACA Supported: ");
+ scsi_inquiry_labels_3[14] = MSGSTR(150, "Response data format: ");
+ scsi_inquiry_labels_3[15] = MSGSTR(151, "Additional length: ");
+ scsi_inquiry_labels_3[16] =
+ MSGSTR(2178, "Cmd received on port: ");
+ scsi_inquiry_labels_3[17] =
+ MSGSTR(2179, "SIP Bits: ");
+ scsi_inquiry_labels_3[18] = MSGSTR(152, "Relative addressing: ");
+ scsi_inquiry_labels_3[19] = MSGSTR(153, "Linked commands: ");
+ scsi_inquiry_labels_3[20] =
+ MSGSTR(2180, "Transfer Disable: ");
+ scsi_inquiry_labels_3[21] = MSGSTR(154, "Command queueing: ");
+
+ /*
+ * Intialize scsi_inquiry_labels_3 with i18n strings
+ */
+ ansi_version[0] = MSGSTR(2181,
+ " (Device might or might not comply to an ANSI version)");
+ ansi_version[1] = MSGSTR(2182,
+ " (This code is reserved for historical uses)");
+ ansi_version[2] = MSGSTR(2183,
+ " (Device complies to ANSI X3.131-1994 (SCSI-2))");
+ ansi_version[3] = MSGSTR(2184,
+ " (Device complies to SCSI-3)");
+
+ /* print inquiry information */
+
+ (void) fprintf(stdout, MSGSTR(2185, "\nINQUIRY:\n"));
+ /*
+ * arg_path is the path sent to luxadm by the user. if arg_path
+ * is a /devices path, then we do not need to print out physical
+ * path info
+ */
+ if (strcmp(arg_path, path) != 0 &&
+ strstr(arg_path, "/devices/") == NULL) {
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout,
+ MSGSTR(5, "Physical Path:"));
+ (void) fprintf(stdout, "\n %s\n", path);
+ }
+ if (inq.inq_ansi < 3) {
+ p = scsi_inquiry_labels_2;
+ scsi_3 = 0;
+ } else {
+ p = scsi_inquiry_labels_3;
+ scsi_3 = 1;
+ }
+ if (inq.inq_len < 11) {
+ p += 1;
+ } else {
+ /* */
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(inq.inq_vid, sizeof (inq.inq_vid), 0);
+ (void) fprintf(stdout, "\n");
+ }
+ if (inq.inq_len < 27) {
+ p += 1;
+ } else {
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(inq.inq_pid, sizeof (inq.inq_pid), 0);
+ (void) fprintf(stdout, "\n");
+ }
+ if (inq.inq_len < 31) {
+ p += 1;
+ } else {
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(inq.inq_revision, sizeof (inq.inq_revision), 0);
+ (void) fprintf(stdout, "\n");
+ }
+ if (inq.inq_len < 39) {
+ p += 2;
+ } else {
+ /*
+ * If Pluto then print
+ * firmware rev & serial #.
+ */
+ if (strstr((char *)inq.inq_pid, "SSA") != 0) {
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(inq.inq_firmware_rev,
+ sizeof (inq.inq_firmware_rev), 0);
+ (void) fprintf(stdout, "\n");
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(serial, serial_len, 0);
+ (void) fprintf(stdout, "\n");
+ } else if ((inq.inq_dtype & DTYPE_MASK) != DTYPE_ESI) {
+ p++;
+ (void) fprintf(stdout, "%s", *p++);
+ print_chars(serial, serial_len, 0);
+ (void) fprintf(stdout, "\n");
+ } else {
+ /* if we miss both the above if's */
+ p += 2;
+ }
+ }
+
+ (void) fprintf(stdout, "%s0x%x (",
+ *p++, (inq.inq_dtype & DTYPE_MASK));
+ if ((inq.inq_dtype & DTYPE_MASK) < 0x10) {
+ (void) fprintf(stdout, "%s", dtype[inq.inq_dtype & DTYPE_MASK]);
+ } else if ((inq.inq_dtype & DTYPE_MASK) < 0x1f) {
+ (void) fprintf(stdout, MSGSTR(71, "Reserved"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(2186, "Unknown device"));
+ }
+ (void) fprintf(stdout, ")\n");
+
+ (void) fprintf(stdout, "%s", *p++);
+ if (inq.inq_rmb != NULL) {
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(45, "no"));
+ }
+ (void) fprintf(stdout, "\n");
+
+ if (scsi_3) {
+ (void) fprintf(stdout, "%s", *p++);
+ if (inq.inq_mchngr != NULL) {
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(45, "no"));
+ }
+ (void) fprintf(stdout, "\n");
+ }
+ (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_iso);
+ (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_ecma);
+
+ (void) fprintf(stdout, "%s%d", *p++, inq.inq_ansi);
+ if (inq.inq_ansi < 0x4) {
+ (void) fprintf(stdout, "%s", ansi_version[inq.inq_ansi]);
+ } else
+ (void) fprintf(stdout, MSGSTR(71, "Reserved"));
+ (void) fprintf(stdout, "\n");
+
+ if (inq.inq_aenc) {
+ (void) fprintf(stdout, "%s", *p++);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ } else {
+ p++;
+ }
+ if (scsi_3) {
+ (void) fprintf(stdout, "%s", *p++);
+ if (inq.inq_normaca != NULL) {
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(45, "no"));
+ }
+ (void) fprintf(stdout, "\n");
+ }
+ if (inq.inq_trmiop) {
+ (void) fprintf(stdout, "%s", *p++);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ } else {
+ p++;
+ }
+ (void) fprintf(stdout, "%s%d\n", *p++, inq.inq_rdf);
+ (void) fprintf(stdout, "%s0x%x\n", *p++, inq.inq_len);
+ if (scsi_3) {
+ if (inq.inq_dual_p) {
+ if (inq.inq_port != NULL) {
+ (void) fprintf(stdout, MSGSTR(2187,
+ "%sa\n"), *p++);
+ } else {
+ (void) fprintf(stdout, MSGSTR(2188,
+ "%sb\n"), *p++);
+ }
+ } else {
+ p++;
+ }
+ }
+ if (scsi_3) {
+ if (inq.inq_SIP_1 || inq.ui.inq_3.inq_SIP_2 ||
+ inq.ui.inq_3.inq_SIP_3) {
+ (void) fprintf(stdout, "%s%d, %d, %d\n", *p,
+ inq.inq_SIP_1, inq.ui.inq_3.inq_SIP_2,
+ inq.ui.inq_3.inq_SIP_3);
+ }
+ p++;
+
+ }
+
+ if (inq.ui.inq_2.inq_2_reladdr) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (!scsi_3) {
+
+ if (inq.ui.inq_2.inq_wbus32) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (inq.ui.inq_2.inq_wbus16) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (inq.ui.inq_2.inq_sync) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ }
+ if (inq.ui.inq_2.inq_linked) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (scsi_3) {
+ (void) fprintf(stdout, "%s", *p++);
+ if (inq.ui.inq_3.inq_trandis != NULL) {
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(45, "no"));
+ }
+ (void) fprintf(stdout, "\n");
+ }
+
+ if (inq.ui.inq_2.inq_cmdque) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ if (!scsi_3) {
+ if (inq.ui.inq_2.inq_sftre) {
+ (void) fprintf(stdout, "%s", *p);
+ (void) fprintf(stdout, MSGSTR(40, "yes"));
+ (void) fprintf(stdout, "\n");
+ }
+ p++;
+
+ }
+
+ /*
+ * Now print the vendor-specific data.
+ */
+ v_parm = inq.inq_ven_specific_1;
+ if (inq.inq_len >= 32) {
+ length = inq.inq_len - 31;
+ if (strstr((char *)inq.inq_pid, "SSA") != 0) {
+ (void) fprintf(stdout, MSGSTR(2189,
+ "Number of Ports, Targets: %d,%d\n"),
+ inq.inq_ssa_ports, inq.inq_ssa_tgts);
+ v_parm += 20;
+ length -= 20;
+ } else if ((strstr((char *)inq.inq_pid, "SUN") != 0) ||
+ (strncmp((char *)inq.inq_vid, "SUN ",
+ sizeof (inq.inq_vid)) == 0)) {
+ v_parm += 16;
+ length -= 16;
+ }
+ /*
+ * Do hex Dump of rest of the data.
+ */
+ if (length > 0) {
+ (void) fprintf(stdout,
+ MSGSTR(2190,
+ " VENDOR-SPECIFIC PARAMETERS\n"));
+ (void) fprintf(stdout,
+ MSGSTR(2191,
+ "Byte# Hex Value "
+ " ASCII\n"));
+ (void) sprintf(byte_number,
+ "%d ", inq.inq_len - length + 5);
+ dump_hex_data(byte_number, v_parm,
+ MIN(length, inq.inq_res3 - v_parm), HEX_ASCII);
+ }
+ /*
+ * Skip reserved bytes 56-95.
+ */
+ length -= (inq.inq_box_name - v_parm);
+ if (length > 0) {
+ (void) sprintf(byte_number, "%d ",
+ inq.inq_len - length + 5);
+ dump_hex_data(byte_number, inq.inq_box_name,
+ MIN(length, sizeof (inq.inq_box_name) +
+ sizeof (inq.inq_avu)), HEX_ASCII);
+ }
+ }
+ if (getenv("_LUX_D_DEBUG") != NULL) {
+ dump_hex_data("\nComplete Inquiry: ",
+ (uchar_t *)&inq,
+ MIN(inq.inq_len + 5, sizeof (inq)), HEX_ASCII);
+ }
+}
+
+/*
+ * Internal routine to clean up ../'s in paths.
+ * returns 0 if no "../" are left.
+ *
+ * Wouldn't it be nice if there was a standard system library
+ * routine to do this...?
+ */
+static int
+cleanup_dotdot_path(char *path)
+{
+ char holder[MAXPATHLEN];
+ char *dotdot;
+ char *previous_slash;
+
+ /* Find the first "/../" in the string */
+ dotdot = strstr(path, "/../");
+ if (dotdot == NULL) {
+ return (0);
+ }
+
+
+ /*
+ * If the [0] character is '/' and "../" immediatly
+ * follows it, then we can strip the ../
+ *
+ * /../../foo/bar == /foo/bar
+ *
+ */
+ if (dotdot == path) {
+ strcpy(holder, &path[3]); /* strip "/.." */
+ strcpy(path, holder);
+ return (1);
+ }
+
+ /*
+ * Now look for the LAST "/" before the "/../"
+ * as this is the parent dir we can get rid of.
+ * We do this by temporarily truncating the string
+ * at the '/' just before "../" using the dotdot pointer.
+ */
+ *dotdot = '\0';
+ previous_slash = strrchr(path, '/');
+ if (previous_slash == NULL) {
+ /*
+ * hmm, somethings wrong. path looks something
+ * like "foo/../bar/" so we can't really deal with it.
+ */
+ return (0);
+ }
+ /*
+ * Now truncate the path just after the previous '/'
+ * and slam everything after the "../" back on
+ */
+ *(previous_slash+1) = '\0';
+ (void) strcat(path, dotdot+4);
+ return (1); /* We may have more "../"s */
+}
+
+/*
+ * Follow symbolic links from the logical device name to
+ * the /devfs physical device name. To be complete, we
+ * handle the case of multiple links. This function
+ * either returns NULL (no links, or some other error),
+ * or the physical device name, alloc'ed on the heap.
+ *
+ * NOTE: If the path is relative, it will be forced into
+ * an absolute path by pre-pending the pwd to it.
+ */
+char *
+get_slash_devices_from_osDevName(char *osDevName, int flag)
+{
+ struct stat stbuf;
+ char source[MAXPATHLEN];
+ char scratch[MAXPATHLEN];
+ char pwd[MAXPATHLEN];
+ char *tmp, *phys_path;
+ int cnt;
+ boolean_t is_lstat_failed = B_TRUE;
+
+ /* return NULL if path is NULL */
+ if (osDevName == NULL) {
+ return (NULL);
+ }
+
+ strcpy(source, osDevName);
+ for (;;) {
+
+ /*
+ * First make sure the path is absolute. If not, make it.
+ * If it's already an absolute path, we have no need
+ * to determine the cwd, so the program should still
+ * function within security-by-obscurity directories.
+ */
+ if (source[0] != '/') {
+ tmp = getcwd(pwd, MAXPATHLEN);
+ if (tmp == NULL) {
+ return (NULL);
+ }
+ /*
+ * Handle special case of "./foo/bar"
+ */
+ if (source[0] == '.' && source[1] == '/') {
+ strcpy(scratch, source+2);
+ } else { /* no "./" so just take everything */
+ strcpy(scratch, source);
+ }
+ strcpy(source, pwd);
+ (void) strcat(source, "/");
+ (void) strcat(source, scratch);
+ }
+
+ /*
+ * Clean up any "../"s that are in the path
+ */
+ while (cleanup_dotdot_path(source));
+
+ /*
+ * source is now an absolute path to the link we're
+ * concerned with
+ */
+ if (flag == NOT_IGNORE_DANGLING_LINK) {
+ /*
+ * In order not to ingore dangling links, check
+ * the lstat. If lstat succeeds, return the path
+ * from readlink.
+ * Note: osDevName input with /devices path from
+ * a dangling /dev link doesn't pass lstat so
+ * NULL is returned.
+ */
+ if (stat(source, &stbuf) == -1) {
+ if (!is_lstat_failed &&
+ strstr(source, "/devices")) {
+ /*
+ * lstat succeeded previously and source
+ * contains "/devices" then it is dangling node.
+ */
+ phys_path = (char *)calloc(1, strlen(source) + 1);
+ if (phys_path != NULL) {
+ (void) strncpy(phys_path, source,
+ strlen(source) + 1);
+ }
+ return (phys_path);
+ } else if (is_lstat_failed) {
+ /* check lstat result. */
+ if (lstat(source, &stbuf) == -1) {
+ return (NULL);
+ } else {
+ is_lstat_failed = B_FALSE; /* and coninue */
+ }
+ } else {
+ /*
+ * With algorithm that resolves a link and
+ * then issues readlink(), should not be
+ * reached here.
+ */
+ return (NULL);
+ }
+ } else {
+ if (lstat(source, &stbuf) == -1) {
+ /*
+ * when stat succeeds it is not a dangling node
+ * so it is not a special case.
+ */
+ return (NULL);
+ }
+ }
+ } else if (flag == STANDARD_DEVNAME_HANDLING) {
+ /*
+ * See if there's a real file out there. If not,
+ * we have a dangling link and we ignore it.
+ */
+ if (stat(source, &stbuf) == -1) {
+ return (NULL);
+ }
+ if (lstat(source, &stbuf) == -1) {
+ return (NULL);
+ }
+ } else {
+ /* invalid flag */
+ return (NULL);
+ }
+
+ /*
+ * If the file is not a link, we're done one
+ * way or the other. If there were links,
+ * return the full pathname of the resulting
+ * file.
+ *
+ * Note: All of our temp's are on the stack,
+ * so we have to copy the final result to the heap.
+ */
+ if (!S_ISLNK(stbuf.st_mode)) {
+ phys_path = (char *)calloc(1, strlen(source) + 1);
+ if (phys_path != NULL) {
+ (void) strncpy(phys_path, source,
+ strlen(source) + 1);
+ }
+ return (phys_path);
+ }
+ cnt = readlink(source, scratch, sizeof (scratch));
+ if (cnt < 0) {
+ return (NULL);
+ }
+ /*
+ * scratch is on the heap, and for some reason readlink
+ * doesn't always terminate things properly so we have
+ * to make certain we're properly terminated
+ */
+ scratch[cnt] = '\0';
+
+ /*
+ * Now check to see if the link is relative. If so,
+ * then we have to append it to the directory
+ * which the source was in. (This is non trivial)
+ */
+ if (scratch[0] != '/') {
+ tmp = strrchr(source, '/');
+ if (tmp == NULL) { /* Whoa! Something's hosed! */
+ O_DPRINTF("Internal error... corrupt path.\n");
+ return (NULL);
+ }
+ /* Now strip off just the directory path */
+ *(tmp+1) = '\0'; /* Keeping the last '/' */
+ /* and append the new link */
+ (void) strcat(source, scratch);
+ /*
+ * Note: At this point, source should have "../"s
+ * but we'll clean it up in the next pass through
+ * the loop.
+ */
+ } else {
+ /* It's an absolute link so no worries */
+ strcpy(source, scratch);
+ }
+ }
+ /* Never reach here */
+}
+
+/*
+ * Input - Space for client_path, phci_path and paddr fields of ioc structure
+ * need to be allocated by the caller of this routine.
+ */
+int
+get_scsi_vhci_pathinfo(char *dev_path, sv_iocdata_t *ioc, int *path_count)
+{
+ char *physical_path, *physical_path_s;
+ int retval;
+ int fd;
+ int initial_path_count;
+ int current_path_count;
+ int i;
+ char *delimiter;
+ int malloc_error = 0;
+ int prop_buf_size;
+ int pathlist_retry_count = 0;
+
+ if (strncmp(dev_path, SCSI_VHCI,
+ strlen(SCSI_VHCI)) != NULL) {
+ if ((physical_path = get_slash_devices_from_osDevName(
+ dev_path, STANDARD_DEVNAME_HANDLING)) == NULL) {
+ return (L_INVALID_PATH);
+ }
+ if (strncmp(physical_path, SCSI_VHCI,
+ strlen(SCSI_VHCI)) != NULL) {
+ free(physical_path);
+ return (L_INVALID_PATH);
+ }
+ } else {
+ if ((physical_path = calloc(1, MAXPATHLEN)) == NULL) {
+ return (L_MALLOC_FAILED);
+ }
+ (void) strcpy(physical_path, dev_path);
+ }
+ physical_path_s = physical_path;
+
+ /* move beyond "/devices" prefix */
+ physical_path += DEV_PREFIX_STRLEN-1;
+ /* remove :c,raw suffix */
+ delimiter = strrchr(physical_path, ':');
+ /* if we didn't find the ':' fine, else truncate */
+ if (delimiter != NULL) {
+ *delimiter = NULL;
+ }
+
+ /*
+ * We'll call ioctl SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO
+ * at least twice. The first time will get the path count
+ * and the size of the ioctl propoerty buffer. The second
+ * time will get the path_info for each path.
+ *
+ * It's possible that additional paths are added while this
+ * code is running. If the path count increases between the
+ * 2 ioctl's above, then we'll retry (and assume all is well).
+ */
+ (void) strcpy(ioc->client, physical_path);
+ ioc->buf_elem = 1;
+ ioc->ret_elem = (uint_t *)&(initial_path_count);
+ ioc->ret_buf = NULL;
+
+ /* free physical path */
+ free(physical_path_s);
+
+ /* 0 buf_size asks driver to return actual size needed */
+ /* open the ioctl file descriptor */
+ if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) {
+ return (L_OPEN_PATH_FAIL);
+ }
+
+ retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
+ if (retval != 0) {
+ close(fd);
+ return (L_SCSI_VHCI_ERROR);
+ }
+ prop_buf_size = SV_PROP_MAX_BUF_SIZE;
+
+
+ while (pathlist_retry_count <= RETRY_PATHLIST) {
+ ioc->buf_elem = initial_path_count;
+ /* Make driver put actual # paths in variable */
+ ioc->ret_elem = (uint_t *)&(current_path_count);
+
+ /*
+ * Allocate space for array of path_info structures.
+ * Allocate enough space for # paths from get_pathcount
+ */
+ ioc->ret_buf = (sv_path_info_t *)
+ calloc(initial_path_count,
+ sizeof (sv_path_info_t));
+ if (ioc->ret_buf == NULL) {
+ close(fd);
+ return (L_MALLOC_FAILED);
+ }
+
+ /*
+ * Allocate space for path properties returned by driver
+ */
+ malloc_error = 0;
+ for (i = 0; i < initial_path_count; i++) {
+ ioc->ret_buf[i].ret_prop.buf_size = prop_buf_size;
+ if ((ioc->ret_buf[i].ret_prop.buf =
+ (caddr_t)malloc(prop_buf_size)) == NULL) {
+ malloc_error = 1;
+ break;
+ }
+ if ((ioc->ret_buf[i].ret_prop.ret_buf_size =
+ (uint_t *)malloc(sizeof (uint_t))) == NULL) {
+ malloc_error = 1;
+ break;
+ }
+ }
+ if (malloc_error == 1) {
+ for (i = 0; i < initial_path_count; i++) {
+ free(ioc->ret_buf[i].ret_prop.buf);
+ free(ioc->ret_buf[i].ret_prop.ret_buf_size);
+ }
+ free(ioc->ret_buf);
+ close(fd);
+ return (L_MALLOC_FAILED);
+ }
+
+ retval = ioctl(fd, SCSI_VHCI_GET_CLIENT_MULTIPATH_INFO, ioc);
+ if (retval != 0) {
+ for (i = 0; i < initial_path_count; i++) {
+ free(ioc->ret_buf[i].ret_prop.buf);
+ free(ioc->ret_buf[i].ret_prop.ret_buf_size);
+ }
+ free(ioc->ret_buf);
+ close(fd);
+ return (L_SCSI_VHCI_ERROR);
+ }
+ if (initial_path_count < current_path_count) {
+ /* then a new path was added */
+ pathlist_retry_count++;
+ initial_path_count = current_path_count;
+ } else {
+ break;
+ }
+ }
+ /* we are done with ioctl's, lose the fd */
+ close(fd);
+
+ /*
+ * Compare the length num elements from the ioctl response
+ * and the caller's request - use smaller value.
+ *
+ * pathlist_p->path_count now has count returned from ioctl.
+ * ioc.buf_elem has the value the caller provided.
+ */
+ if (initial_path_count < current_path_count) {
+ /* More paths exist than we allocated space for */
+ *path_count = initial_path_count;
+ } else {
+ *path_count = current_path_count;
+ }
+
+ return (0);
+}
+
+int
+get_mode_page(char *path, uchar_t **pg_buf)
+{
+struct mode_header_g1 *mode_header_ptr;
+int status, size, fd;
+
+ /* open controller */
+ if ((fd = open(path, O_NDELAY | O_RDWR)) == -1)
+ return (-1); /* L_OPEN_PATH_FAIL */
+
+ /*
+ * Read the first part of the page to get the page size
+ */
+ size = 20;
+ if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
+ (void) close(fd);
+ return (L_MALLOC_FAILED);
+ }
+ /* read page */
+ if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
+ 0, MODEPAGE_ALLPAGES)) {
+ (void) close(fd);
+ (void) free(*pg_buf);
+ return (status);
+ }
+ /* Now get the size for all pages */
+ mode_header_ptr = (struct mode_header_g1 *)(void *)*pg_buf;
+ size = ntohs(mode_header_ptr->length) +
+ sizeof (mode_header_ptr->length);
+ (void) free(*pg_buf);
+ if ((*pg_buf = (uchar_t *)calloc(1, size)) == NULL) {
+ (void) close(fd);
+ return (L_MALLOC_FAILED);
+ }
+ /* read all pages */
+ if (status = scsi_mode_sense_cmd(fd, *pg_buf, size,
+ 0, MODEPAGE_ALLPAGES)) {
+ (void) close(fd);
+ (void) free(*pg_buf);
+ return (status);
+ }
+ (void) close(fd);
+ return (0);
+}
+
+/*
+ * Dump a structure in hexadecimal.
+ */
+void
+dump_hex_data(char *hdr, uchar_t *src, int nbytes, int format)
+{
+ int i;
+ int n;
+ char *p;
+ char s[256];
+
+ assert(format == HEX_ONLY || format == HEX_ASCII);
+
+ (void) strcpy(s, hdr);
+ for (p = s; *p; p++) {
+ *p = ' ';
+ }
+
+ p = hdr;
+ while (nbytes > 0) {
+ (void) fprintf(stdout, "%s", p);
+ p = s;
+ n = MIN(nbytes, BYTES_PER_LINE);
+ for (i = 0; i < n; i++) {
+ (void) fprintf(stdout, "%02x ", src[i] & 0xff);
+ }
+ if (format == HEX_ASCII) {
+ for (i = BYTES_PER_LINE-n; i > 0; i--) {
+ (void) fprintf(stdout, " ");
+ }
+ (void) fprintf(stdout, " ");
+ for (i = 0; i < n; i++) {
+ (void) fprintf(stdout, "%c",
+ isprint(src[i]) ? src[i] : '.');
+ }
+ }
+ (void) fprintf(stdout, "\n");
+ nbytes -= n;
+ src += n;
+ }
+}
diff --git a/usr/src/cmd/luxadm/luxadm.h b/usr/src/cmd/luxadm/luxadm.h
new file mode 100644
index 0000000000..b428035c03
--- /dev/null
+++ b/usr/src/cmd/luxadm/luxadm.h
@@ -0,0 +1,110 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * luxadm.h
+ *
+ * External functions and global variables needed for PHOTON
+ */
+
+/*
+ * I18N message number ranges
+ * This file: 13500 - 13999
+ * Shared common messages: 1 - 1999
+ */
+
+#ifndef _LUXADM_H
+#define _LUXADM_H
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* External functions */
+extern int fc_update(unsigned, unsigned, char *);
+extern int fcal_update(unsigned, char *);
+extern int q_qlgc_update(unsigned, char *);
+extern int emulex_update(char *);
+extern int emulex_fcode_reader(int, char *, char *, uint32_t);
+extern int setboot(unsigned, unsigned, char *);
+extern int sysdump(int);
+extern int h_insertSena_fcdev();
+extern int hotplug(int, char **, int, int);
+extern int hotplug_e(int, char **, int, int);
+extern void print_fabric_dtype_prop(uchar_t *, uchar_t *, uchar_t);
+/* SSA and RSM */
+extern int p_download(char *, char *, int, int, uchar_t *);
+extern void ssa_fast_write(char *);
+extern void ssa_perf_statistics(char *);
+extern void ssa_cli_start(char **, int);
+extern void ssa_cli_stop(char **, int);
+extern void ssa_cli_display_config(char **argv, char *, int, int, int);
+extern void cli_display_envsen_data(char **, int);
+extern int p_sync_cache(char *);
+extern int p_purge(char *);
+extern void led(char **, int, int);
+extern void alarm_enable(char **, int, int);
+extern void alarm_set(char **, int);
+extern void power_off(char **, int);
+extern char *get_physical_name(char *);
+
+/* SSA LIB environment sense */
+extern int scsi_get_envsen_data(int, char *, int);
+extern int scsi_put_envsen_data(int, char *, int);
+
+/* hotplug */
+extern void print_errString(int, char *);
+extern int print_devState(char *, char *, int, int, int);
+extern void print_dev_state(char *, int);
+extern void print_bus_state(char *, int);
+extern int dev_handle_insert(char *, int);
+extern int dev_handle_remove(char *, int);
+extern int dev_handle_replace(char *, int);
+
+/* funct.c */
+extern char ctoi(char);
+
+
+/* Functions for FC-HBA based operations */
+extern int fchba_display_port(int verbose);
+extern int fchba_non_encl_probe();
+extern int fchba_inquiry(char **argv);
+extern int fchba_dump_map(char **argv);
+extern int use_fchba();
+extern int fchba_display_link_status(char **);
+extern int fchba_display_config(char **, int, int);
+extern int fchba_hotplug_e(int, char **, int, int);
+
+/* for g_adm.c & hotplug.c */
+int print_devState(char *, char *, int, int, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LUXADM_H */
diff --git a/usr/src/cmd/luxadm/qlgcupdate.c b/usr/src/cmd/luxadm/qlgcupdate.c
new file mode 100644
index 0000000000..653e98e234
--- /dev/null
+++ b/usr/src/cmd/luxadm/qlgcupdate.c
@@ -0,0 +1,1591 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * I18N message number ranges
+ * This file: 21000 - 21499
+ * Shared common messages: 1 - 1999
+ */
+
+
+
+/*
+ * Functions to support the download of FCode to PCI HBAs
+ * Qlogic ISP21XX/22XX boards: FC100/P single port, ISP2200 dual port
+ * and Emulex cards
+ */
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <signal.h>
+#include <dirent.h>
+#include <nl_types.h>
+#include <utmpx.h>
+#include <sys/mnttab.h>
+#include <sys/file.h>
+#include <sys/mtio.h>
+#include <sys/scsi/impl/uscsi.h>
+#include <sys/fibre-channel/fcio.h>
+#include <stgcom.h>
+#include <sys/scsi/adapters/ifpio.h>
+#include <libdevinfo.h>
+#include "luxadm.h"
+
+/* Error codes - used by the fcode_load_file routine */
+#define FCODE_SUCCESS 0 /* successful completion */
+#define FCODE_LOAD_FAILURE 1 /* general failure */
+#define FCODE_IOCTL_FAILURE 2 /* FCODE ioctl download failure */
+
+#define HBA_MAX 128
+#define FCODE_HDR 200
+#define MAX_RETRIES 3
+#define MAX_WAIT_TIME 30
+
+/*
+ * EMULEX Fcode attributes
+ */
+#define EMULEX_FCODE_VERSION_LENGTH 16
+#define EMULEX_READ_BUFFER_SIZE 128
+
+/* Emulex specific error codes */
+#define EMLX_ERRNO_START 0x100
+
+/* Diagnostic error codes */
+#define EMLX_TEST_FAILED (EMLX_ERRNO_START + 0)
+
+/* Download image contains bad data */
+#define EMLX_IMAGE_BAD (EMLX_ERRNO_START + 1)
+/* Download image not compatible with current hardware */
+#define EMLX_IMAGE_INCOMPATIBLE (EMLX_ERRNO_START + 2)
+/* Unable to take adapter offline */
+#define EMLX_IMAGE_FAILED (EMLX_ERRNO_START + 3)
+/* Image download failed */
+#define EMLX_OFFLINE_FAILED (EMLX_ERRNO_START + 4)
+
+
+
+
+/*
+ * This is just a random value chosen to identify Sbus Fcodes. Sbus FCode
+ * for Ivory is based on a 2200 chip but this value does not reflect that.
+ */
+#define SBUS_CHIP_ID 0x1969
+#define IVORY_BUS "/sbus@"
+#define IVORY_DRVR "/SUNW,qlc@"
+
+/* Global variables */
+static char fc_trans[] = "SUNW,ifp"; /* fibre channel transport */
+static char fp_trans[] = "SUNW,qlc"; /* fca layer driver */
+static char fp_trans_id[] = "fp@"; /* transport layer id */
+static char qlgc2100[] = "FC100/P"; /* product name for 2100 */
+static char qlgc2200[] = "ISP2200"; /* product name for 2200 */
+static char qlgc2300[] = "ISP2300"; /* product name for 2300 */
+static char qlgc2312[] = "ISP2312"; /* product name for 2312 */
+/*
+ * The variable qlgc2200Sbus represents the string which is always the
+ * starting string of the version information in an ISP2200 Sbus Fcode.
+ */
+static char qlgc2200Sbus[] = "ISP2200 Sbus FC-AL Host Adapter Driver";
+static char pcibus_list[HBA_MAX][PATH_MAX];
+/* Internal functions */
+static int q_load_file(int, char *);
+static int q_getbootdev(uchar_t *);
+static int q_getdevctlpath(char *, int *);
+static int q_warn(int);
+static int q_findversion(int, int, uchar_t *, uint16_t *);
+static int q_findfileversion(char *, uchar_t *, uint16_t *, int, int *);
+static int q_findSbusfile(int, int *);
+static int memstrstr(char *, char *, int, int);
+static int fcode_load_file(int, char *, int *);
+
+/*
+ * Functions to support Fcode download for Emulex HBAs
+ */
+static int emulex_fcodeversion(di_node_t, uchar_t *);
+static void handle_emulex_error(int, char *);
+
+/*
+ * Searches for and updates the cards. This is the "main" function
+ * and will give the output to the user by calling the subfunctions.
+ * args: FCode file; if NULL only the current FCode version is printed
+ */
+int
+q_qlgc_update(unsigned int verbose, char *file)
+/*ARGSUSED*/
+{
+ int fd, fcode_fd = -1, errnum = 0, devcnt = 0, retval = 0, isSbus = 0;
+ int sbus_off;
+ uint_t i, fflag = 0;
+ uint16_t chip_id = 0, file_id = 0;
+ uchar_t fcode_buf[FCODE_HDR];
+ static uchar_t bootpath[PATH_MAX];
+ static uchar_t version[MAXNAMELEN], version_file[MAXNAMELEN];
+ char devpath[PATH_MAX], tmppath[PATH_MAX];
+ void (*sigint)(); /* to store default SIGTERM setting */
+ static struct utmpx *utmpp = NULL; /* pointer for getutxent() */
+ char *ptr1, *ptr2;
+ char phys_path[PATH_MAX];
+ /*
+ * The variables port1 and port2 are used to store the bus id
+ * e.g. the bus id for this path:
+ * /devices/sbus@12,0/SUNW,qlc@2,30000/fp@0,0:devctl
+ * is "sbus@12". They are initialized to a random value and are
+ * set such that they are not equal initially.
+ */
+ static char port1[MAXNAMELEN] = {NULL};
+ static char port2[MAXNAMELEN] = {NULL};
+
+ if (file) {
+ fflag++;
+
+ /* check for a valid file */
+ if ((fcode_fd = open(file, O_RDONLY)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(21000, "Error: Could not open %s\n"), file);
+ return (1);
+ }
+ if (read(fcode_fd, fcode_buf, FCODE_HDR) != FCODE_HDR) {
+ perror(MSGSTR(21001, "read"));
+ (void) close(fcode_fd);
+ return (1);
+ }
+
+ /*
+ * Check if it's SBUS FCode by calling q_findSbusfile
+ * if it is then isSbus will be 1, if not it will be 0
+ * in case of an error, it will be -1
+ */
+ isSbus = q_findSbusfile(fcode_fd, &sbus_off);
+ if (isSbus == -1) {
+ (void) close(fcode_fd);
+ return (1);
+ }
+
+ /*
+ * FCode header check - make sure it's PCI FCode
+ * Structure of FCode header (byte# refers to byte numbering
+ * in FCode spec, not the byte# of our fcode_buf buffer):
+ * header byte 00 0x55 prom signature byte one
+ * byte 01 0xaa prom signature byte two
+ * data byte 00-03 P C I R
+ * OR
+ * header byte 32 0x55
+ * byte 33 0xaa
+ * data byte 60-63 P C I R
+ * The second format with an offset of 32 is used for ifp prom
+ */
+ if (!(((fcode_buf[0x00] == 0x55) &&
+ (fcode_buf[0x01] == 0xaa) &&
+ (fcode_buf[0x1c] == 'P') &&
+ (fcode_buf[0x1d] == 'C') &&
+ (fcode_buf[0x1e] == 'I') &&
+ (fcode_buf[0x1f] == 'R')) ||
+
+ ((fcode_buf[0x20] == 0x55) &&
+ (fcode_buf[0x21] == 0xaa) &&
+ (fcode_buf[0x3c] == 'P') &&
+ (fcode_buf[0x3d] == 'C') &&
+ (fcode_buf[0x3e] == 'I') &&
+ (fcode_buf[0x3f] == 'R')) ||
+
+ (isSbus))) {
+ (void) fprintf(stderr, MSGSTR(21002,
+ "Error: %s is not a valid FC100/P, "
+ "ISP2200, ISP23xx FCode file.\n"),
+ file);
+ (void) close(fcode_fd);
+ return (1);
+ }
+
+ /* check for single user mode */
+ while ((utmpp = getutxent()) != NULL) {
+ if (strstr(utmpp->ut_line, "run-level") &&
+ (strcmp(utmpp->ut_line, "run-level S") &&
+ strcmp(utmpp->ut_line, "run-level 1"))) {
+ if (q_warn(1)) {
+ (void) endutxent();
+ (void) close(fcode_fd);
+ return (1);
+ }
+ break;
+ }
+ }
+ (void) endutxent();
+
+ /* get bootpath */
+ if (!q_getbootdev((uchar_t *)&bootpath[0]) &&
+ getenv("_LUX_D_DEBUG") != NULL) {
+ (void) fprintf(stdout, " Bootpath: %s\n", bootpath);
+ }
+ }
+ /*
+ * Get count of, and names of PCI slots with ifp device control
+ * (devctl) nodes. Search /devices.
+ */
+ (void) strcpy(devpath, "/devices");
+ if (q_getdevctlpath(devpath, (int *)&devcnt) == 0) {
+ (void) fprintf(stdout, MSGSTR(21003,
+ "\n Found Path to %d FC100/P, ISP2200, ISP23xx Devices\n"),
+ devcnt);
+ } else {
+ (void) fprintf(stderr, MSGSTR(21004,
+ "Error: Could not get /devices path to FC100/P,"
+ "ISP2200, ISP23xx Cards.\n"));
+ retval++;
+ }
+
+ for (i = 0; i < devcnt; i++) {
+
+ (void) strncpy((char *)phys_path, &pcibus_list[i][0],
+ strlen(&pcibus_list[i][0]));
+ if (fflag && (strstr((char *)bootpath,
+ strtok((char *)phys_path, ":")) != NULL)) {
+ (void) fprintf(stderr,
+ MSGSTR(21005, "Ignoring %s (bootpath)\n"),
+ &pcibus_list[i][0]);
+ continue;
+ }
+
+ (void) fprintf(stdout,
+ MSGSTR(21006, "\n Opening Device: %s\n"), &pcibus_list[i][0]);
+ /* Check if the device is valid */
+ if ((fd = open(&pcibus_list[i][0], O_RDWR)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(21000, "Error: Could not open %s\n"),
+ &pcibus_list[i][0]);
+ retval++;
+ continue;
+ }
+ (void) close(fd);
+ /*
+ * Check FCode version present on the adapter (at last boot)
+ */
+ if (q_findversion(verbose, i, (uchar_t *)&version[0],
+ &chip_id) == 0) {
+ if (strlen((char *)version) == 0) {
+ (void) fprintf(stdout, MSGSTR(21007,
+ " Detected FCode Version:\tNo version available for this FCode\n"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(21008,
+ " Detected FCode Version:\t%s\n"), version);
+ }
+ } else {
+ chip_id = 0x0;
+ }
+
+ if (fflag) {
+ /*
+ * For ISP2200, Sbus HBA, do just 1 download
+ * for both the ports (dual port HBA)
+ * Here it is assumed that readdir() always
+ * returns the paths in pcibus_list[] in the
+ * sorted order.
+ */
+ (void) strcpy(tmppath, pcibus_list[i]);
+ if (ptr1 = strstr(tmppath, IVORY_BUS)) {
+ if (ptr2 = strstr(ptr1, IVORY_DRVR)) {
+ ptr2 = strchr(ptr2, ',');
+ if (ptr2 = strchr(++ptr2, ',')) {
+ *ptr2 = '\0';
+ }
+ }
+ (void) strcpy(port2, ptr1);
+ if (strcmp(port1, port2) == 0) {
+ (void) fprintf(stdout, MSGSTR(21037,
+ "/n New FCode has already been downloaded "
+ "to this ISP2200 SBus HBA Card.\n"
+ "It is sufficient to download to one "
+ "port of the ISP2200 SBus HBA Card. "
+ "Moving on...\n"));
+ continue;
+ }
+ }
+ /*
+ * Check version of the supplied FCode file (once)
+ */
+ if ((file_id != 0 && version_file != NULL) ||
+ (q_findfileversion((char *)
+ &fcode_buf[0], (uchar_t *)&version_file[0],
+ &file_id, isSbus, &sbus_off) == 0)) {
+ (void) fprintf(stdout, MSGSTR(21009,
+ " New FCode Version:\t\t%s\n"),
+ version_file);
+ } else {
+ (void) close(fcode_fd);
+ return (1);
+ }
+
+ /*
+ * Load the New FCode
+ * Give warning if file doesn't appear to be correct
+ *
+ */
+ if (chip_id == 0) {
+ errnum = 2; /* can't get chip_id */
+ retval++;
+ } else if (chip_id - file_id != 0) {
+ errnum = 3; /* file/card mismatch */
+ retval++;
+ } else {
+ errnum = 0; /* everything is ok */
+ }
+
+ if (!q_warn(errnum)) {
+ /* Disable user-interrupt Control-C */
+ sigint =
+ (void (*)(int)) signal(SIGINT, SIG_IGN);
+
+ /* Load FCode */
+ (void) fprintf(stdout, MSGSTR(21010,
+ " Loading FCode: %s\n"), file);
+
+ if (q_load_file(fcode_fd,
+ &pcibus_list[i][0]) == 0) {
+ (void) fprintf(stdout, MSGSTR(21011,
+ " Successful FCode download: %s\n"),
+ &pcibus_list[i][0]);
+ (void) strcpy(port1, port2);
+ } else {
+ (void) fprintf(stderr, MSGSTR(21012,
+ "Error: FCode download failed: %s\n"),
+ &pcibus_list[i][0]);
+ retval++;
+ }
+ /* Restore SIGINT (user interrupt) setting */
+ (void) signal(SIGINT, sigint);
+ }
+ }
+ }
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(125, "Complete\n"));
+ if (fcode_fd != -1)
+ (void) close(fcode_fd);
+ return (retval);
+}
+
+
+/*
+ * Retrieve the version banner from the card
+ * uses ioctl: FCIO_FCODE_MCODE_VERSION FCode revision
+ */
+static int
+q_findversion(int verbose, int index, uchar_t *version, uint16_t *chip_id)
+/*ARGSUSED*/
+{
+ int fd, ntries;
+ struct ifp_fm_version *version_buffer = NULL;
+ char prom_ver[100] = {NULL};
+ char mcode_ver[100] = {NULL};
+ fcio_t fcio;
+
+ if (strstr(&pcibus_list[index][0], fc_trans)) {
+
+ if ((fd = open(&pcibus_list[index][0], O_RDWR)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(21000, "Error: Could not open %s\n"),
+ &pcibus_list[index][0]);
+ return (1);
+ }
+
+ if ((version_buffer = (struct ifp_fm_version *)malloc(
+ sizeof (struct ifp_fm_version))) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(21013, "Error: Memory allocation failed\n"));
+ (void) close(fd);
+ return (1);
+ }
+
+ version_buffer->fcode_ver = (char *)version;
+ version_buffer->mcode_ver = mcode_ver;
+ version_buffer->prom_ver = prom_ver;
+ version_buffer->fcode_ver_len = MAXNAMELEN - 1;
+ version_buffer->mcode_ver_len = 100;
+ version_buffer->prom_ver_len = 100;
+
+ if (ioctl(fd, FCIO_FCODE_MCODE_VERSION, version_buffer) < 0) {
+ (void) fprintf(stderr, MSGSTR(21014,
+ "Error: Driver interface FCIO_FCODE_MCODE_VERSION failed\n"));
+ free(version_buffer);
+ (void) close(fd);
+ return (1);
+ }
+ version[version_buffer->fcode_ver_len] = '\0';
+
+ /* Need a way to get card MCODE (firmware) to track certain HW bugs */
+ if (getenv("_LUX_D_DEBUG") != NULL) {
+ (void) fprintf(stdout, " Device %i: QLGC chip_id %x\n",
+ index+1, *chip_id);
+ (void) fprintf(stdout, " FCode:%s\n MCODE:%s\n PROM:%s\n",
+ (char *)version, mcode_ver, prom_ver);
+ }
+ free(version_buffer);
+
+ } else if (strstr(&pcibus_list[index][0], fp_trans)) {
+ /*
+ * Get the fcode and prom's fw version
+ * using the fp ioctls. Currently, we pass
+ * only the fcode version to the calling function
+ * and ignore the FW version (using the existing
+ * implementation).
+ */
+
+ if ((fd = open(&pcibus_list[index][0], O_RDWR)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(4511, "Could not open %s\n"),
+ &pcibus_list[index][0]);
+ (void) close(fd);
+ return (1);
+ }
+ /* Get the fcode version */
+ bzero(version, sizeof (version));
+ fcio.fcio_cmd = FCIO_GET_FCODE_REV;
+ /* Information read operation */
+ fcio.fcio_xfer = FCIO_XFER_READ;
+ fcio.fcio_obuf = (caddr_t)version;
+ fcio.fcio_olen = MAXNAMELEN;
+
+ for (ntries = 0; ntries < MAX_RETRIES; ntries++) {
+ if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
+ if ((errno == EAGAIN) &&
+ (ntries+1 < MAX_RETRIES)) {
+ /* wait 30 secs */
+ (void) sleep(MAX_WAIT_TIME);
+ continue;
+ }
+ (void) close(fd);
+ return (L_FCIO_GET_FCODE_REV_FAIL);
+ }
+ break;
+ }
+ version[MAXNAMELEN-1] = '\0';
+ }
+
+ /* Get type of card from product name in FCode version banner */
+ if (strstr((char *)version, qlgc2100)) {
+ *chip_id = 0x2100;
+ } else if (strstr((char *)version, qlgc2200)) {
+ *chip_id = 0x2200;
+ if (strstr((char *)version, "Sbus")) {
+ *chip_id = SBUS_CHIP_ID;
+ }
+ } else if (strstr((char *)version, qlgc2300)) {
+ *chip_id = 0x2300;
+ } else if (strstr((char *)version, qlgc2312)) {
+ *chip_id = 0x2312;
+ } else {
+ *chip_id = 0x0;
+ }
+
+ (void) close(fd);
+ return (0);
+}
+
+/*
+ * Retrieve the version banner and file type (2100 or 2200) from the file
+ */
+static int
+q_findfileversion(char *dl_fcode, uchar_t *version_file, uint16_t *file_id,
+ int isSbus, int *sbus_offset)
+{
+ int mark;
+ int qlc_offset = 0;
+ char temp[4] = {NULL};
+
+
+ /*
+ * Get file version from FCode for 2100 or 2202
+ */
+ if (isSbus) {
+ *file_id = SBUS_CHIP_ID;
+ } else {
+ if ((dl_fcode[0x23] == 0x22) ||
+ (dl_fcode[0x23] == 0x23)) {
+ *file_id = dl_fcode[0x22] & 0xff;
+ *file_id |= (dl_fcode[0x23] << 8) & 0xff00;
+ } else {
+ *file_id = dl_fcode[0x42] & 0xff;
+ *file_id |= (dl_fcode[0x43] << 8) & 0xff00;
+ }
+ }
+
+ /*
+ * Ok, we're just checking for 2200 here. If it is we need
+ * to offset to find the banner.
+ */
+ if ((*file_id == 0x2200) ||
+ (*file_id == 0x2300) ||
+ (*file_id == 0x2312)) {
+ qlc_offset = -32;
+ }
+
+ /*
+ * If this is an ISP2200 Sbus Fcode file, then search for the string
+ * "ISP2200 FC-AL Host Adapter Driver" in the whole fcode file
+ */
+ if (isSbus) {
+ *file_id = SBUS_CHIP_ID;
+ qlc_offset = *sbus_offset;
+ /* Subtract 111 from the offset we add below for PCI Fcodes */
+ qlc_offset -= 111;
+ }
+
+ /* Banner length varies; grab banner to end of date marker yr/mo/da */
+ version_file[0] = '\0';
+ for (mark = (111 + qlc_offset); mark < (191 + qlc_offset); mark++) {
+ (void) strncpy(temp, (char *)&dl_fcode[mark], 4);
+ if ((strncmp(&temp[0], "/", 1) == 0) &&
+ (strncmp(&temp[3], "/", 1) == 0)) {
+ (void) strncat((char *)version_file,
+ (char *)&dl_fcode[mark], 6);
+ break;
+ }
+ (void) strncat((char *)version_file, temp, 1);
+ }
+ return (0);
+}
+
+/*
+ * Find if the FCode file is a ISP2200 SBUS Fcode file
+ */
+static int
+q_findSbusfile(int fd, int *sbus_offset)
+{
+ static int file_size;
+ char *sbus_info;
+ struct stat statinfo;
+
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ perror(MSGSTR(21022, "seek"));
+ return (-1);
+ }
+ if (fstat(fd, &statinfo)) {
+ perror(MSGSTR(21023, "fstat"));
+ return (-1);
+ }
+ file_size = statinfo.st_size;
+
+ if ((sbus_info = (char *)malloc(file_size)) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(21013, "Error: Memory allocation failed\n"));
+ return (-1);
+ }
+
+ if (read(fd, sbus_info, file_size) < 0) {
+ perror(MSGSTR(21001, "read"));
+ free(sbus_info);
+ return (-1);
+ }
+
+ /*
+ * Search for the version string in the whole file
+ */
+ if ((*sbus_offset = memstrstr((char *)sbus_info, qlgc2200Sbus,
+ file_size, strlen(qlgc2200Sbus))) != -1) {
+ free(sbus_info);
+ return (1);
+ } else {
+ free(sbus_info);
+ return (0);
+ }
+}
+
+
+/*
+ * Build a list of all the devctl entries for all the 2100/2200 based adapters
+ */
+static int
+q_getdevctlpath(char *devpath, int *devcnt)
+{
+ struct stat statbuf;
+ struct dirent *dirp = NULL;
+ DIR *dp = NULL;
+ char *ptr = NULL;
+ int err;
+ int testopen;
+
+ if (lstat(devpath, &statbuf) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(21016, "Error: %s lstat() error\n"), devpath);
+ return (1);
+ }
+
+ if ((strstr(devpath, fc_trans) ||
+ (strstr(devpath, fp_trans_id) && strstr(devpath, fp_trans))) &&
+ strstr(devpath, "devctl")) {
+ /* Verify the path is valid */
+ if ((testopen = open(devpath, O_RDONLY)) >= 0) {
+ (void) close(testopen);
+ (void) strcpy(pcibus_list[*devcnt], devpath);
+ *devcnt += 1;
+ return (0);
+ }
+ }
+
+ if (S_ISDIR(statbuf.st_mode) == 0) {
+ /*
+ * not a directory so
+ * we don't care about it - return
+ */
+ return (0);
+ }
+
+ /*
+ * It's a directory. Call ourself to
+ * traverse the path(s)
+ */
+ ptr = devpath + strlen(devpath);
+ *ptr++ = '/';
+ *ptr = 0;
+
+ /* Forget the /devices/pseudo/ directory */
+ if (strcmp(devpath, "/devices/pseudo/") == 0) {
+ return (0);
+ }
+
+ if ((dp = opendir(devpath)) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(21017, "Error: %s Can't read directory\n"), devpath);
+ return (1);
+ }
+
+ while ((dirp = readdir(dp)) != NULL) {
+
+ if (strcmp(dirp->d_name, ".") == 0 ||
+ strcmp(dirp->d_name, "..") == 0) {
+ continue;
+ }
+ (void) strcpy(ptr, dirp->d_name); /* append name */
+ err = q_getdevctlpath(devpath, devcnt);
+ }
+
+ if (closedir(dp) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(21018, "Error: Can't close directory %s\n"), devpath);
+ return (1);
+ }
+ return (err);
+}
+
+/*
+ * Get the boot device. Cannot load FCode to current boot device.
+ * Boot devices under volume management will prompt a warning.
+ */
+static int
+q_getbootdev(uchar_t *bootpath)
+{
+ struct mnttab mp;
+ struct mnttab mpref;
+ FILE *fp = NULL;
+ static char buf[BUFSIZ];
+ char *p = NULL, *p1 = NULL; /* p = full device, p1 = chunk to rm */
+ char *slot = ":devctl";
+ char *root = "/";
+
+ if ((fp = fopen(MNTTAB, "r")) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(21000, "Error: Could not open %s\n"), MNTTAB);
+ return (1);
+ }
+
+ mntnull(&mpref);
+ mpref.mnt_mountp = (char *)root;
+
+ if (getmntany(fp, &mp, &mpref) != 0 ||
+ mpref.mnt_mountp == NULL) {
+ (void) fprintf(stderr, MSGSTR(21019,
+ "Error: Cannot get boot device, check %s.\n"), MNTTAB);
+ (void) fclose(fp);
+ return (1);
+ }
+ (void) fclose(fp);
+
+ /*
+ * If we can't get a link, we may be dealing with a volume mgr
+ * so give a warning. If a colon is present, we likely have a
+ * non-local disk or cd-rom, so no warning is necessary.
+ * e.g. /devices/pci@1f,4000/scsi@3/sd@6,0:b (cdrom, no link) or
+ * storage-e4:/blah/blah remote boot server
+ */
+ if (readlink(mp.mnt_special, buf, BUFSIZ) < 0) {
+ if (strstr(mp.mnt_special, ":") == NULL) {
+ (void) fprintf(stderr, MSGSTR(21020,
+ "\nWarning: Cannot read boot device link, check %s.\n"), MNTTAB);
+ (void) fprintf(stderr, MSGSTR(21021,
+ "Do not upgrade FCode on adapters controlling the boot device.\n"));
+ }
+ return (1);
+ }
+ /*
+ * Copy boot device path to bootpath. First remove leading
+ * path junk (../../..) then if it's an ifp device, chop off
+ * the disk and add the devctl to the end of the path.
+ */
+ if (p = strstr(buf, "/devices")) {
+ if (strstr(buf, fc_trans) != NULL) {
+ p1 = strrchr(p, '/');
+ *p1 = '\0';
+ }
+ }
+ (void) strcpy((char *)bootpath, (char *)p);
+ if (p1) {
+ (void) strcat((char *)bootpath, slot);
+ }
+ return (0);
+}
+
+/*
+ * Load FCode to card.
+ * uses ioctl: IFPIO_FCODE_DOWNLOAD
+ */
+static int
+q_load_file(int fcode_fd, char *device)
+{
+ static int dev_fd, fcode_size;
+ struct stat stat;
+ ifp_download_t *download_p = NULL;
+ fcio_t fcio;
+ uint16_t file_id = 0;
+ uchar_t *bin;
+
+ if (lseek(fcode_fd, 0, SEEK_SET) == -1) {
+ perror(MSGSTR(21022, "seek"));
+ (void) close(fcode_fd);
+ return (1);
+ }
+ if (fstat(fcode_fd, &stat) == -1) {
+ perror(MSGSTR(21023, "fstat"));
+ (void) close(fcode_fd);
+ return (1);
+ }
+
+ fcode_size = stat.st_size;
+
+ if (strstr(device, fc_trans)) {
+ if ((download_p = (ifp_download_t *)malloc(
+ sizeof (ifp_download_t) + fcode_size)) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(21013, "Error: Memory allocation failed\n"));
+ (void) close(fcode_fd);
+ return (1);
+ }
+ } else {
+ if ((bin = (uchar_t *)malloc(fcode_size)) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(21013, "Error: Memory allocation failed\n"));
+ (void) close(fcode_fd);
+ return (1);
+ }
+ }
+
+ if (strstr(device, fc_trans)) {
+ if (read(fcode_fd, download_p->dl_fcode, fcode_size)
+ != fcode_size) {
+ perror(MSGSTR(21001, "read"));
+ free(download_p);
+ (void) close(fcode_fd);
+ return (1);
+ }
+ } else {
+ if (read(fcode_fd, bin, fcode_size)
+ != fcode_size) {
+ perror(MSGSTR(21001, "read"));
+ free(bin);
+ (void) close(fcode_fd);
+ return (1);
+ }
+ }
+
+
+ if ((dev_fd = open(device, O_RDWR|O_EXCL)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(21000, "Error: Could not open %s\n"), device);
+ free(download_p);
+ return (1);
+ }
+ if (strstr(device, fc_trans)) {
+ download_p->dl_fcode_len = fcode_size;
+ file_id = download_p->dl_fcode[0x42] & 0xff;
+ file_id |= (download_p->dl_fcode[0x43] << 8) & 0xff00;
+ download_p->dl_chip_id = file_id;
+ if (ioctl(dev_fd, IFPIO_FCODE_DOWNLOAD, download_p) < 0) {
+ (void) fprintf(stderr, MSGSTR(21024,
+ "Error: Driver interface IFPIO_FCODE_DOWNLOAD failed\n"));
+ free(download_p);
+ (void) close(dev_fd);
+ return (1);
+ }
+ free(download_p);
+ } else if (strstr(device, fp_trans)) {
+ fcio.fcio_cmd = FCIO_DOWNLOAD_FCODE;
+ /* Information read operation */
+ fcio.fcio_xfer = FCIO_XFER_WRITE;
+ fcio.fcio_ibuf = (caddr_t)bin;
+ fcio.fcio_ilen = fcode_size;
+
+ if (ioctl(dev_fd, FCIO_CMD, &fcio) != 0) {
+ (void) fprintf(stderr, MSGSTR(21036,
+ "Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n"));
+ free(download_p);
+ (void) close(dev_fd);
+ return (1);
+ }
+ free(bin);
+ }
+ (void) close(dev_fd);
+ return (0);
+}
+
+/*
+ * Issue warning strings and loop for Yes/No user interaction
+ * err# 0 -- we're ok, warn for pending FCode load
+ * 1 -- not in single user mode
+ * 2 -- can't get chip_id
+ * 3 -- card and file do not have same type (2100/2200)
+ */
+static int
+q_warn(int errnum)
+{
+ char input[1024];
+ input[0] = '\0';
+
+ if (errnum == 1) {
+ (void) fprintf(stderr, MSGSTR(21025,
+ "\nWarning: System is not in single-user mode.\n"));
+ (void) fprintf(stderr, MSGSTR(21026,
+ "Loading FCode will reset the adapter and terminate I/O activity\n"));
+ } else {
+ if (errnum == 2) {
+ (void) fprintf(stderr, MSGSTR(21027,
+ " Warning: FCode is missing or existing FCode has"
+ " unrecognized version.\n"));
+ return (1);
+ } else if (errnum == 3) {
+ (void) fprintf(stderr, MSGSTR(21028,
+ " Warning: New FCode file version does not match this"
+ " board type. Skipping...\n"));
+ return (1);
+ }
+ (void) fprintf(stderr, MSGSTR(21029,
+ "\nWARNING!! This program will update the FCode in this"
+ " FC100/PCI, ISP2200/PCI, ISP23xx/PCI "
+ " and Emulex devices.\n"));
+ (void) fprintf(stderr, MSGSTR(21030,
+ "This may take a few (5) minutes. Please be patient.\n"));
+ }
+
+loop1:
+ (void) fprintf(stderr, MSGSTR(21031,
+ "Do you wish to continue ? (y/n) "));
+
+ (void) gets(input);
+
+ if ((strcmp(input, MSGSTR(21032, "y")) == 0) ||
+ (strcmp(input, MSGSTR(40, "yes")) == 0)) {
+ return (0);
+ } else if ((strcmp(input, MSGSTR(21033, "n")) == 0) ||
+ (strcmp(input, MSGSTR(45, "no")) == 0)) {
+ (void) fprintf(stderr,
+ MSGSTR(21034, "Not Downloading FCode\n"));
+ return (1);
+ } else {
+ (void) fprintf(stderr, MSGSTR(21035, "Invalid input\n"));
+ goto loop1;
+ }
+}
+
+/*
+ * Name : memstrstr
+ * Input : pointer to buf1, pointer to buf2, size of buf1, size of buf2
+ * Returns :
+ * Offset of the start of contents-of-buf2 in buf1 if it is found
+ * -1 if buf1 does not contain contents of buf2
+ * Synopsis:
+ * This function works similar to strstr(). The difference is that null
+ * characters in the buffer are treated like any other character. So, buf1
+ * and buf2 can have embedded null characters in them.
+ */
+static int
+memstrstr(char *s1, char *s2, int size1, int size2)
+{
+ int count1, count2;
+ char *s1_ptr, *s2_ptr;
+
+ count1 = size1; count2 = size2;
+ s1_ptr = s1; s2_ptr = s2;
+
+ if ((size2 == 0)||(size1 == 0))
+ return (-1);
+
+ for (count1 = 0; count1 < (size1 - size2 + 1); count1++) {
+ if (*s1_ptr++ == *s2_ptr++) {
+ if (--count2 == 0) {
+ return (count1 - size2 + 1);
+ }
+ continue;
+ }
+ count2 = size2;
+ s2_ptr = s2;
+ }
+
+ return (-1);
+}
+
+/*
+ * generic fcode load file routine. given a file descriptor to a fcode file
+ * this routine will issue the FCIO_DOWNLOAD_FCODE ioctl to the given
+ * device. Any ioctl errors will be returned in fcio_errno
+ *
+ * Arguments:
+ * fcode_fd file descriptor to a fcode file
+ * device path to the device we will be downloading the fcode onto
+ * fcio_errno pointer to an int that will be used to return any errors
+ * back to the caller
+ * Retrurn Values:
+ * 0 successful download
+ * >0 otherwise
+ */
+static int
+fcode_load_file(int fcode_fd, char *device, int *fcio_errno)
+{
+
+ fcio_t fcio;
+ static int dev_fd, fcode_size;
+ uchar_t *bin;
+ struct stat stat;
+
+ if (device == NULL || fcio_errno == NULL) {
+ return (FCODE_LOAD_FAILURE);
+ }
+
+ *fcio_errno = 0;
+ if (lseek(fcode_fd, 0, SEEK_SET) == -1) {
+ perror(MSGSTR(21022, "seek"));
+ return (FCODE_LOAD_FAILURE);
+ }
+
+ if (fstat(fcode_fd, &stat) == -1) {
+ perror(MSGSTR(21023, "fstat"));
+ return (FCODE_LOAD_FAILURE);
+ }
+
+ fcode_size = stat.st_size;
+
+ if ((bin = (uchar_t *)malloc(fcode_size)) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(21013, "Error: Memory allocation failed\n"));
+ return (FCODE_LOAD_FAILURE);
+ }
+
+ if (read(fcode_fd, bin, fcode_size)
+ != fcode_size) {
+ perror(MSGSTR(21001, "read"));
+ free(bin);
+ return (FCODE_LOAD_FAILURE);
+ }
+
+ if ((dev_fd = open(device, O_RDWR|O_EXCL)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(21122, "Error: Could not open %s, failed "
+ "with errno %d\n"), device, errno);
+ free(bin);
+ return (FCODE_LOAD_FAILURE);
+ }
+
+ fcio.fcio_cmd = FCIO_DOWNLOAD_FCODE;
+ fcio.fcio_xfer = FCIO_XFER_WRITE;
+ fcio.fcio_ibuf = (caddr_t)bin;
+ fcio.fcio_ilen = fcode_size;
+
+ if (ioctl(dev_fd, FCIO_CMD, &fcio) != 0) {
+ (void) close(dev_fd);
+ *fcio_errno = fcio.fcio_errno;
+ free(bin);
+ return (FCODE_IOCTL_FAILURE);
+ }
+
+ free(bin);
+ (void) close(dev_fd);
+ return (FCODE_SUCCESS);
+}
+
+/*
+ * Searches for and updates the fcode for Emulex HBA cards
+ * args: FCode file; if NULL only the current FCode
+ * version is printed
+ */
+
+int
+emulex_update(char *file)
+{
+
+ int fd, retval = 0;
+ int devcnt = 0;
+ uint_t state = 0, fflag = 0;
+ static uchar_t bootpath[PATH_MAX];
+ int fcode_fd = -1;
+ static struct utmpx *utmpp = NULL;
+ di_node_t root;
+ di_node_t node, sib_node, count_node;
+ di_minor_t minor_node;
+ char phys_path[PATH_MAX], *path;
+ int errnum = 0, fcio_errno = 0;
+ static uchar_t prom_ver_data[MAXNAMELEN];
+ static char ver_file[EMULEX_FCODE_VERSION_LENGTH];
+ void (*sigint)();
+ int prop_entries = -1;
+ int *port_data = NULL;
+
+ if (file) {
+ /* set the fcode download flag */
+ fflag++;
+
+ /* check for a valid file */
+ if ((fcode_fd = open(file, O_RDONLY)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(21118, "Error: Could not open %s, failed "
+ "with errno %d\n"), file, errno);
+ return (1);
+ }
+
+ /* check for single user mode */
+ while ((utmpp = getutxent()) != NULL) {
+ if (strstr(utmpp->ut_line, "run-level") &&
+ (strcmp(utmpp->ut_line, "run-level S") &&
+ strcmp(utmpp->ut_line, "run-level 1"))) {
+ if (q_warn(1)) {
+ (void) endutxent();
+ (void) close(fcode_fd);
+ return (1);
+ }
+ break;
+ }
+ }
+ (void) endutxent();
+
+ /* get bootpath */
+ if (!q_getbootdev((uchar_t *)&bootpath[0]) &&
+ getenv("_LUX_D_DEBUG") != NULL) {
+ (void) fprintf(stdout, " Bootpath: %s\n", bootpath);
+ }
+ }
+
+ /*
+ * Download the Fcode to all the emulex cards found
+ */
+
+ /* Create a snapshot of the kernel device tree */
+ if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
+ (void) fprintf(stderr, MSGSTR(21114,
+ "Error: Could not get /devices path to "
+ "Emulex Devices.\n"));
+ retval++;
+ }
+
+ /* point to first node which matches emulex driver */
+ node = di_drv_first_node("emlxs", root);
+
+ if (node == DI_NODE_NIL) {
+ /*
+ * Could not find any emulex cards
+ */
+ (void) di_fini(root);
+ (void) fprintf(stderr, MSGSTR(21115,
+ "\n Found Path to %d Emulex Devices.\n"), devcnt);
+ retval++;
+ } else {
+
+ count_node = node;
+ while (count_node != DI_NODE_NIL) {
+ state = di_state(count_node);
+ if ((state & DI_DRIVER_DETACHED)
+ != DI_DRIVER_DETACHED) {
+ devcnt++;
+ }
+ count_node = di_drv_next_node(count_node);
+ }
+ (void) fprintf(stdout, MSGSTR(21116,
+ "\n Found Path to %d Emulex Devices.\n"), devcnt);
+ }
+
+
+ /*
+ * Traverse device tree to find all emulex cards
+ */
+ while (node != DI_NODE_NIL) {
+
+ state = di_state(node);
+ if ((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) {
+ node = di_drv_next_node(node);
+ continue;
+ }
+
+ sib_node = di_child_node(node);
+ while (sib_node != DI_NODE_NIL) {
+
+ state = di_state(sib_node);
+ if ((state & DI_DRIVER_DETACHED) !=
+ DI_DRIVER_DETACHED) {
+
+ /* Found an attached node */
+ prop_entries = di_prop_lookup_ints(
+ DDI_DEV_T_ANY, sib_node,
+ "port", &port_data);
+ if (prop_entries != -1) {
+
+ /* Found a node with "port" property */
+ minor_node = di_minor_next(sib_node,
+ DI_MINOR_NIL);
+ break;
+ }
+ }
+ sib_node = di_sibling_node(sib_node);
+ }
+
+ if (sib_node == DI_NODE_NIL) {
+ return (1);
+ }
+ path = di_devfs_path(sib_node);
+ (void) strcpy(phys_path, "/devices");
+ (void) strncat(phys_path, path, strlen(path));
+ di_devfs_path_free(path);
+
+ if (fflag && (strstr((char *)bootpath,
+ (char *)phys_path) != NULL)) {
+ (void) fprintf(stderr,
+ MSGSTR(21117, "Ignoring %s (bootpath)\n"),
+ phys_path);
+ node = di_drv_next_node(node);
+ continue;
+ }
+
+ if (minor_node) {
+ (void) strncat(phys_path, ":", 1);
+ (void) strncat(phys_path,
+ di_minor_name(minor_node),
+ strlen(di_minor_name(minor_node)));
+ }
+
+ (void) fprintf(stdout,
+ MSGSTR(21107, "\n Opening Device: %s\n"),
+ phys_path);
+
+ /* Check if the device is valid */
+ if ((fd = open(phys_path, O_RDWR)) < 0) {
+ (void) fprintf(stderr,
+ MSGSTR(21121, "Error: Could not open %s, failed "
+ "with errno %d\n"), phys_path, errno);
+ retval++;
+ node = di_drv_next_node(node);
+ continue;
+ }
+
+ (void) close(fd);
+
+ /*
+ * Check FCode version present on the adapter
+ * (at last boot)
+ */
+ memset(prom_ver_data, 0, sizeof (prom_ver_data));
+ if (emulex_fcodeversion(node, (uchar_t *)&prom_ver_data[0])
+ == 0) {
+ errnum = 0;
+ if (strlen((char *)prom_ver_data) == 0) {
+ (void) fprintf(stdout, MSGSTR(21108,
+ " Detected FCode Version:\tNo version available for this FCode\n"));
+ } else {
+ (void) fprintf(stdout, MSGSTR(21109,
+ " Detected FCode Version:\t%s\n"),
+ prom_ver_data);
+ }
+ } else {
+ errnum = 2; /* can't get prom properties */
+ retval++;
+ }
+
+ if (fflag) {
+
+ memset(ver_file, 0, sizeof (ver_file));
+ if (emulex_fcode_reader(fcode_fd, "fcode-version",
+ ver_file, sizeof (ver_file)) == 0) {
+ (void) fprintf(stdout, MSGSTR(21110,
+ " New FCode Version:\t\t%s\n"),
+ ver_file);
+ } else {
+ di_fini(root);
+ (void) close(fcode_fd);
+ return (1);
+ }
+
+ /*
+ * Load the New FCode
+ * Give warning if file doesn't appear to be correct
+ */
+ if (!q_warn(errnum)) {
+ /* Disable user-interrupt Control-C */
+ sigint =
+ (void (*)(int)) signal(SIGINT, SIG_IGN);
+ /* Load FCode */
+ (void) fprintf(stdout, MSGSTR(21111,
+ " Loading FCode: %s\n"), file);
+ if (fcode_load_file(fcode_fd, phys_path,
+ &fcio_errno) == FCODE_SUCCESS) {
+ (void) fprintf(stdout, MSGSTR(21112,
+ " Successful FCode download: %s\n"),
+ phys_path);
+ } else {
+ handle_emulex_error(fcio_errno,
+ phys_path);
+ retval++;
+ }
+
+ /* Restore SIGINT (user interrupt) setting */
+ (void) signal(SIGINT, sigint);
+ }
+ }
+
+ node = di_drv_next_node(node);
+ }
+
+ di_fini(root);
+ (void) fprintf(stdout, " ");
+ (void) fprintf(stdout, MSGSTR(125, "Complete\n"));
+ if (fcode_fd != -1)
+ (void) close(fcode_fd);
+ return (retval);
+
+}
+
+/*
+ * Retrieve the version from the card.
+ * uses PROM properties
+ */
+static int
+emulex_fcodeversion(di_node_t node, uchar_t *ver) {
+ di_prom_prop_t promprop;
+ di_prom_handle_t ph;
+ char *promname;
+ uchar_t *ver_data = NULL;
+ int size, found = 0;
+
+ /* check to make sure ver is not NULL */
+ if (ver == NULL) {
+ return (1);
+ }
+
+ if ((ph = di_prom_init()) == DI_PROM_HANDLE_NIL) {
+ return (1);
+ }
+
+ for (promprop = di_prom_prop_next(ph, node,
+ DI_PROM_PROP_NIL);
+ promprop != DI_PROM_PROP_NIL;
+ promprop = di_prom_prop_next(ph, node, promprop)) {
+ if (((promname = di_prom_prop_name(
+ promprop)) != NULL) &&
+ (strcmp(promname, "fcode-version") == 0)) {
+ size = di_prom_prop_data(promprop, &ver_data);
+ (void) memset(ver, NULL, size);
+ (void) memcpy(ver, ver_data, size);
+ found = 1;
+ }
+ }
+
+ if (found) {
+ return (0);
+ } else {
+ return (1);
+ }
+}
+
+/*
+ * Retrieves information from the Emulex fcode
+ *
+ * Given a pattern, this routine will look for this pattern in the fcode
+ * file and if found will return the pattern value
+ *
+ * possible patterns are manufacturer and fcode-version
+ */
+int
+emulex_fcode_reader(int fcode_fd, char *pattern, char *pattern_value,
+ uint32_t pattern_value_size) {
+ int32_t i = 0;
+ uint32_t n = 0;
+ uint32_t b = 0;
+ char byte1;
+ char byte2;
+ char byte3;
+ char byte4;
+ char buffer1[EMULEX_READ_BUFFER_SIZE];
+ char buffer2[EMULEX_READ_BUFFER_SIZE];
+ uint32_t plen, image_size;
+ struct stat stat;
+ uchar_t *image;
+
+ /* Check the arguments */
+ if (!fcode_fd || !pattern_value || pattern_value_size < 8) {
+ return (1);
+ }
+
+ if (fstat(fcode_fd, &stat) == -1) {
+ perror(MSGSTR(21023, "fstat"));
+ return (1);
+ }
+ image_size = stat.st_size;
+ if (image_size < 2) {
+ return (1);
+ }
+ if ((image = (uchar_t *)calloc(image_size, 1)) == NULL) {
+ (void) fprintf(stderr,
+ MSGSTR(21013, "Error: Memory allocation failed\n"));
+ return (1);
+ }
+
+ /* Read the fcode image file */
+ lseek(fcode_fd, 0, SEEK_SET);
+ read(fcode_fd, image, image_size);
+
+ /* Initialize */
+ bzero(buffer1, sizeof (buffer1));
+ bzero(buffer2, sizeof (buffer2));
+ /* Default pattern_value string */
+ strcpy((char *)pattern_value, "<unknown>");
+ plen = strlen(pattern);
+ n = 0;
+ b = 0;
+ i = 0;
+
+ /* Search entire image for pattern string */
+ while (i <= (image_size - 2)) {
+ /* Read next two bytes */
+ byte1 = image[i++];
+ byte2 = image[i++];
+
+ /* Check second byte first due to endianness */
+
+ /* Save byte in circular buffer */
+ buffer1[b++] = byte2;
+ if (b == sizeof (buffer1)) {
+ b = 0;
+ }
+
+ /* Check byte for pattern match */
+ if (pattern[n++] != byte2) {
+ /* If no match, then reset pattern */
+ n = 0;
+ } else {
+ /*
+ * If complete pattern has been matched then
+ * exit loop
+ */
+ if (n == plen) {
+ goto found;
+ }
+ }
+
+
+ /* Check first byte second due to endianness */
+ /* Save byte in circular buffer */
+ buffer1[b++] = byte1;
+ if (b == sizeof (buffer1)) {
+ b = 0;
+ }
+ /* Check byte for pattern match */
+ if (pattern[n++] != byte1) {
+ /* If no match, then reset pattern */
+ n = 0;
+ } else {
+ /*
+ * If complete pattern has been matched
+ * then exit loop
+ */
+ if (n == plen) {
+ goto found;
+ }
+ }
+ }
+
+ /* Not found. Try again with different endianess */
+
+ /* Initialize */
+ bzero(buffer1, sizeof (buffer1));
+ bzero(buffer2, sizeof (buffer2));
+ n = 0;
+ b = 0;
+ i = 0;
+
+ /* Search entire 32bit endian image for pattern string */
+ while (i <= (image_size - 4)) {
+ /* Read next four bytes */
+ byte1 = image[i++];
+ byte2 = image[i++];
+ byte3 = image[i++];
+ byte4 = image[i++];
+
+ /* Save byte in circular buffer */
+ buffer1[b++] = byte4;
+ if (b == sizeof (buffer1)) {
+ b = 0;
+ }
+
+ /* Check byte for pattern match */
+ if (pattern[n++] != byte4) {
+ /* If no match, then reset pattern */
+ n = 0;
+ } else {
+ /*
+ * If complete pattern has been matched then exit loop
+ */
+ if (n == plen) {
+ goto found;
+ }
+ }
+
+ /* Save byte in circular buffer */
+ buffer1[b++] = byte3;
+ if (b == sizeof (buffer1)) {
+ b = 0;
+ }
+
+ /* Check byte for pattern match */
+ if (pattern[n++] != byte3) {
+ /* If no match, then reset pattern */
+ n = 0;
+ } else {
+ /*
+ * If complete pattern has been matched then exit loop
+ */
+ if (n == plen) {
+ goto found;
+ }
+ }
+
+ /* Save byte in circular buffer */
+ buffer1[b++] = byte2;
+ if (b == sizeof (buffer1)) {
+ b = 0;
+ }
+
+ /* Check byte for pattern match */
+ if (pattern[n++] != byte2) {
+ /* If no match, then reset pattern */
+ n = 0;
+ } else {
+ /*
+ * If complete pattern has been matched then exit loop
+ */
+ if (n == plen) {
+ goto found;
+ }
+ }
+
+ /* Save byte in circular buffer */
+ buffer1[b++] = byte1;
+ if (b == sizeof (buffer1)) {
+ b = 0;
+ }
+
+ /* Check byte for pattern match */
+ if (pattern[n++] != byte1) {
+ /* If no match, then reset pattern */
+ n = 0;
+ } else {
+ /*
+ * If complete pattern has been matched then exit loop
+ */
+ if (n == plen) {
+ goto found;
+ }
+ }
+ }
+
+ free(image);
+ return (1);
+
+found:
+ free(image);
+
+ /* Align buffer and eliminate non-printable characters */
+ for (i = 0; i < (sizeof (buffer1)-plen); i++) {
+ byte1 = buffer1[b++];
+ if (b == sizeof (buffer1)) {
+ b = 0;
+ }
+ /* Zero any non-printable characters */
+ if (byte1 >= 33 && byte1 <= 126) {
+ buffer2[i] = byte1;
+ } else {
+ buffer2[i] = 0;
+ }
+ }
+
+ /*
+ * Scan backwards for first non-zero string. This will be the
+ * version string
+ */
+ for (i = sizeof (buffer1)-plen-1; i >= 0; i--) {
+ if (buffer2[i] != 0) {
+ for (; i >= 0; i--) {
+ if (buffer2[i] == 0) {
+ i++;
+ strncpy((char *)pattern_value,
+ &buffer2[i], pattern_value_size);
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return (0);
+}
+
+/*
+ * error handling routine to handle emulex error conditions
+ */
+static void
+handle_emulex_error(int fcio_errno, char *phys_path) {
+ if (fcio_errno == EMLX_IMAGE_BAD) {
+ fprintf(stderr, MSGSTR(21119,
+ "Error: Fcode download failed. "
+ "Bad fcode image.\n"));
+ } else if (fcio_errno == EMLX_IMAGE_INCOMPATIBLE) {
+ fprintf(stderr, MSGSTR(21120,
+ "Error: Fcode download failed. Fcode is not "
+ "compatible with card.\n"));
+ } else {
+ (void) fprintf(stderr, MSGSTR(21036,
+ "Error: Driver interface FCIO_DOWNLOAD_FCODE failed\n"));
+ (void) fprintf(stderr,
+ MSGSTR(21113,
+ "Error: FCode download failed: %s\n"),
+ phys_path);
+ }
+}
diff --git a/usr/src/cmd/luxadm/setboot.c b/usr/src/cmd/luxadm/setboot.c
new file mode 100644
index 0000000000..909d312111
--- /dev/null
+++ b/usr/src/cmd/luxadm/setboot.c
@@ -0,0 +1,257 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * I18N message number ranges
+ * This file: 6000 - 6499
+ * Shared common messages: 1 - 1999
+ */
+
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/mnttab.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/openpromio.h>
+
+
+/*
+ * For i18n
+ */
+#include <stgcom.h>
+
+
+/*
+ * 128 is the size of the largest (currently) property name
+ * 8192 - MAXPROPSIZE - sizeof (int) is the size of the largest
+ * (currently) property value, viz. nvramrc.
+ * the sizeof(uint_t) is from struct openpromio
+ */
+#define MAXPROPSIZE 128
+#define MAXVALSIZE (8192 - MAXPROPSIZE - sizeof (uint_t))
+
+#define BOOTDEV_PROP_NAME "boot-device"
+
+static int getbootdevname(char *, char *);
+static int setprom(unsigned, unsigned, char *);
+extern int devfs_dev_to_prom_name(char *, char *);
+
+/*
+ * Call getbootdevname() to get the absolute pathname of boot device
+ * and call setprom() to set the boot-device variable.
+ */
+int
+setboot(unsigned int yes, unsigned int verbose, char *fname)
+{
+ char bdev[MAXPATHLEN];
+
+ if (!getbootdevname(fname, bdev)) {
+ (void) fprintf(stderr, MSGSTR(6000,
+ "Cannot determine device name for %s\n"),
+ fname);
+ return (errno);
+ }
+
+ return (setprom(yes, verbose, bdev));
+}
+
+/*
+ * Read the mnttab and resolve the special device of the fs we are
+ * interested in, into an absolute pathname
+ */
+static int
+getbootdevname(char *bootfs, char *bdev)
+{
+ FILE *f;
+ char *fname;
+ char *devname;
+ struct mnttab m;
+ struct stat sbuf;
+ int mountpt = 0;
+ int found = 0;
+
+ devname = bootfs;
+
+ if (stat(bootfs, &sbuf) < 0) {
+ perror(MSGSTR(6001, "stat"));
+ return (0);
+ }
+
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFBLK:
+ break;
+ default:
+ mountpt = 1;
+ break;
+ }
+
+ if (mountpt) {
+ fname = MNTTAB;
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ perror(fname);
+ return (0);
+ }
+
+ while (getmntent(f, &m) == 0) {
+ if (strcmp(m.mnt_mountp, bootfs))
+ continue;
+ else {
+ found = 1;
+ break;
+ }
+ }
+
+ (void) fclose(f);
+
+ if (!found) {
+ return (0);
+ }
+ devname = m.mnt_special;
+ }
+
+ if (devfs_dev_to_prom_name(devname, bdev) != 0) {
+ perror(devname);
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * setprom() - use /dev/openprom to read the "boot_device" variable and set
+ * it to the new value.
+ */
+static int
+setprom(unsigned yes, unsigned verbose, char *bdev)
+{
+ struct openpromio *pio;
+ int fd;
+ char save_bootdev[MAXVALSIZE];
+
+ if ((fd = open("/dev/openprom", O_RDWR)) < 0) {
+ perror(MSGSTR(6002, "Could not open openprom dev"));
+ return (errno);
+ }
+
+ pio = (struct openpromio *)malloc(sizeof (struct openpromio) +
+ MAXVALSIZE + MAXPROPSIZE);
+
+ if (pio == (struct openpromio *)NULL) {
+ perror(MSGSTR(6003, " Error: Unable to allocate memory."));
+ return (errno);
+ }
+
+ pio->oprom_size = MAXVALSIZE;
+ (void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME);
+
+ if (ioctl(fd, OPROMGETOPT, pio) < 0) {
+ perror(MSGSTR(6004, "openprom getopt ioctl"));
+ return (errno);
+ }
+
+ /*
+ * save the existing boot-device, so we can use it if setting
+ * to new value fails.
+ */
+ (void) strcpy(save_bootdev, pio->oprom_array);
+
+ if (verbose) {
+ (void) fprintf(stdout,
+ MSGSTR(6005,
+ "Current boot-device = %s\n"), pio->oprom_array);
+ (void) fprintf(stdout, MSGSTR(6006,
+ "New boot-device = %s\n"), bdev);
+ }
+
+ if (!yes) {
+ (void) fprintf(stdout, MSGSTR(6007,
+ "Do you want to change boot-device "
+ "to the new setting? (y/n) "));
+ switch (getchar()) {
+ case 'Y':
+ case 'y':
+ break;
+ default:
+ return (0);
+ }
+ }
+
+ /* set the new value for boot-device */
+
+ pio->oprom_size = (int)strlen(BOOTDEV_PROP_NAME) + 1 +
+ (int)strlen(bdev);
+
+ (void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME);
+ (void) strcpy(pio->oprom_array + (int)strlen(BOOTDEV_PROP_NAME) + 1,
+ bdev);
+
+ if (ioctl(fd, OPROMSETOPT, pio) < 0) {
+ perror(MSGSTR(6008, "openprom setopt ioctl"));
+ return (errno);
+ }
+
+ /* read back the value that was set */
+
+ pio->oprom_size = MAXVALSIZE;
+ (void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME);
+
+ if (ioctl(fd, OPROMGETOPT, pio) < 0) {
+ perror(MSGSTR(6009, "openprom getopt ioctl"));
+ return (errno);
+ }
+
+ if (strcmp(bdev, pio->oprom_array)) {
+
+ /* could not set the new device name, set the old one back */
+
+ perror(MSGSTR(6010,
+ "Could not set boot-device, reverting to old value"));
+ pio->oprom_size = (int)strlen(BOOTDEV_PROP_NAME) + 1 +
+ (int)strlen(save_bootdev);
+
+ (void) strcpy(pio->oprom_array, BOOTDEV_PROP_NAME);
+ (void) strcpy(pio->oprom_array +
+ (int)strlen(BOOTDEV_PROP_NAME) + 1,
+ save_bootdev);
+
+ if (ioctl(fd, OPROMSETOPT, pio) < 0) {
+ perror(MSGSTR(6011, "openprom setopt ioctl"));
+ return (errno);
+ }
+
+ }
+
+ (void) close(fd);
+
+ return (0);
+}
diff --git a/usr/src/cmd/luxadm/x86_adm.c b/usr/src/cmd/luxadm/x86_adm.c
new file mode 100644
index 0000000000..a53b2b92fe
--- /dev/null
+++ b/usr/src/cmd/luxadm/x86_adm.c
@@ -0,0 +1,533 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <hbaapi.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/fibre-channel/fcio.h>
+#include <sys/fibre-channel/impl/fc_error.h>
+#include <sys/scsi/adapters/scsi_vhci.h>
+#include "common.h"
+#include "errorcodes.h"
+#include <locale.h>
+
+/* The i18n catalog */
+nl_catd l_catd;
+
+void
+i18n_catopen() {
+ static int fileopen = 0;
+
+ if (setlocale(LC_ALL, "") == NULL) {
+ (void) fprintf(stderr,
+ "Cannot operate in the locale requested. "
+ "Continuing in the default C locale\n");
+ }
+ if (!fileopen) {
+ l_catd = catopen("a5k_g_fc_i18n_cat", NL_CAT_LOCALE);
+ if (l_catd == (nl_catd)-1) {
+ return;
+ }
+ fileopen = 1;
+ }
+ return;
+
+}
+
+/*
+ * Given an error number, this functions
+ * calls the get_errString() to print a
+ * corresponding error message to the stderr.
+ * get_errString() always returns an error
+ * message, even in case of undefined error number.
+ * So, there is no need to check for a NULL pointer
+ * while printing the error message to the stdout.
+ *
+ * RETURNS: N/A
+ *
+ */
+void
+print_errString(int errnum, char *devpath)
+{
+
+char *errStr;
+
+ errStr = get_errString(errnum);
+
+ if (devpath == NULL) {
+ (void) fprintf(stderr,
+ "%s \n\n", errStr);
+ } else {
+ (void) fprintf(stderr,
+ "%s - %s.\n\n", errStr, devpath);
+ }
+
+ /* free the allocated memory for error string */
+ if (errStr != NULL)
+ (void) free(errStr);
+}
+
+static void terminate() {
+ fprintf(stdout, MSGSTR(2506, "Unsupported"));
+ fprintf(stdout, "\n");
+ exit(1);
+}
+
+/*ARGSUSED*/
+int adm_display_config(char **a) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+void adm_download(char **a, char *b) {
+ terminate();
+}
+
+/*ARGSUSED*/
+void up_encl_name(char **a, int b) {
+ terminate();
+}
+
+void adm_failover(char **argv) {
+ int path_index = 0, err = 0, fd;
+ char path_class[MAXNAMELEN];
+ char client_path[MAXPATHLEN];
+ char *path_phys = NULL, *trailingMinor;
+ sv_switch_to_cntlr_iocdata_t iocsc;
+
+ (void) memset(path_class, 0, sizeof (path_class));
+ (void) strcpy(path_class, argv[path_index++]);
+ if ((strcmp(path_class, "primary") != 0) &&
+ (strcmp(path_class, "secondary") != 0)) {
+ (void) fprintf(stderr,
+ MSGSTR(2300, "Incorrect pathclass\n"));
+ exit(-1);
+ }
+
+ if ((fd = open("/devices/scsi_vhci:devctl", O_RDWR)) < 0) {
+ print_errString(L_OPEN_PATH_FAIL, "/devices/scsi_vhci:devctl");
+ exit(-1);
+ }
+
+ iocsc.client = client_path;
+ iocsc.class = path_class;
+
+ while (argv[path_index] != NULL) {
+ path_phys =
+ get_slash_devices_from_osDevName(argv[path_index++],
+ STANDARD_DEVNAME_HANDLING);
+ if ((path_phys == NULL) ||
+ (strstr(path_phys, "/devices/scsi_vhci") == NULL)) {
+ (void) fprintf(stderr,
+ MSGSTR(2301, "Incorrect pathname\n"));
+ close(fd);
+ exit(-1);
+ }
+
+ strcpy(iocsc.client, path_phys + strlen("/devices"));
+
+ /* Now chop off the trailing ":xxx" portion if present */
+ if ((trailingMinor = strrchr(iocsc.client, ':')) != NULL) {
+ trailingMinor[0] = '\0';
+ }
+
+ if (ioctl(fd, SCSI_VHCI_SWITCH_TO_CNTLR, &iocsc) != 0) {
+ switch (errno) {
+ case EALREADY:
+ err = L_SCSI_VHCI_ALREADY_ACTIVE;
+ break;
+ case ENXIO:
+ err = L_INVALID_PATH;
+ break;
+ case EIO:
+ err = L_SCSI_VHCI_NO_STANDBY;
+ break;
+ case ENOTSUP:
+ err = L_SCSI_VHCI_FAILOVER_NOTSUP;
+ break;
+ case EBUSY:
+ err = L_SCSI_VHCI_FAILOVER_BUSY;
+ break;
+ case EFAULT:
+ default:
+ err = L_SCSI_VHCI_ERROR;
+ }
+ }
+
+ if (err != 0) {
+ close(fd);
+ print_errString(err, path_phys);
+ exit(-1);
+ }
+ }
+
+ close(fd);
+}
+
+/*ARGSUSED*/
+int adm_inquiry(char **a) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+void pho_probe() {
+ terminate();
+}
+
+/*ARGSUSED*/
+void non_encl_probe() {
+ terminate();
+}
+
+/*ARGSUSED*/
+void adm_led(char **a, int b) {
+ terminate();
+}
+
+/*ARGSUSED*/
+void up_password(char **a) {
+ terminate();
+}
+
+/*ARGSUSED*/
+int adm_reserve(char *path) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int adm_release(char *path) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int adm_start(char **a) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int adm_stop(char **a) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int adm_power_off(char **a, int b) {
+ terminate();
+ return (1);
+}
+
+int
+adm_forcelip(char **argv)
+{
+ int path_index = 0, fd;
+ uint64_t wwn;
+ fcio_t fcio;
+ HBA_HANDLE handle;
+ HBA_ADAPTERATTRIBUTES hbaAttrs;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_FCPTARGETMAPPINGV2 *map;
+ HBA_STATUS status;
+ int count, adapterIndex, portIndex, mapIndex;
+ char name[256];
+ int matched, ret = 0, wwnCompare = 0, ntries;
+ char *physical = NULL, *slash_OSDeviceName = NULL;
+
+ if ((status = loadLibrary())) {
+ /* loadLibrary print out error msg */
+ return (ret++);
+ }
+ for (path_index = 0; argv[path_index] != NULL; path_index++) {
+
+ if (is_wwn(argv[path_index])) {
+ (void) sscanf(argv[path_index], "%016llx", &wwn);
+ wwnCompare = 1;
+ } else if (!is_path(argv[path_index])) {
+ print_errString(L_INVALID_PATH, argv[path_index]);
+ ret++;
+ continue;
+ }
+ if (!wwnCompare) {
+ /* Convert the paths to phsyical paths */
+ physical = get_slash_devices_from_osDevName(argv[path_index],
+ STANDARD_DEVNAME_HANDLING);
+ if (!physical) {
+ print_errString(L_INVALID_PATH, argv[path_index]);
+ ret++;
+ continue;
+ }
+ }
+
+ count = getNumberOfAdapters();
+
+ matched = 0;
+ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
+ status = HBA_GetAdapterName(adapterIndex, (char *)&name);
+ if (status != HBA_STATUS_OK) {
+ /* May have been DR'd */
+ continue;
+ }
+ handle = HBA_OpenAdapter(name);
+ if (handle == 0) {
+ /* May have been DR'd */
+ continue;
+ }
+
+ if (getAdapterAttrs(handle, name, &hbaAttrs)) {
+ /* Should never happen */
+ HBA_CloseAdapter(handle);
+ continue;
+ }
+
+ /* Loop over all HBA Ports */
+ for (portIndex = 0; portIndex < hbaAttrs.NumberOfPorts;
+ portIndex++) {
+ if (getAdapterPortAttrs(handle, name, portIndex,
+ &portAttrs)) {
+ continue;
+ }
+
+ matched = 0;
+ if (is_wwn(argv[path_index])) {
+ if (wwn == wwnConversion(
+ portAttrs.NodeWWN.wwn) ||
+ wwn == wwnConversion(
+ portAttrs.PortWWN.wwn)) {
+ matched = 1;
+ }
+ } else {
+ slash_OSDeviceName = get_slash_devices_from_osDevName(
+ portAttrs.OSDeviceName, STANDARD_DEVNAME_HANDLING);
+ if (!slash_OSDeviceName) {
+ continue;
+ } else {
+ if (strncmp(physical, slash_OSDeviceName,
+ strlen(slash_OSDeviceName) -
+ strlen(strrchr(slash_OSDeviceName, ':')))
+ == 0) {
+ matched = 1;
+ }
+ free(slash_OSDeviceName);
+ }
+ }
+
+ if (!matched) {
+ if (!fetch_mappings(handle, portAttrs.PortWWN, &map)) {
+ /*
+ * matchr_mapping checks the arg
+ * so we pass argv here.
+ */
+ mapIndex = match_mappings(argv[path_index], map);
+ if (mapIndex >= 0) {
+ matched = 1;
+ }
+ } else {
+ continue;
+ }
+ }
+
+ if (matched) {
+ if ((fd = open(portAttrs.OSDeviceName,
+ O_RDONLY | O_EXCL)) == -1) {
+ print_errString(L_OPEN_PATH_FAIL,
+ portAttrs.OSDeviceName);
+ return (ret++);
+ }
+
+ fcio.fcio_cmd = FCIO_RESET_LINK;
+ fcio.fcio_xfer = FCIO_XFER_WRITE;
+ /*
+ * Reset the local loop here (fcio_ibuf = 0).
+ * Reset a remote loop on the Fabric by
+ * passing its node wwn (fcio_len = sizeof(nwwn)
+ * and fcio_ibuf = (caddr_t)&nwwn) to the port driver.
+ */
+ (void) memset(&wwn, 0, sizeof (wwn));
+ fcio.fcio_ilen = sizeof (wwn);
+ fcio.fcio_ibuf = (caddr_t)&wwn;
+
+ for (ntries = 0; ntries < RETRY_FCIO_IOCTL; ntries++) {
+ errno = 0;
+ if (ioctl(fd, FCIO_CMD, &fcio) != 0) {
+ /*
+ * When port is offlined, qlc
+ * returns the FC_OFFLINE error and errno
+ * is set to EIO.
+ * We do want to ignore this error,
+ * especially when an enclosure is
+ * removed from the loop.
+ */
+ if (fcio.fcio_errno == FC_OFFLINE)
+ break;
+ if ((errno == EAGAIN) &&
+ (ntries+1 < RETRY_FCIO_IOCTL)) {
+ /* wait WAIT_FCIO_IOCTL */
+ (void) usleep(WAIT_FCIO_IOCTL);
+ continue;
+ }
+ I_DPRINTF("FCIO ioctl failed.\n"
+ "Error: %s. fc_error = %d (0x%x)\n",
+ strerror(errno), fcio.fcio_errno,
+ fcio.fcio_errno);
+ close(fd);
+ print_errString(L_FCIO_FORCE_LIP_FAIL,
+ portAttrs.OSDeviceName);
+ return (ret++);
+ } else {
+ break; /* ioctl succeeds. */
+ }
+ }
+ close(fd);
+ if (ntries == RETRY_FCIO_IOCTL) {
+ print_errString(L_FCIO_FORCE_LIP_FAIL,
+ portAttrs.OSDeviceName);
+ return (ret++);
+ }
+ }
+ if (matched)
+ break; /* for HBA port for loop */
+ }
+ if (matched) /* HBA adapter for loop */
+ break;
+ }
+
+ if (!matched) {
+ print_errString(L_INVALID_PATH, argv[path_index]);
+ ret++;
+ }
+ }
+ HBA_FreeLibrary();
+ return (ret);
+}
+
+/*ARGSUSED*/
+void adm_bypass_enable(char **argv, int bypass_flag) {
+ terminate();
+}
+
+/*ARGSUSED*/
+int adm_port_offline_online(char **a, int b) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+void display_link_status(char **a) {
+ terminate();
+}
+
+/*ARGSUSED*/
+void dump_map(char **argv) {
+ terminate();
+}
+
+/*ARGSUSED*/
+int adm_display_port(int a) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int adm_port_loopback(char *a, int b) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int hotplug_e(int todo, char **argv, int verbose_flag, int force_flag) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int
+setboot(unsigned int yes, unsigned int verbose, char *fname)
+{
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int hotplug(int todo, char **argv, int verbose_flag, int force_flag) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int adm_check_file(char **argv, int flag) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int sysdump(int verbose) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int fcal_update(unsigned int verbose, char *file) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int q_qlgc_update(unsigned int verbose, char *file) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int emulex_update(char *file) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+int emulex_fcode_reader(int fcode_fd, char *pattern, char *pattern_value,
+ uint32_t pattern_value_size) {
+ terminate();
+ return (1);
+}
+
+/*ARGSUSED*/
+void dump(char **argv) {
+ terminate();
+}
+
+/*ARGSUSED*/
+int h_insertSena_fcdev() {
+ terminate();
+ return (1);
+}