OpenSolaris_b135/cmd/luxadm/qlgcupdate.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2009 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 = 0;
	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) {
				sib_node = di_child_node(count_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) {
							devcnt++;
							break;
						}
					}

					sib_node = di_sibling_node(sib_node);
				}
			}
			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) {
			goto try_next;
		}

		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);
			}
		}

	try_next:
		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);
	}
}