/*
* 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.
*/
/*LINTLIBRARY*/
/*
* This module is part of the Fibre Channel Interface library.
*
*/
/*
* I18N message number ranges
* This file: 10500 - 10999
* Shared common messages: 1 - 1999
*/
/* Includes */
#include <stdlib.h>
#include <stdio.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/dklabel.h>
#include <sys/autoconf.h>
#include <sys/utsname.h>
#include <sys/ddi.h> /* for min */
#include <ctype.h> /* for isprint */
#include <sys/scsi/scsi.h>
#include <dirent.h> /* for DIR */
#include <nl_types.h>
#include <locale.h>
#include <thread.h>
#include <synch.h>
#include <l_common.h>
#include <stgcom.h>
#include <l_error.h>
#include <g_state.h>
#include <libdevinfo.h>
/* Defines */
#define BYTES_PER_LINE 16 /* # of bytes to dump per line */
#define SCMD_UNKNOWN 0xff
/* Bus strings - for internal use by g_get_path_type() only */
#define PCI_BUS 1
#define SBUS 2
#define FCOE 3
struct str_type {
char *string;
uint_t type;
};
static struct str_type ValidBusStrings[] = {
{"pci@", PCI_BUS},
{"sbus@", SBUS},
{"fcoe", FCOE},
{NULL, 0}
};
/*
* Strings below were used before cougar driver(qlc) was proposed.
* {"scsi/", FC_PCI_FCA},
* {"fibre-channel/", FC_PCI_FCA},
*/
static struct str_type ValidFCAstrings[] = {
{"SUNW,ifp@", FC4_PCI_FCA | FC4_IFP_XPORT},
{"SUNW,socal@", FC4_SOCAL_FCA},
{NULL, 0}
};
static struct str_type ValidXportStrings[] = {
{"/sf@", FC4_SF_XPORT},
{"/fp@", FC_GEN_XPORT},
{NULL, 0}
};
struct _enclDisk {
char *vid;
char *pid;
};
/*
* SENA/SUNWGS type enclosure disk table. This table contains vendor IDs and
* the non-unique portion of the product identifier sufficient for
* comparison. This table needs to be updated as new drives are supported
* in the SENA/SUNWGS type enclosures that do not have a corresponding match
* in this table. Currently, the v880 and v890 are the only shipping products
* that utilize the SUNWGS type enclosure. SENA is EOL'd. The risk of new
* devices being added that do not match an entry in this table is small but it
* does exist.
*/
static struct _enclDisk enclDiskTbl[] = {
{"SUN", "SENA"},
{"SUN", "SUNWGS"},
{"FUJITSU", "MA"},
{"HITACHI", "DK"},
{"HITACHI", "HU"},
{"SEAGATE", "ST"},
{NULL, NULL}
};
/* i18n */
nl_catd l_catd;
/* Internal Functions */
static void string_dump(char *, uchar_t *, int, int, char msg_string[]);
/*
* Allocate space for and return a pointer to a string
* on the stack. If the string is null, create
* an empty string.
* Use g_destroy_data() to free when no longer used.
*/
char *
g_alloc_string(char *s)
{
char *ns;
if (s == (char *)NULL) {
ns = (char *)g_zalloc(1);
} else {
ns = (char *)g_zalloc(strlen(s) + 1);
if (ns != NULL) {
(void) strncpy(ns, s, (strlen(s) + 1));
}
}
return (ns);
}
/*
* This routine is a wrapper for free.
*/
void
g_destroy_data(void *data)
{
A_DPRINTF(" g_destroy_data: Free\'ed buffer at 0x%x\n",
data);
free((void *)data);
}
/*
* Dump a structure in hexadecimal.
*/
void
g_dump(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;
}
}
/*
* 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';
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 *
g_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);
strcat(source, "/");
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
*
* 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) {
O_DPRINTF("stat() failed for %s- %s\n",
source, strerror(errno));
return (NULL);
}
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 */
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
*
* This function can handle 3 different inputs.
*
* 1) Inputs of the form cN
* This requires the program to search the /dev/rdsk
* directory for a device that is conected to the
* controller with number 'N' and then getting
* the physical pathname of the controller.
* The format of the controller pathname is
* /devices/.../.../SUNW,soc@x,x/SUNW,pln@xxxx,xxxxxxxx:ctlr
* The physical pathname is returned.
*
* 2) Inputs of the form /dev/rdsk/cNtNdNsN
* These are identified by being a link
* The physical path they are linked to is returned.
*
* 3) Inputs of the form /devices/...
* These are actual physical names.
* They are not converted.
*/
char *
g_get_physical_name(char *path)
{
struct stat stbuf;
char s[MAXPATHLEN];
char namebuf[MAXPATHLEN];
char savedir[MAXPATHLEN];
char *result = NULL;
DIR *dirp;
struct dirent *entp;
char *dev_name, *char_ptr;
struct stat sb;
int found_flag = 0;
int status = 0;
int i;
/* return invalid path if path NULL */
if (path == NULL) {
return (NULL);
}
(void) strcpy(s, path);
/*
* See if the form is cN
* Must handle scenaro where there is a file cN in this directory
* Bug ID: 1184633
*
* We could be in the /dev/rdsk directory and the file could be of
* the form cNdNsN (See man disks).
*/
status = stat(s, &stbuf);
if (((status == -1) && (errno == ENOENT)) ||
((s[0] == 'c') && ((int)strlen(s) > 1) && ((int)strlen(s) < 5))) {
/*
* Further qualify cN entry
*/
if ((s[0] != 'c') || ((int)strlen(s) <= 1) ||
((int)strlen(s) >= 5)) {
goto exit;
}
for (i = 1; i < (int)strlen(s); i++) {
if ((s[i] < '0') || (s[i] > '9')) {
goto exit;
}
}
/*
* path does not point to a file or file is of form cN
*/
P_DPRINTF(" g_get_physical_name: "
"Found entry of the form cN n=%s len=%d\n",
&s[1], strlen(s));
dev_name = g_zalloc(sizeof ("/dev/rdsk"));
sprintf((char *)dev_name, "/dev/rdsk");
if ((dirp = opendir(dev_name)) == NULL) {
g_destroy_data(dev_name);
goto exit;
}
while ((entp = readdir(dirp)) != NULL) {
if (strcmp(entp->d_name, ".") == 0 ||
strcmp(entp->d_name, "..") == 0)
continue;
if (entp->d_name[0] != 'c')
/*
* Silently Ignore for now any names
* not stating with c
*/
continue;
sprintf(namebuf, "%s/%s", dev_name, entp->d_name);
if ((lstat(namebuf, &sb)) < 0) {
L_WARNINGS(MSGSTR(55,
"Warning: Cannot stat %s\n"),
namebuf);
continue;
}
if (!S_ISLNK(sb.st_mode)) {
L_WARNINGS(MSGSTR(56,
"Warning: %s is not a symbolic link\n"),
namebuf);
continue;
}
if (strstr(entp->d_name, s) != NULL) {
/*
* found link to device in /devices
*
* Further qualify to be sure I have
* not found entry of the form c10
* when I am searching for c1
*/
if (atoi(&s[1]) == atoi(&entp->d_name[1])) {
P_DPRINTF(" g_get_physical_name: "
"Found entry in /dev/rdsk matching %s: %s\n",
s, entp->d_name);
found_flag = 1;
break;
}
}
}
closedir(dirp);
g_destroy_data(dev_name);
if (found_flag) {
result = g_get_physical_name_from_link(namebuf);
if (result == NULL) {
goto exit;
}
/*
* Convert from device name to controller name
*/
char_ptr = strrchr(result, '/');
*char_ptr = '\0'; /* Terminate sting */
(void) strcat(result, CTLR_POSTFIX);
}
goto exit;
}
if (status == -1)
goto exit;
if (lstat(s, &stbuf) == -1) {
L_WARNINGS(MSGSTR(134,
"%s: lstat() failed - %s\n"),
s, strerror(errno));
goto exit;
}
/*
*/
if (!S_ISLNK(stbuf.st_mode)) {
/*
* Path is not a linked file so must be
* a physical path
*/
if (S_ISCHR(stbuf.st_mode) || S_ISDIR(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] == '.') {
strcat(savedir, &s[1]);
} else {
strcat(savedir, "/");
strcat(savedir, s);
}
result = g_alloc_string(savedir);
}
}
} else {
/*
* Entry is linked file
* so follow link to physical name
*/
result = g_get_physical_name_from_link(path);
}
exit:
return (result);
}
/*
* Function to open a device
*/
int
g_object_open(char *path, int flag)
{
int fd = -1, retry = 0;
if (getenv("_LUX_O_DEBUG") != NULL) {
(void) printf(" Object_open:%s ", path);
if (flag & O_WRONLY) {
(void) printf("O_WRONLY,");
} else if (flag & O_RDWR) {
(void) printf("O_RDWR,");
} else {
(void) printf("O_RDONLY,");
}
if (flag & O_NDELAY) {
(void) printf("O_NDELAY,");
}
if (flag & O_APPEND) {
(void) printf("O_APPEND,");
}
if (flag & O_DSYNC) {
(void) printf("O_DSYNC,");
}
if (flag & O_RSYNC) {
(void) printf("O_RSYNC,");
}
if (flag & O_SYNC) {
(void) printf("O_SYNC,");
}
if (flag & O_NOCTTY) {
(void) printf("O_NOCTTY,");
}
if (flag & O_CREAT) {
(void) printf("O_CREAT,");
}
if (flag & O_EXCL) {
(void) printf("O_EXCL,");
}
if (flag & O_TRUNC) {
(void) printf("O_TRUNC,");
}
(void) printf("\n");
}
/* Open retries introduced due to bugid 4473337 */
errno = 0;
fd = open(path, flag);
while (fd < 0 && retry++ < RETRY_OBJECT_OPEN && (
errno == EBUSY || errno == EAGAIN)) {
O_DPRINTF(" Object_open: Retried:%d %d %s\n",
retry, errno, path);
(void) usleep(WAIT_OBJECT_OPEN);
fd = open(path, flag);
}
if (fd < 0) {
O_DPRINTF(" Object_open: Open failed:%s\n", path);
}
return (fd);
}
/*
* Return a pointer to a string telling us the name of the command.
*/
char *
g_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
*/
void
g_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"),
g_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 */
}
/*
* 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;
}
}
/*
* This routine is a wrapper for malloc. It allocates pre-zeroed space,
* and checks the return value so the caller doesn't have to.
*/
void *
g_zalloc(int count)
{
void *ptr;
ptr = (void *) calloc(1, (unsigned)count);
A_DPRINTF(" g_zalloc: Allocated 0x%x bytes "
"at 0x%x\n", count, ptr);
return (ptr);
}
/*
* Open up the i18n catalog.
* Returns:
* 0 = O.K.
* -1 = Failed (Will revert to default strings)
*/
int
g_i18n_catopen(void)
{
static int fileopen = 0;
static mutex_t mp;
if (setlocale(LC_ALL, "") == NULL) {
(void) fprintf(stderr,
"Cannot operate in the locale requested. "
"Continuing in the default C locale\n");
}
if (mutex_lock(&mp) != 0) {
return (-1);
}
if (!fileopen) {
l_catd = catopen("a5k_g_fc_i18n_cat", NL_CAT_LOCALE);
if (l_catd == (nl_catd)-1) {
(void) mutex_unlock(&mp);
return (-1);
}
fileopen = 1;
}
(void) mutex_unlock(&mp);
return (0);
}
/* Macro used by g_get_path_type() */
#define GetMatch(s_ptr) \
for (found = 0, search_arr_ptr = s_ptr; \
search_arr_ptr->string != NULL; \
search_arr_ptr++) {\
if (strstr(path_ptr, search_arr_ptr->string) != NULL) {\
found = 1;\
break;\
}\
}
/*
* Input : A NULL terminated string
* This string is checked to be an absolute device path
* Output :
* The FCA type and Xport type if found in the path on success
* 0 on Failure
*
* Examples of valid device strings :
*
* Non Fabric FC driver :
* /devices/io-unit@f,e0200000/sbi@0,0/SUNW,socal@1,0/sf@1,0:ctlr
* /devices/io-unit@f,e2200000/sbi@0,0/SUNW,socal@3,0/sf@0,0/ssd@20,0:c,raw
* /devices/sbus@1f,0/SUNW,socal@0,0/sf@0,0:devctl
* /devices/sbus@1f,0/SUNW,socal@2,0/sf@1,0/ssd@w2200002037110cbf,0:b,raw
* /devices/pci@1f,4000/SUNW,ifp@4:devctl
* /devices/pci@1f,4000/SUNW,ifp@2/ssd@w2100002037049ba0,0:c,raw
* /devices/pci@6,4000/pci@2/SUNW,ifp@5/ssd@w210000203708b44f,0:c,raw
*
* Fabric FC driver (fp) :
* - offical device path for Qlogic 2202 with proper FCODE
* as of 12/99.
* /devices/pci@1f,2000/pci@1/SUNW,qlc@5/fp@0,0:devctl
* /devices/pci@e,2000/pci@2/SUNW,qlc@4/fp@0,0:devctl
*
*/
uint_t
g_get_path_type(char *path)
{
uint_t path_type = 0;
int i = 0, pathcnt = 1;
char *path_ptr = path;
struct str_type *search_arr_ptr; /* updated by GetMatch macro */
char found; /* Updated by GetMatch marco */
char drvr_path1[MAXPATHLEN];
mp_pathlist_t pathlist;
int p_on = 0, p_st = 0;
/* Path passed must be an absolute device path */
if (strncmp(path_ptr, DEV_PREFIX, DEV_PREFIX_LEN) ||
(strlen(path_ptr) == DEV_PREFIX_LEN)) {
return (0); /* Invalid path */
}
/* if mpxio device, need to convert from vhci to phci */
if (strstr(path, SCSI_VHCI)) {
(void) strcpy(drvr_path1, path);
if (g_get_pathlist(drvr_path1, &pathlist)) {
return (0);
}
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_path1,
pathlist.path_info[p_on].path_hba);
} else {
/* standby or path0 */
(void) strcpy(drvr_path1,
pathlist.path_info[p_st].path_hba);
}
free(pathlist.path_info);
path_ptr = drvr_path1;
}
GetMatch(ValidBusStrings);
if (found == 0) {
/* No valid bus string - so not a valid path */
return (0);
}
GetMatch(ValidFCAstrings); /* Check for a valid FCA string */
if (found != 0) {
path_type |= search_arr_ptr->type;
}
/*
* continue to check xport even without valid FCA string.
* This is to support 3rd party FCA vendor on Leadville stack.
*/
GetMatch(ValidXportStrings); /* Check for a valid transport str */
if (found == 0) {
return (path_type);
} else {
/*
* if leadville tranport is detected and fca is not set yet,
* set fca flag to generic FC_FCA_MASK.
*/
if ((search_arr_ptr->type == FC_GEN_XPORT) &&
(!(path_type & FC_FCA_MASK))) {
path_type |= FC_FCA_MASK;
}
}
path_type |= search_arr_ptr->type;
/*
* A quick sanity check to make sure that we dont have
* a combination that is not possible
*/
if (((path_type & (FC4_FCA_MASK | FC_XPORT_MASK)) ==
path_type) ||
((path_type & (FC_FCA_MASK | FC4_XPORT_MASK)) ==
path_type)) {
path_type = 0;
}
return (path_type);
}
/*
* g_get_port_path(char *, portlist_t *)
* Purpose: Find all port nexus paths for a particular driver
* Input: portdrvr
* set to name of driver for which to find the paths
* Output: portlist
* allocated structure to hold paths found
* user must call g_free_portlist(portlist_t *) to
* free allocated memory
*/
int
g_get_port_path(char *portdrvr, portlist_t *portlist)
{
di_node_t root;
di_node_t node;
di_minor_t minor_node;
char hbapathfound[MAXPATHLEN];
char *tmppath;
struct stat buf;
/* return invalid argument if *portdrvr or *portlist is NULL */
if ((portdrvr == NULL) || (portlist == NULL)) {
return (L_INVALID_ARG);
}
/* Create a snapshot of the kernel device tree */
root = di_init("/", DINFOCPYALL);
if (root == DI_NODE_NIL) {
return (L_DEV_SNAPSHOT_FAILED);
}
/* point to first node which matches portdrvr */
node = di_drv_first_node(portdrvr, root);
if (node == DI_NODE_NIL) {
/*
* Could not find driver node
*/
(void) di_fini(root);
if (errno == EINVAL)
return (L_PORT_DRIVER_NOT_FOUND);
else
return (L_PHYS_PATH_NOT_FOUND);
}
while (node) {
/* point to first minor node which matches node */
minor_node = di_minor_next(node, DI_MINOR_NIL);
/* if we have a minor node use it */
while (minor_node) {
/*
* Is this a devctl or pseudo node?
* If not, skip it.
* Soc+ HBA port device paths such as:
* /devices/sbus@2,0/SUNW,socal@d,10000:0
* are pseudo nodes as of S9 so we need to
* include those as well.
*/
if (di_minor_nodetype(minor_node) &&
(strcmp(di_minor_nodetype(minor_node),
DDI_NT_NEXUS) &&
strcmp(di_minor_nodetype(minor_node),
DDI_PSEUDO))) {
minor_node = di_minor_next(node, minor_node);
continue;
}
/*
* Prepend '/devices' to path
* Note: The path returned from di_devfs_path
* does NOT begin with '/devices'.
* '/devices' is considered a mount point
*/
strcpy(hbapathfound, "/devices");
tmppath = di_devfs_path(node);
strcat(hbapathfound, tmppath);
(void) free(tmppath);
strcat(hbapathfound, ":");
strcat(hbapathfound, di_minor_name(minor_node));
/*
* Verify that the path is validly constructed
*/
if ((stat(hbapathfound, (struct stat *)&buf)) < 0) {
(void) di_fini(root);
return (L_STAT_ERROR);
}
/* allocate memory and copy constructed path */
if ((portlist->hbacnt > MAX_HBA_PORT - 1) ||
((portlist->physpath[portlist->hbacnt] =
(char *)malloc(MAXPATHLEN)) == NULL)) {
(void) di_fini(root);
return (L_MALLOC_FAILED);
}
strcpy(portlist->physpath[portlist->hbacnt],
hbapathfound);
portlist->hbacnt++;
minor_node = di_minor_next(node, minor_node);
}
node = di_drv_next_node(node);
}
/*
* Destroy the snapshot and return
*/
(void) di_fini(root);
return (0);
}
/*
* Free the allocated portlist structure
*/
void
g_free_portlist(portlist_t *portlist)
{
int x = 0;
/* return if portlist is NULL */
if (portlist == NULL) {
return;
}
for (x = 0; x < portlist->hbacnt; x++) {
if (portlist->physpath[x] != NULL) {
free(portlist->physpath[x]);
}
}
}
/*
* Check VID/PID against enclosure disk table
*/
boolean_t
g_enclDiskChk(char *vid, char *pid)
{
int i;
for (i = 0; enclDiskTbl[i].vid; i++) {
if ((strncmp(vid, enclDiskTbl[i].vid,
strlen(enclDiskTbl[i].vid)) == 0) &&
(strncmp(pid, enclDiskTbl[i].pid,
strlen(enclDiskTbl[i].pid)) == 0)) {
return (B_TRUE);
}
}
return (B_FALSE);
}