/*
* 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
* 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*/
/*
* I18N message number ranges
* This file: 9000 - 9499
* Shared common messages: 1 - 1999
*/
/*
* This module is part of the photon library
*/
/* Includes */
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <dirent.h> /* for DIR */
#include <nl_types.h>
#include <strings.h>
#include <l_common.h>
#include <stgcom.h>
#include <l_error.h>
#include <rom.h>
#include <exec.h>
#include <a_state.h>
#include <a5k.h>
/* Defines */
/* Global variables */
extern uchar_t g_switch_to_alpa[];
extern uchar_t g_sf_alpa_to_switch[];
/* Forward declarations */
static int pwr_up_down(char *, L_state *, int, int, int, int);
static int load_flds_if_enc_disk(char *, struct path_struct **);
static int l_get_node_status(char *, struct l_disk_state_struct *,
int *, WWN_list *, int);
static int check_file(int, int, uchar_t **, int);
static int check_dpm_file(int);
static int ib_download_code_cmd(int, int, int, uchar_t *, int, int);
static int dak_download_code_cmd(int, uchar_t *, int);
static void free_mp_dev_map(struct gfc_map_mp **);
static int get_mp_dev_map(char *, struct gfc_map_mp **, int);
/*
* l_get_mode_pg() - Read all mode pages.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*
* INPUTS:
* path pointer to device path
* pg_buf ptr to mode pages
*
*/
/*ARGSUSED*/
int
{
P_DPRINTF(" l_get_mode_pg: Reading Mode Sense pages.\n");
/* do not do mode sense if this is a tape device */
/* mode sense will rewind the tape */
return (-1);
}
/* open controller */
return (L_OPEN_PATH_FAIL);
/*
* Read the first part of the page to get the page size
*/
size = 20;
return (L_MALLOC_FAILED);
}
/* read page */
0, MODEPAGE_ALLPAGES)) {
(void) g_destroy_data((char *)*pg_buf);
return (status);
}
/* Now get the size for all pages */
(void) g_destroy_data((char *)*pg_buf);
return (L_MALLOC_FAILED);
}
/* read all pages */
0, MODEPAGE_ALLPAGES)) {
(void) g_destroy_data((char *)*pg_buf);
return (status);
}
return (0);
}
/*
* Format QLA21xx status
*
* INPUTS: message buffer
* Count
* status
*
* OUTPUT: Message of this format in message buffer
* "status type: 0xstatus count"
*/
int
{
if (status_msg_buf == NULL) {
return (0);
}
switch (status) {
case IFP_CMD_CMPLT:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_INCOMPLETE:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_DMA_DERR:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_TRAN_ERR:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_RESET:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_ABORTED:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_TIMEOUT:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_DATA_OVR:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_ABORT_REJECTED:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_RESET_REJECTED:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_DATA_UNDER:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_QUEUE_FULL:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_PORT_UNAVAIL:
(void) sprintf(status_msg_buf,
break;
case IFP_CMD_PORT_LOGGED_OUT:
(void) sprintf(status_msg_buf,
break;
/* Not enough packets for given request */
(void) sprintf(status_msg_buf,
break;
default:
(void) sprintf(status_msg_buf,
"%s 0x%-2x"
} /* End of switch() */
return (0);
}
/*
* Format Fibre Channel status
*
* INPUTS: message buffer
* Count
* status
*
* OUTPUT: Message of this format in message buffer
* "status type: 0xstatus count"
*/
int
{
if (status_msg_buf == NULL) {
return (0);
}
switch (status) {
case FCAL_STATUS_OK:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_P_RJT:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_F_RJT:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_P_BSY:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_F_BSY:
(void) sprintf(status_msg_buf,
break;
/* Should not happen. */
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_ERR_OFFLINE:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_TIMEOUT:
/* Should not happen. */
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_ERR_OVERRUN:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_LOOP_ONLINE:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_OLD_PORT:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_AL_PORT:
(void) sprintf(status_msg_buf,
break;
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_BAD_SEG_CNT:
(void) sprintf(status_msg_buf,
break;
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_BAD_XID:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_XCHG_BUSY:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_BAD_POOL_ID:
(void) sprintf(status_msg_buf,
break;
/* Not enough packets for given request */
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_ALLOC_FAIL:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_BAD_SID:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_NO_SEQ_INIT:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_BAD_DID:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_ABORTED:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_ABORT_FAILED:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_DIAG_BUSY:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_DIAG_INVALID:
(void) sprintf(status_msg_buf,
break;
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_CRC_ERR:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_OPEN_FAIL:
(void) sprintf(status_msg_buf,
break;
case FCAL_STATUS_ERROR:
(void) sprintf(status_msg_buf,
break;
(void) sprintf(status_msg_buf,
break;
default:
(void) sprintf(status_msg_buf,
"%s 0x%-2x"
} /* End of switch() */
return (0);
}
/*
* Get the indexes to the disk device elements in page 2,
* based on the locations found in page 1.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
int *rear_index)
{
int i, rear_flag = 0;
(rear_index == NULL)) {
return (L_INVALID_PATH_FORMAT);
}
*front_index = *rear_index = 0;
/* Get the indexes to the disk device elements */
if (front_flag) {
local_rear = index;
rear_flag = 1;
break;
} else {
local_front = index;
front_flag = 1;
}
}
index++; /* for global element */
}
D_DPRINTF(" l_get_disk_element_index:"
" Index to front disk elements 0x%x\n"
" l_get_disk_element_index:"
" Index to rear disk elements 0x%x\n",
return (L_RD_NO_DISK_ELEM);
}
*rear_index = local_rear;
return (0);
}
/*
* l_led() manage the device led's
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
struct device_element *status,
int verbose)
{
unsigned short page_len;
int enc_type;
return (L_INVALID_PATH_FORMAT);
}
/*
*
* The path_struct will return a valid slot
* and the IB path or a disk path.
*/
if (!path_struct->ib_path_flag) {
return (err);
return (err);
}
} else {
}
return (L_MALLOC_FAILED);
}
if (!path_struct->slot_valid) {
(void) l_free_lstate(&l_state);
return (err);
}
(void) l_free_lstate(&l_state);
return (err);
}
(void) l_free_lstate(&l_state);
return (err);
}
/* We are passing the disks path */
(void) l_free_lstate(&l_state);
return (err);
}
}
MAX_REC_DIAG_LENGTH)) == NULL) {
(void) l_free_lstate(&l_state);
return (L_MALLOC_FAILED);
}
(void) l_free_lstate(&l_state);
(void) g_destroy_data(page_buf);
return (L_OPEN_PATH_FAIL);
}
(void) l_free_lstate(&l_state);
(void) g_destroy_data(page_buf);
return (err);
}
/* Get index to the disk we are interested in */
(void) l_free_lstate(&l_state);
(void) g_destroy_data(page_buf);
return (err);
}
/* find enclosure type */
strlen(DAK_OFF_NAME)) == 0) ||
strlen(DAK_PROD_STR)) == 0)) {
} else {
}
/* Double check slot. */
(void) l_free_lstate(&l_state);
return (L_INVALID_SLOT);
}
&rear_index)) {
(void) l_free_lstate(&l_state);
return (err);
}
/* Skip global element */
front_index++;
if (enc_type == DAK_ENC_TYPE) {
} else {
rear_index++;
}
if (path_struct->f_flag) {
} else {
}
/*
* now do requested action.
*/
sizeof (struct device_element)); /* save status */
write = 1;
switch (led_action) {
case L_LED_STATUS:
write = 0;
break;
case L_LED_RQST_IDENTIFY:
elem->ident = 1;
if (verbose) {
if (enc_type == DAK_ENC_TYPE) {
} else {
}
}
break;
case L_LED_OFF:
if (verbose) {
if (enc_type == DAK_ENC_TYPE) {
MSGSTR(9044,
" Turning off LED for slot %d in enclosure"
} else {
MSGSTR(9044,
" Turning off LED for slot %d in enclosure"
}
}
break;
default:
(void) l_free_lstate(&l_state);
return (L_INVALID_LED_RQST);
} /* End of switch */
if (write) {
g_dump(" l_led: Updating led state: "
"Device Status Element ",
HEX_ONLY);
}
(void) g_destroy_data(page_buf);
(void) l_free_lstate(&l_state);
return (err);
}
(void) g_destroy_data(page_buf);
(void) l_free_lstate(&l_state);
return (err);
}
sizeof (struct device_element));
}
g_dump(" l_led: Device Status Element ",
HEX_ONLY);
}
(void) l_free_lstate(&l_state);
(void) g_destroy_data(page_buf);
return (0);
}
/*
* frees the previously alloced l_state
* structure.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
{
int i;
return (0);
(void) g_free_multipath(
(void) g_free_multipath(
}
(void) g_destroy_data (*l_state);
return (0);
}
/*
* Set the state of an individual disk
* in the Photon enclosure the powered
* a disk or the ib_path_flag must be set.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
/*ARGSUSED*/
{
if (path_struct == NULL) {
return (L_INVALID_PATH_FORMAT);
}
return (err);
return (err);
}
/*
* Check to see if we have a photon, and if not, don't allow
* this operation
*/
return (err);
}
return (L_ENCL_INVALID_PATH);
}
/*
* OK, so we have a photon... we can continue
*/
return (L_MALLOC_FAILED);
}
(void) l_free_lstate(&l_state);
return (err);
}
if (!path_struct->slot_valid) {
/* We are passing the disks path */
(void) l_free_lstate(&l_state);
return (err);
}
}
/*
* Either front or rear drive
*/
if (path_struct->f_flag) {
} else {
}
/*
* Check for drive presence always
*/
(void) l_free_lstate(&l_state);
return (L_SLOT_EMPTY);
}
/*
* Check disk state
* before the power off.
*
*/
if (power_off_flag && !force_flag) {
goto pre_pwr_dwn;
} else {
goto pwr_up_dwn;
}
/*
* Check whether disk
* is reserved by another
* host
*/
L_RESERVED)) {
(void) l_free_lstate(&l_state);
return (L_DEVICE_RESERVED);
}
(void) l_free_lstate(&l_state);
return (L_MALLOC_FAILED);
}
/*
* NOTE: It is not necessary to get the multipath list here as ------
* we alread have it after getting the status earlier.
* - REWRITE -
*/
/*
* Get path to all the FC disk and tape devices.
*
* I get this now and pass down for performance
* reasons.
* If for some reason the list can become invalid,
* i.e. device being offlined, then the list
* must be re-gotten.
*/
(void) g_destroy_data(dl);
(void) l_free_lstate(&l_state);
return (err); /* Failure */
}
(void) g_destroy_data(dl);
(void) g_free_wwn_list(&wwn_list);
(void) l_free_lstate(&l_state);
return (err);
}
ER_DPRINTF("%s could not acquire"
" the device: %s\n\n",
continue;
}
}
if (devctl_device_offline(devhdl) != 0) {
(void) devctl_release(devhdl);
(void) g_destroy_data(dl);
(void) g_free_wwn_list(&wwn_list);
(void) l_free_lstate(&l_state);
return (L_POWER_OFF_FAIL_BUSY);
}
(void) devctl_release(devhdl);
}
(void) g_destroy_data(dl);
}
(void) g_free_wwn_list(&wwn_list);
(void) l_free_lstate(&l_state);
if (err) {
return (err);
}
return (0);
}
/*
* l_pho_pwr_up_down() Set the state of the Photon enclosure
* The path must point to an IB.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
int verbose, int force_flag)
{
int i, err = 0;
return (L_INVALID_PATH_FORMAT);
}
return (L_MALLOC_FAILED);
}
(void) l_free_lstate(&l_state);
return (err);
}
if (power_off_flag && !force_flag) {
goto pre_pwr_dwn;
} else {
goto pwr_up_dwn;
}
/*
* Check if any disk in this enclosure
* is reserved by another host before
* the power off.
*/
L_RESERVED) ||
L_RESERVED) ||
L_RESERVED) ||
L_RESERVED)) {
return (L_DISKS_RESERVED);
}
}
/*
* Check if any disk in this enclosure
* Get path to all the FC disk and tape devices.
*
* I get this now and pass down for performance
* reasons.
* If for some reason the list can become invalid,
* i.e. device being offlined, then the list
* must be re-gotten.
*/
(void) l_free_lstate(&l_state);
return (err); /* Failure */
}
(void) g_free_wwn_list(&wwn_list);
(void) l_free_lstate(&l_state);
return (L_MALLOC_FAILED);
}
(void) g_destroy_data(dl);
continue;
}
/* attempt to acquire the device */
if ((devhdl = devctl_device_acquire(
ER_DPRINTF("%s: Could not "
"acquire the device: %s\n\n",
continue;
}
}
/* attempt to offline the device */
if (devctl_device_offline(devhdl) != 0) {
(void) devctl_release(devhdl);
(void) g_free_multipath(
(void) g_destroy_data(dl);
(void) g_free_wwn_list(&wwn_list);
(void) l_free_lstate(&l_state);
return (L_POWER_OFF_FAIL_BUSY);
}
/* release handle acquired above */
(void) devctl_release(devhdl);
}
(void) g_destroy_data(dl);
}
(void) g_free_wwn_list(&wwn_list);
(void) l_free_lstate(&l_state);
return (L_MALLOC_FAILED);
}
(void) g_destroy_data(dl);
continue;
}
/* attempt to acquire the device */
if ((devhdl = devctl_device_acquire(
ER_DPRINTF("%s: Could not "
"acquire the device: %s\n\n",
continue;
}
}
/* attempt to offline the device */
if (devctl_device_offline(devhdl) != 0) {
(void) devctl_release(devhdl);
(void) g_free_multipath(
(void) g_destroy_data(dl);
(void) g_free_wwn_list(&wwn_list);
(void) l_free_lstate(&l_state);
return (L_POWER_OFF_FAIL_BUSY);
}
/* release handle acquired above */
(void) devctl_release(devhdl);
}
(void) g_destroy_data(dl);
}
}
(void) g_free_wwn_list(&wwn_list);
power_off_flag, verbose)) != 0) {
(void) l_free_lstate(&l_state);
return (err);
}
(void) l_free_lstate(&l_state);
return (0);
}
/*
* Set the state of the Photon enclosure or disk
* The path must point to an IB.
* slot == -1 implies entire enclosure.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
static int
int power_off_flag, int verbose)
{
unsigned short page_len;
return (L_OPEN_PATH_FAIL);
}
/* Verify it is a Photon */
return (status);
}
return (L_ENCL_INVALID_PATH);
}
/*
* bit in the global device control element.
*/
return (L_MALLOC_FAILED);
}
(void) g_destroy_data(page_buf);
return (err);
}
/* Double check slot as convert_name only does gross check */
(void) g_destroy_data(page_buf);
return (L_INVALID_SLOT);
}
&rear_index)) {
(void) g_destroy_data(page_buf);
return (err);
}
/* Skip global element */
front_index++;
rear_index++;
/*
* now do requested action.
*/
}
/* Now do rear */
}
g_dump(" pwr_up_down: "
"Front Device Status Element ",
(uchar_t *)front_elem,
sizeof (struct device_element),
HEX_ONLY);
}
g_dump(" pwr_up_down: "
"Rear Device Status Element ",
sizeof (struct device_element),
HEX_ONLY);
}
}
(void) g_destroy_data(page_buf);
return (err);
}
(void) g_destroy_data(page_buf);
return (0);
}
/*
* Set the password of the FPM by sending the password
* in page 4 of the Send Diagnostic command.
*
* The path must point to an IB.
*
* The size of the password string must be <= 8 bytes.
* The string can also be NULL. This is the way the user
* chooses to not have a password.
*
* I then tell the photon by giving him 4 NULL bytes.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
{
return (L_OPEN_PATH_FAIL);
}
/* Verify it is a Photon */
return (status);
}
return (L_ENCL_INVALID_PATH);
}
/* Double check */
return (L_INVALID_PASSWORD_LEN);
}
return (status);
}
return (0);
}
/*
* Set the name of the enclosure by sending the name
* in page 4 of the Send Diagnostic command.
*
* The path must point to an IB.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
{
return (L_INVALID_PATH_FORMAT);
}
return (L_OPEN_PATH_FAIL);
}
/* Verify it is a Photon */
return (status);
}
return (L_ENCL_INVALID_PATH);
}
sizeof (page4))) {
return (status);
}
/*
* Check the name really changed.
*/
return (status);
}
sizeof (inq.inq_box_name));
return (L_ENCL_NAME_CHANGE_FAIL);
}
return (0);
}
/*
* Issue a Loop Port enable Primitive sequence
* to the device specified by the pathname.
*/
int
/*ARGSUSED*/
{
return (0);
}
/*
* Issue a Loop Port Bypass Primitive sequence
* to the device specified by the pathname. This requests the
* device to set its L_Port into the bypass mode.
*/
int
/*ARGSUSED*/
{
return (0);
}
/*
* Create a linked list of all the Photon enclosures that
* are attached to this host.
*
* RETURN VALUES: 0 O.K.
*
* box_list pointer:
* NULL: No enclosures found.
* !NULL: Enclosures found
* box_list points to a linked list of boxes.
*/
int
{
char *dev_name;
int al_pa;
if (box_list_ptr == NULL) {
return (L_INVALID_PATH_FORMAT);
}
return (L_MALLOC_FAILED);
}
if (verbose) {
MSGSTR(9045,
" Searching directory %s for links to enclosures\n"),
dev_name);
}
(void) g_destroy_data(dev_name);
/* No Photons found */
B_DPRINTF(" l_get_box_list: No Photons found\n");
return (0);
}
continue;
ER_DPRINTF("Warning: Cannot stat %s\n",
namebuf);
continue;
}
ER_DPRINTF("Warning: %s is not a symbolic link\n",
namebuf);
continue;
}
ER_DPRINTF(" Warning: Get physical name from"
" link failed. Link=%s\n", namebuf);
continue;
}
/* Found a SES card. */
B_DPRINTF(" l_get_box_list: Link to SES Card found: %s/%s\n",
continue; /* Ignore errors */
}
/* Get the box name */
continue; /* Ignore errors */
}
/*
*/
/* Get the port WWN from the IB, page 1 */
(void) g_destroy_data(dev_name);
return (status);
}
/*
* Build list of names.
*/
if ((l2 = (struct box_list_struct *)
g_zalloc(sizeof (struct box_list_struct)))
== NULL) {
return (L_MALLOC_FAILED);
}
/* Fill in structure */
(char *)result);
(char *)namebuf);
"%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
page1.enc_node_wwn[0],
(char *)inq.inq_box_name,
sizeof (inq.inq_box_name));
/* make sure null terminated */
/*
* Now get the port WWN for the port
* we are connected to.
*/
if (status == 0) {
"%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
B_DPRINTF(" l_get_box_list:"
} else {
}
} else {
(void) g_destroy_data(dev_name);
(void) g_destroy_data(l2);
return (status);
}
}
}
(void) g_destroy_data(dev_name);
return (0);
}
void
{
return;
}
(void) g_destroy_data(*box_list);
}
}
/*
* Finds out if there are any other boxes
* with the same name as "name".
*
* RETURNS:
* 0 There are no other boxes with the same name.
* >0 if duplicate names found
*/
/*ARGSUSED*/
int
{
int dup_flag = 0;
return (0);
while (box_list_ptr != NULL) {
dup_flag++;
break;
}
}
return (dup_flag);
}
/*
* Checks for a name conflict with an SSA cN type name.
*/
int
{
char s[MAXPATHLEN];
char *p = NULL;
return (0);
}
(void) g_destroy_data(*result);
return (0);
}
P_DPRINTF(" l_get_conflict: Found "
"SSA path using %s\n", s);
/* Find path to IB */
return (err); /* Failure */
}
/*
* Valid cN type name found.
*/
if ((strcmp((char *)s,
found_box = 1;
if (p == NULL) {
+ 2)) == NULL) {
(void) l_free_box_list(&box_list);
return (errno);
}
} else {
+ strlen(p)
+ 2)) == NULL) {
(void) l_free_box_list(&box_list);
return (errno);
}
(void) g_destroy_data(p);
p = pp;
}
(void) strcat(p, "\n");
}
}
if (found_box) {
D_DPRINTF("There is a conflict between the "
"enclosure\nwith this name, %s, "
"and a SSA name of the same form.\n"
"Please use one of the following physical "
"pathnames:\n%s\n%s\n",
s, *result, p);
(void) l_free_box_list(&box_list);
(void) g_destroy_data(p);
return (L_SSA_CONFLICT); /* failure */
}
(void) l_free_box_list(&box_list);
return (0);
}
/*
* This function sets the "slot", "slot_valid" and "f_flag" fields of the
* path_struct that is passed in IFF the device path passed in ("phys_path")
* is a disk in an A5K or a Daktari. This is achieved by calling l_get_slot().
*
* INPUT :
* phys_path - physical path to a device
* path_sturct - Pointer to pointer to a path_struct data structure
*
* OUTPUT :
* path_struct->slot is set to the slot position in enclosure
* path_struct->slot_valid is set to 1
* path_struct->f_flag is set to 1 if in the front of an A5k
* or if among the first 6 disks on a Daktari
* else
* they are left as they were
* RETURNS:
* 0 on SUCCESS
* non-zero otherwise
*/
static int
{
return (L_INVALID_PATH_FORMAT);
}
(g_get_path_type(phys_path) == 0)) {
/*
* Don't proceed when not a disk device or if it is not a
* valid FC device on which g_get_dev_map() can be done
* (for example, g_get_dev_map() will fail on SSAs).
*
* Just return success
*/
return (0);
}
if ((*path_struct)->ib_path_flag) {
/*
* If this flag is set, l_get_slot() should not be called
* So, no point in proceeding. Just return success.
*/
return (0);
}
return (err);
}
if (err == L_NO_SES_PATH) {
/*
* This is not an error since this could be a device
* which does not have SES nodes
*/
return (0);
}
return (err);
}
/*
* There is a SES path on the same FCA as the given disk. But if the
*/
return (err);
}
/*
* only want to continue if this is a photon or a Daktari
*
* if product ID is not SENA or VID is not "SUN" (checks for photon)
* and if enclosure type is not a Daktari, then I return
*/
return (0);
}
/* Now, set some fields that l_get_slot() uses and then call it */
return (L_MALLOC_FAILED);
}
(void) l_free_lstate(&l_state);
return (err);
}
(void) l_free_lstate(&l_state);
return (err);
}
(void) l_free_lstate(&l_state);
return (0);
}
/*
* convert box name or WWN or logical path to physical path.
*
* OUTPUT:
* path_struct:
* - This structure is used to return more detailed
* information about the path.
* - *p_physical_path
* Normally this is the requested physical path.
* If the requested path is not found then iff the
* ib_path_flag is set this is the IB path.
* - *argv
* This is the argument variable input. e.g. Bob,f1
* - slot_valid
* - slot
* This is the slot number that was entered when using
* the box,[fr]slot format. It is only valid if the
* slot_valid flag is set.
* - f_flag
* Front flag - If set, the requested device is located in the
* front of the enclosure.
* - ib_path_flag
* If this flag is set it means a devices path was requested
* but could not be found but an IB's path was found and
* the p_physical_path points to that path.
* - **phys_path
* physical path to the device.
* RETURNS:
* - 0 if O.K.
* - error otherwise.
*/
int
{
(path_struct == NULL)) {
return (L_INVALID_PATH_FORMAT);
}
start_time = gethrtime();
}
return (L_MALLOC_FAILED);
}
/*
* If the path contains a "/" then assume
* it is a logical or physical path as the
* box name or wwn can not contain "/"s.
*/
return (L_NO_PHYS_PATH);
}
/*
* Make sure it's a disk or tape path
*/
return (L_SCSI_ERROR);
}
/*
* Check to see if it is not a
*
*/
return (0);
}
}
return (err);
}
goto done;
}
if ((tmp_name[0] == 'c') &&
(void) g_destroy_data(result);
}
return (err);
}
return (L_SCSI_ERROR);
}
/*
* Check to see if it is a supported
*/
if (err = load_flds_if_enc_disk(
result, path_struct)) {
return (err);
}
}
goto done;
}
}
/*
* Check to see if we have a box or WWN name.
*
* If it contains a , then the format must be
* box_name,f1 where f is front and 1 is the slot number
* or it is a format like
* ssd@w2200002037049adf,0:h,raw
* or
* SUNW,pln@a0000000,77791d:ctlr
*/
char_ptr++; /* point to f/r */
} else if (*char_ptr != 'r') {
return (L_INVALID_PATH_FORMAT);
}
char_ptr++;
/*
* NOTE: Need to double check the slot when we get
* the number of the devices actually in the box.
*/
return (L_INVALID_SLOT);
}
/* Say slot valid. */
} else
}
found_comma = 1;
}
/* Find path to IB */
(void) l_free_box_list(&box_list);
return (err);
}
/* Look for box name. */
result =
L_DPRINTF(" l_convert_name:"
" Found subsystem: name %s WWN %s\n",
/*
* Check for another box with this name.
*/
verbose)) {
(void) l_free_box_list(&box_list_ptr);
(void) g_destroy_data(result);
return (L_DUPLICATE_ENCLOSURES);
}
found_box = 1;
break;
}
}
/*
* Check to see if we must get individual disks path.
*/
(void) g_destroy_data(result);
(void) l_free_box_list(&box_list_ptr);
return (L_MALLOC_FAILED);
}
verbose)) != 0) {
(void) g_destroy_data(result);
(void) g_destroy_data(l_state);
(void) l_free_box_list(&box_list_ptr);
return (err);
}
/*
* Now double check the slot number.
*/
path_ptr->slot_valid = 0;
(void) g_destroy_data(result);
(void) l_free_box_list(&box_list_ptr);
(void) l_free_lstate(&l_state);
return (L_INVALID_SLOT);
}
/* Only allow the single slot version for Daktari */
return (L_SCSI_ERROR);
}
path_ptr->slot_valid = 0;
(void) g_destroy_data(result);
(void) l_free_box_list(&box_list_ptr);
(void) l_free_lstate(&l_state);
return (L_INVALID_SLOT);
}
result =
} else {
/* Result is the IB path */
(void) g_destroy_data(result);
}
} else {
result =
} else {
/* Result is the IB path */
(void) g_destroy_data(result);
}
}
(void) l_free_lstate(&l_state);
goto done;
}
if (found_box || found_comma) {
goto done;
}
/*
* No luck with the box name.
*
* Try WWN's
*/
/* Look for the SES's WWN */
if (((strcasecmp((char *)tmp_name,
(char *)box_list->b_port_wwn_s)) == 0) ||
((strcasecmp((char *)tmp_name,
(char *)box_list->b_node_wwn_s)) == 0)) {
result =
L_DPRINTF(" l_convert_name:"
" Found subsystem using the WWN"
": name %s WWN %s\n",
goto done;
}
}
/* Look for a device's WWN */
(void) l_free_box_list(&box_list_ptr);
return (err);
}
if (((strcasecmp((char *)tmp_name,
(char *)wwn_list_ptr->node_wwn_s)) == 0) ||
((strcasecmp((char *)tmp_name,
(char *)wwn_list_ptr->port_wwn_s)) == 0)) {
/*
* Found the device's WWN in the global WWN list.
* additional fields in path_struct.
*/
L_DPRINTF(" l_convert_name:"
" Found device: WWN %s Path %s\n",
(void) g_free_wwn_list(&wwn_list);
/*
* Now check if it is a disk in an A5K and set
* path_struct fields
*/
return (L_SCSI_ERROR);
}
/*
* Check to see if it is a supported
*/
if (err = load_flds_if_enc_disk(
result, path_struct)) {
return (err);
}
}
goto done;
}
}
}
/*
* Try again in case we were in the /dev
* or /devices directory.
*/
done:
(void) l_free_box_list(&box_list_ptr);
if (!path_ptr->ib_path_flag)
return (-1);
} else {
}
L_DPRINTF(" l_convert_name: path_struct:\n\tphysical_path:\n\t %s\n"
"\targv:\t\t%s"
"\n\tslot_valid\t%d"
"\n\tslot\t\t%d"
"\n\tf_flag\t\t%d"
"\n\tib_path_flag\t%d\n",
"Time = %lld millisec\n",
}
if (path_ptr->ib_path_flag)
return (-1);
return (0);
}
/*
* Gets envsen information of an enclosure from IB
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
int verbose)
{
return (L_INVALID_BUF_LEN);
}
if (verbose) {
}
sizeof (struct rec_diag_hdr), page_code)) {
return (status);
}
/* Check */
return (L_RD_PG_INVLD_CODE);
}
/*
* Because of a hardware restriction in the soc+ chip
* the transfers must be word aligned.
*/
while (size & 0x03) {
size++;
return (L_RD_PG_MIN_BUFF);
}
P_DPRINTF(" l_get_envsen_page: Adjusting size of the "
"g_scsi_rec_diag_cmd buffer.\n");
}
return (L_MALLOC_FAILED);
}
P_DPRINTF(" l_get_envsen_page: Reading page %x of size 0x%x\n",
(void) g_destroy_data((char *)pg);
return (status);
}
(void) g_destroy_data(pg);
return (0);
}
/*
* Get consolidated copy of all environmental information
* into buf structure.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
{
return (L_INVALID_PATH_FORMAT);
}
/* open IB */
return (L_OPEN_PATH_FAIL);
P_DPRINTF(" l_get_envsen: Getting list of supported"
" pages from IB\n");
if (verbose) {
}
/* Get page 0 */
return (rval);
}
/*
* check whether the number of pages received
* from IB are valid. SENA enclosure
* supports only 8 pages of sense information.
* According to SES specification dpANS X3.xxx-1997
* pages can go upto L_MAX_POSSIBLE_PAGES (0xFF).
* Return an error if no. of pages exceeds L_MAX_POSSIBLE_PAGES.
* See if (num_pages >= L_MAX_POSSIBLE_PAGES) since 1 page (page 0)
* was already subtracted from the total number of pages before.
*/
return (L_INVALID_NO_OF_ENVSEN_PAGES);
}
/*
* Buffer size of MAX_REC_DIAG_LENGTH can be small if the
* number of pages exceed more than L_MAX_SENAIB_PAGES
* but less than L_MAX_POSSIBLE_PAGES.
*/
if (size == MAX_REC_DIAG_LENGTH &&
num_pages >= L_MAX_SENAIB_PAGES) {
return (L_INVALID_BUF_LEN);
}
/* Align buffer */
}
/*
* Getting all pages and appending to buf
*/
for (; num_pages--; page_list_ptr++) {
/*
* The fifth byte of page 0 is the start
* of the list of pages not including page 0.
*/
return (rval);
}
}
return (0);
}
/*
* Get the individual disk status.
* Path must be physical and point to a disk.
*
* This function updates the d_state_flags, port WWN's
* and num_blocks for all accessiable ports
* in l_disk_state->g_disk_state structure.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
{
int i = 0;
return (L_INVALID_PATH_FORMAT);
}
/* Check device name */
return (L_INVALID_PATH);
}
/* Initialize */
/* Get paths. */
G_DPRINTF(" l_get_disk_status: Error finding a "
"multipath to the disk.\n");
return (0);
}
/*
* It is an MPXIO Path
*/
return (0);
}
for (i = 0; i < pathcnt; i++) {
/*
* Skip inactive paths.
* A path that is not in either
* MDI_PATHINFO_STATE_ONLINE or
* MDI_PATHINFO_STATE_STANDBY state is not
* an active path.
*
* When a disk port is bypassed and mpxio is
* enabled, the path_state for that path goes to the
* offline state
*/
continue;
}
if (!(path_a_found || path_b_found)) {
local_port_a_flag = 1;
} else {
local_port_a_flag = 0;
}
} else if (path_a_found &&
/* do port b */
local_port_a_flag = 0;
} else if (path_b_found &&
/* do port a */
local_port_a_flag = 1;
}
return (err);
}
if (local_port_a_flag && (!path_a_found)) {
(void) strcpy(l_disk_state->
path_a_found++;
}
if ((!local_port_a_flag) && (!path_b_found)) {
(void) strcpy(l_disk_state->
path_b_found++;
}
}
return (0);
}
(void) g_free_multipath(ml);
return (err);
}
(void) g_free_multipath(ml);
return (err);
}
/*
* Get the port, A or B, of the disk,
* by passing the IB path.
*/
(void) g_free_multipath(ml);
return (err);
}
if (local_port_a_flag && (!path_a_found)) {
G_DPRINTF(" l_get_disk_status: Path to Port A "
(void) g_free_multipath(ml);
return (err);
}
(void) g_free_multipath(ml);
return (err);
}
"%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
path_a_found++;
}
if ((!local_port_a_flag) && (!path_b_found)) {
G_DPRINTF(" l_get_disk_status: Path to Port B "
return (err);
}
(void) g_free_multipath(ml);
return (err);
}
"%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
path_b_found++;
}
}
return (0);
}
/*
* Check for Persistent Reservations.
*/
int
int verbose)
{
int status;
ACTION_READ_KEYS))) {
return (status);
}
/* This means persistent reservations are supported by the disk. */
if (read_key_buf.rk_length) {
}
(void) memset(&read_reserv_buf, 0,
sizeof (struct read_reserv_struct));
(uchar_t *)&read_reserv_buf,
sizeof (struct read_reserv_struct),
ACTION_READ_RESERV))) {
return (status);
}
if (read_reserv_buf.rr_length) {
}
if (verbose) {
"Reservations:"));
} else {
}
} else {
MSGSTR(87,
"Not being used"));
}
}
return (0);
}
/*
* Gets the disk status and
* updates the l_disk_state_struct structure.
* Checks for open fail, Reservation Conflicts,
* Not Ready and so on.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
int port_a_flag, int verbose)
{
return (L_INVALID_PATH_FORMAT);
}
/*
* Try to open drive.
*/
G_DPRINTF(" l_get_disk_port_status: Error "
"opening drive %s\n", path);
} else {
/* See if drive ready */
if ((status & L_SCSI_ERROR) &&
/*
* TBD
* This is where I should figure out
* if the device is Not Ready or whatever.
*/
} else if ((status & L_SCSI_ERROR) &&
((status & ~L_SCSI_ERROR) ==
/* mark reserved */
} else {
}
/*
* There may not be a label on the drive - check
*/
/*
* Sanity-check the vtoc
*/
G_DPRINTF(" l_get_disk_port_status: "
"Checking vtoc - No Label found.\n");
}
I_DPRINTF("\t- DKIOCGVTOC ioctl failed: "
" invalid geometry\n");
}
}
}
/*
* Need an extra check for tape devices
* read capacity should not be run on tape devices.
* It will always return Not Readable
*/
sizeof (capacity))) {
G_DPRINTF(" l_get_disk_port_status: "
"Read Capacity failed.\n");
if (status & L_SCSI_ERROR) {
if ((status & ~L_SCSI_ERROR) ==
/* mark reserved */
} else
/* mark bad */
} else {
/*
* TBD
* Need a more complete state definition here.
*/
return (0);
}
} else {
/* save capacity */
}
}
G_DPRINTF(" l_get_disk_port_status: Individual Disk"
" Status: 0x%x for"
" port %s for path:"
" %s\n", local_state,
return (0);
}
/*
* Copy and format page 1 from big buffer to state structure.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
static int
{
int size, i;
/* Sanity check. */
return (L_REC_DIAG_PG1);
}
(encl_ptr->enc_num_elem == 0)) {
return (L_REC_DIAG_PG1);
}
/*
* Copy Type Descriptors seperately to get aligned.
*/
/*
* Copy Text Descriptors seperately to get aligned.
*
* Must use the text size from the Type Descriptors.
*/
}
return (0);
}
/*
* Copy page 7 (Element Descriptor page) to state structure.
* Copy header then copy each element descriptor
* seperately.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
static void
{
size = HEADER_LEN +
g_dump(" copy_page_7: Page 7 header: ",
" copy_page_7: Elements being stored "
"in state table\n"
" ");
}
/* I am assuming page 1 has been read. */
for (j = 0, p7_index = 0;
/* Copy global element */
size = HEADER_LEN +
bcopy((void *)(my_from_ptr),
my_from_ptr += size;
k++) {
/* Copy individual elements */
size = HEADER_LEN +
*(my_from_ptr + 3));
bcopy((void *)(my_from_ptr),
my_from_ptr += size;
D_DPRINTF(".");
}
}
D_DPRINTF("\n");
}
/*
* Gets IB diagnostic pages on a given pathname from l_get_envsen().
* It also fills up the individual device element of l_state_struct using
* diagnostics pages.
* Gets IB diagnostic pages on a given pathname from l_get_envsen().
* It also fills up the individual device element of l_state_struct using
* diagnostics pages.
*
* The path must be of the ses driver.
* e.g.
* or
*
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
int verbose)
{
int enc_type = 0;
return (L_INVALID_PATH_FORMAT);
}
/*
* get big buffer
*/
MAX_REC_DIAG_LENGTH)) == NULL) {
return (L_MALLOC_FAILED);
}
/*
* Get IB information
* Even if there are 2 IB's in this box on this loop don't bother
* talking to the other one as both IB's in a box
* are supposed to report the same information.
*/
verbose)) {
(void) g_destroy_data(ib_buf);
return (err);
}
/*
* Set up state structure
*/
for (i = 1; i < num_pages; i++) {
return (err);
}
L_PAGE_2) {
g_dump(" l_get_ib_status: Page 2: ",
HEX_ONLY);
}
L_PAGE_7) {
}
from_ptr += HEADER_LEN;
}
(void) g_destroy_data(ib_buf);
G_DPRINTF(" l_get_ib_status: Read %d Receive Diagnostic pages "
"from the IB.\n", num_pages);
return (err);
}
/*
* Get the total number of drives per box.
* This assumes front & rear are the same.
*/
if (l_state->total_num_drv) {
if (l_state->total_num_drv !=
return (L_INVALID_NUM_DISKS_ENCL);
}
} else {
if (enc_type == DAK_ENC_TYPE) {
} else {
}
}
}
}
/*
* transfer the individual drive Device Element information
* from IB state to drive state.
*/
&rear_index)) {
return (err);
}
/* Skip global element */
front_index++;
if (enc_type == DAK_ENC_TYPE) {
} else {
rear_index++;
}
(size_t)sizeof (struct device_element));
(size_t)sizeof (struct device_element));
}
g_dump(" l_get_ib_status: disk elements: ",
HEX_ONLY);
}
return (0);
}
/*
* Given an IB path get the port, A or B.
*
* OUTPUT:
* port_a: sets to 1 for port A
* and 0 for port B.
* RETURNS:
* err: 0 O.k.
* non-zero otherwise
*/
int
{
return (L_NO_SES_PATH);
}
return (L_MALLOC_FAILED);
}
(void) l_free_lstate(&ib_state);
return (err);
}
elem_index++; /* skip global */
bcopy((const void *)
break;
}
}
G_DPRINTF(" l_get_port: Found ses is the %s card.\n",
(void) l_free_lstate(&ib_state);
return (0);
}
/*
* This function expects a pointer to a device path ending in the form
* .../ses@w<NODEWWN>,<something> or .../ssd@w<NODEWWN>,<something>
*
* No validity checking of the path is done by the function.
*
* It gets the wwn (node wwn) out of the passed string, searches the passed
* map for a match, gets the corresponding phys addr (port id) for that entry
* and stores in the pointer the caller has passed as an argument (pid)
*
*
* If this interface is going to get exported, one point to be
* considered is if a call to g_get_path_type() has to be made.
*
* INPUT:
* map - pointer to the map
*
* OUTPUT:
* pid - the physical address associated for the node WWN that was found
* in the map
*
* RETURNS:
* 0 - on success
* non-zero - otherwise
*/
int
{
int i;
unsigned long long ll_wwn;
/* if mpxio device */
return (L_INVALID_PATH);
} else {
}
} else {
/* First a quick check on the path */
(*++char_ptr != 'w')) {
return (L_INVALID_PATH);
} else {
char_ptr++;
}
}
return (L_INVALID_PATH);
}
errno = 0; /* For error checking */
return (L_INVALID_PATH);
}
/*
* Search for the ses's node wwn in map to get the area and
* domain ids from the corresponding port id (phys address).
*/
dev_addr_ptr++, i++) {
break;
}
return (L_INVALID_PATH);
return (0);
}
/*
* Finds the disk's node wwn string, and
* port A and B's WWNs and their port status.
*
* INPUT:
* path - pointer to a ses path
* wwn_list - pointer to the wwn_list
*
* OUTPUT:
* state - node_wwn and wwn of ports A & B of disk, etc are inited
* - by l_get_disk_status()
* found_flag - incremented after each examined element in the map
*
* RETURNS:
* 0 O.K.
* non-zero otherwise.
*/
static int
{
int path_pid;
/*
* Get a new map.
*/
return (err);
case FC_TOP_PRIVATE_LOOP:
/*
* Get a generic path to a device
*
* This assumes the path looks something like this
* ...ses@x,0:0
* then creates a path that looks like
*/
return (L_INVALID_PATH);
}
/*
* Create complete path.
*
* Build entry ssd@xx,0:c,raw
* where xx is the WWN.
*/
G_DPRINTF(" l_get_node_status: Searching loop map "
"to find disk: ID:0x%x"
" AL_PA:0x%x\n", select_id,
"w%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x,0:c,raw",
sf_port_wwn[0],
sf_port_wwn[1],
sf_port_wwn[2],
sf_port_wwn[3],
sf_port_wwn[4],
sf_port_wwn[5],
sf_port_wwn[6],
sf_port_wwn[7]);
}
/*
* If we find a device on this loop in this box
* update its status.
*/
/*
* Found a device on this loop in this box.
*
* Update state.
*/
"%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
sf_node_wwn[0],
sf_node_wwn[1],
sf_node_wwn[2],
sf_node_wwn[3],
sf_node_wwn[4],
sf_node_wwn[5],
sf_node_wwn[6],
sf_node_wwn[7]);
break;
}
}
"g%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x:c,raw",
sf_node_wwn[0],
sf_node_wwn[1],
sf_node_wwn[2],
sf_node_wwn[3],
sf_node_wwn[4],
sf_node_wwn[5],
sf_node_wwn[6],
sf_node_wwn[7]);
/*
* check to make sure this is a valid path.
* Paths may not always be created on the
* host. So, we make a quick check.
*/
return (errno);
}
}
}
/* Bad if WWN is all zeros. */
sf_node_wwn)) {
G_DPRINTF(" l_get_node_status: "
"Disk state was "
" Invalid WWN.\n");
(*found_flag)++;
return (0);
}
/* get device status */
return (err);
}
/*
* found device in map. Don't need to look
* any further
*/
(*found_flag)++;
return (0);
}
} /* for loop */
break;
case FC_TOP_PUBLIC_LOOP:
case FC_TOP_FABRIC:
/*
* Get a generic path to a device
* This assumes the path looks something like this
* then creates a path that looks like
*/
return (L_INVALID_PATH);
}
return (err);
}
/* Now append the ssd string */
/*
* Create complete path.
*
* Build entry ssd@WWN,0:c,raw
*
* First, search the map for a device with the area code and
* domain as in 'path_pid'.
*/
port_id & 0xFF];
AREA_DOMAIN_ID) ==
(path_pid & AREA_DOMAIN_ID)) &&
/*
* Found the device. Update state.
*/
"w%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x,0:c,raw",
/*
* Paths for fabric cases may not always
* be created on the host. So, we make a
* quick check.
*/
return (errno);
}
"%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
} else {
break;
}
}
"w%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x,0:c,raw",
}
}
/* Bad if WWN is all zeros. */
dev_addr[j].gfc_port_dev.
raw_wwn)) {
" l_get_node_status: "
"Disk state was "
" Invalid WWN.\n");
(*found_flag)++;
return (0);
}
/* get device status */
return (err);
}
(*found_flag)++;
return (0);
} /* if select_id match */
} /* if !DTYPE_ESI */
} /* for loop */
break;
case FC_TOP_PT_PT:
return (L_PT_PT_FC_TOP_NOT_SUPPORTED);
default:
return (L_UNEXPECTED_FC_TOPOLOGY);
} /* End of switch on port_topology */
}
return (0);
}
/*
* Get the individual drives status for the device specified by the index.
* device at the path where the path is of the IB and updates the
* g_disk_state_struct structure.
*
* If the disk's port is bypassed, it gets the
* drive status such as node WWN from the second port.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
{
return (L_INVALID_PATH_FORMAT);
}
start_time = gethrtime();
/*
* Disk could have been bypassed on this loop.
* Check the port state before l_state_flag
* is set to L_INVALID_MAP.
*/
for (j = 0;
j++) {
elem_index++;
break;
elem_index +=
}
/*
* check if port A & B of backplane are bypassed.
* If so, do not bother.
*/
if (front_flag) {
bcopy((const void *)
return (0);
} else {
/* if disk is in rear slot */
bcopy((const void *)
return (0);
}
return (err);
if (!found_flag) {
&seslist, 0)) != 0) {
return (err);
}
goto done;
if (port_a_flag) {
if (err = l_get_port(
&port_a_flag, verbose)) {
goto done;
}
path) != 0) &&
!port_a_flag) {
if (err =
state, &found_flag,
goto done;
}
}
}
}
} else {
if (err = l_get_port(
&port_a_flag, verbose)) {
goto done;
}
path) != 0) && port_a_flag) {
if (err =
state, &found_flag,
goto done;
}
}
}
}
}
if (!found_flag) {
G_DPRINTF(" l_get_individual_state: "
"Disk state was "
"Not in map.\n");
} else {
G_DPRINTF(" l_get_individual_state: "
"Disk was found in the map.\n");
}
(void) g_free_multipath(seslist);
}
} else {
G_DPRINTF(" l_get_individual_state: Disk state was %s.\n",
"Not Installed" : "Not Available");
}
"\tTime = %lld millisec\n",
}
return (0);
done:
(void) g_free_multipath(seslist);
return (err);
}
/*
* Get the global state of the photon.
*
* INPUT:
* path and verbose flag
*
* "path" must be of the ses driver.
* e.g.
* or
*
* OUTPUT:
* The struct l_state (which was passed in) has the status info
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
{
return (L_INVALID_PATH_FORMAT);
}
start_time = gethrtime();
G_DPRINTF(" l_get_status: Get Status for enclosure at: "
" %s\n", path);
/* initialization */
return (err);
}
return (L_ENCL_INVALID_PATH);
}
/*
* Get all of the IB Receive Diagnostic pages.
*/
return (err);
}
/*
* Now get the individual devices information from
* the device itself.
*
* May need to use multiple paths to get to the
* front and rear drives in the box.
* If the loop is split some drives may not even be available
* from this host.
*
* The way this works is in the select ID the front disks
* are accessed via the IB with the bit 4 = 0
* and the rear disks by the IB with bit 4 = 1.
*
* First get device map from fc nexus driver for this loop.
*/
/*
* Get the boxes node WWN & al_pa for this path.
*/
return (err);
}
(void) l_free_box_list(&o_list);
return (err); /* Failure */
}
found_front = found_rear = 0;
for (i = 0; i < WWN_SIZE; i++) {
}
/*
* The al_pa (or pa) can be 24 bits in size for fabric loops.
* But we will take only the low order byte to get the select_id.
* Private loops have al_pa which is only a byte in size.
*/
G_DPRINTF(" l_get_status: Using this select_id 0x%x "
"and node WWN %s\n",
/* there is no way to obtain all the al_pa with */
/* current implementation. assume both front */
/* and rear. need changes later on. */
found_rear = 1;
found_front = 1;
} else {
if (select_id & ALT_BOX_ID) {
found_rear = 1;
while (b_list) {
(void) l_free_box_list(&o_list);
return (err);
}
/* Take the low order byte of al_pa */
if (!(select_id & ALT_BOX_ID)) {
(void) strcpy(ses_path_front,
found_front = 1;
break;
}
}
}
} else {
found_front = 1;
while (b_list) {
(void) l_free_box_list(&o_list);
return (err);
}
if (select_id & ALT_BOX_ID) {
(void) strcpy(ses_path_rear,
found_rear = 1;
break;
}
}
}
}
}
if (!found_front) {
(void) printf("l_get_status: Loop to front disks not found.\n");
}
if (!found_rear) {
(void) printf("l_get_status: Loop to rear disks not found.\n");
}
}
/*
* Get path to all the FC disk and tape devices.
*
* I get this now and pass down for performance
* reasons.
* If for some reason the list can become invalid,
* i.e. device being offlined, then the list
* must be re-gotten.
*/
return (err); /* Failure */
}
if (found_front) {
front_flag = 1;
count++, i++) {
if (enc_type == DAK_ENC_TYPE) {
G_DPRINTF(" l_get_status: Getting individual"
" State for disk in slot %d\n", count);
} else {
G_DPRINTF(" l_get_status: Getting individual"
" State for front disk in slot %d\n", i);
}
(void) l_free_box_list(&o_list);
(void) g_free_wwn_list(&wwn_list);
return (err);
}
}
} else {
/* Set to loop not accessable. */
}
}
/*
* For Daktari's, disk 0-5 information are located in the
* l_state->drv_front array
* For Daktari's, disk 6-11 information are located in the
* l_state->drv_rear array
*
* For this reason, on daktari's, I ignore the found_front and
* found_rear flags and check both the drv_front and drv_rear
*/
front_flag = 1;
G_DPRINTF(" l_get_status: Getting individual"
" State for disk in slot %d\n", count);
(void) l_free_box_list(&o_list);
(void) g_free_wwn_list(&wwn_list);
return (err);
}
}
G_DPRINTF(" l_get_status: Getting individual"
" State for rear disk in slot %d\n", i);
(void) l_free_box_list(&o_list);
(void) g_free_wwn_list(&wwn_list);
return (err);
}
}
} else if (enc_type != DAK_ENC_TYPE) {
/* Set to loop not accessable. */
}
}
(void) l_free_box_list(&o_list);
(void) g_free_wwn_list(&wwn_list);
"Time = %lld millisec\n",
}
return (0);
}
/*
* Check the SENA file for validity:
* - verify the size is that of 3 proms worth of text.
* - verify PROM_MAGIC.
* - verify (and print) the date.
* - verify the checksum.
* - verify the WWN == 0.
* Since this requires reading the entire file, do it now and pass a pointer
* to the allocated buffer back to the calling routine (which is responsible
* for freeing it). If the buffer is not allocated it will be NULL.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
static int
{
char *date_str;
/* read exec header */
return (errno);
return (L_DWNLD_READ_HEADER_FAIL);
}
return (L_DWNLD_READ_INCORRECT_BYTES);
}
return (L_DWNLD_INVALID_TEXT_SIZE);
}
return (L_MALLOC_FAILED);
return (L_DWNLD_READ_ERROR);
}
return (L_DWNLD_READ_INCORRECT_BYTES);
}
/* check the IB firmware MAGIC */
return (L_DWNLD_BAD_FRMWARE);
}
/*
* Get the date
*/
if (verbose) {
date_str);
}
/*
* verify checksum
*/
if (dl_info_offset == FPM_DL_INFO) {
} else {
}
if (j != 0) {
return (L_DWNLD_CHKSUM_FAILED);
}
/* file verified */
return (0);
}
/*
* Check the DPM file for validity:
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
static int
{
struct s3hdr {
} theRec;
int nread;
int reclen;
if (fd < 0) {
return (L_DWNLD_READ_ERROR);
}
/* First record */
if (nread != 4) {
return (L_DWNLD_READ_ERROR);
}
/* error in first record type */
return (L_DWNLD_READ_HEADER_FAIL);
}
if (reclen == 0) {
/* error in length == 0 */
return (L_DWNLD_READ_HEADER_FAIL);
}
/* error in trying to read data */
return (L_DWNLD_READ_HEADER_FAIL);
}
/* error in compiled file name */
return (L_DWNLD_READ_HEADER_FAIL);
}
/* Second record */
if (nread != 4) {
return (L_DWNLD_READ_ERROR);
}
/* error in second record type */
return (L_DWNLD_READ_HEADER_FAIL);
}
if (reclen == 0) {
/* error in length == 0 */
return (L_DWNLD_READ_HEADER_FAIL);
}
/* error in trying to read data */
return (L_DWNLD_READ_HEADER_FAIL);
}
/* error in SSC100 offset pointer */
return (L_DWNLD_READ_HEADER_FAIL);
}
return (0);
}
int
{
int file_fd;
int err;
return (L_OPEN_PATH_FAIL);
}
if (buf)
(void) g_destroy_data((char *)buf);
return (err);
}
/*
* Write buffer command set up to download
* firmware to the Photon IB.
*
* RETURNS:
* status
*/
static int
{
while (buf_len) {
if (status)
return (status);
}
return (status);
}
/*
*
*
*
* Inputs:
* fd - int for the file descriptor
* buf_ptr - uchar_t pointer to the firmware itself
* buf_len - int for the length of the data
*
* Returns:
* status: 0 indicates success, != 0 failure, returned from writebuffer
*
*/
static int
{
int status = 0;
int sz = 0;
int offs = 0;
while (buf_len > 0) {
if (status != 0) {
return (status);
}
}
return (status);
}
/*
* Downloads the new prom image to IB.
*
* INPUTS:
* path - physical path of Photon SES card
* file - input file for new code (may be NULL)
* ps - whether the "save" bit should be set
* verbose - to be verbose or not
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
{
int retry;
int enc_type;
return (L_INVALID_PATH_FORMAT);
}
if (!file) {
} else {
}
if (verbose)
return (L_OPEN_PATH_FAIL);
if (verbose)
P_DPRINTF(" Doing download to:"
/*
* Return a different error code here to differentiate between
* this failure in g_object_open() and the one above.
*/
return (L_INVALID_PATH);
}
return (L_SCSI_ERROR);
}
switch (enc_type) {
case DAK_ENC_TYPE:
/*
* We don't have a default daktari file location, so
* the user must specify the firmware file on the command line
*/
if (!file) {
return (L_REQUIRE_FILE);
}
/* Validate the file */
return (err);
}
/* Now go ahead and load up the data */
return (L_OPEN_PATH_FAIL);
}
return (L_MALLOC_FAILED);
}
g_destroy_data((char *)buf_ptr);
return (L_DWNLD_READ_ERROR);
}
break;
default:
if (buf_ptr) {
(void) g_destroy_data((char *)buf_ptr);
return (err);
}
}
break;
}
if (verbose) {
}
P_DPRINTF(" Checkfile OK.\n");
if (verbose) {
" Verifying the IB is available.\n"));
}
while (retry) {
break;
} else {
if ((retry % 30) == 0) {
ER_DPRINTF(" Waiting for the IB to be"
" available.\n");
}
(void) sleep(1);
}
}
if (!retry) {
if (buf_ptr)
(void) g_destroy_data((char *)buf_ptr);
(void) close(controller_fd);
return (status);
}
if (verbose)
P_DPRINTF(" Writing new image to IB\n");
switch (enc_type) {
case DAK_ENC_TYPE:
if (status != 0) {
g_destroy_data((char *)buf_ptr);
}
(void) close(controller_fd);
return (status);
}
break;
default:
if (status) {
(void) close(controller_fd);
(void) g_destroy_data((char *)buf_ptr);
return (status);
}
if (verbose) {
}
if (status) {
(void) close(controller_fd);
(void) g_destroy_data((char *)buf_ptr);
return (status);
}
break;
}
if (verbose) {
" Re-verifying the IB is available.\n"));
}
while (retry) {
break;
} else {
if ((retry % 30) == 0) {
ER_DPRINTF(" Waiting for the IB to be"
" available.\n");
}
(void) sleep(1);
}
retry--;
}
if (!retry) {
(void) close(controller_fd);
(void) g_destroy_data((char *)buf_ptr);
return (L_DWNLD_TIMED_OUT);
}
switch (enc_type) {
case DAK_ENC_TYPE:
break;
default:
if (verbose) {
}
break;
}
/*
* Reset the IB
*/
}
(void) close(controller_fd);
return (status);
}
/*
* Set the World Wide Name
* in page 4 of the Send Diagnostic command.
*
* Is it allowed to change the wwn ???
* The path must point to an IB.
*
*/
int
{
return (L_OPEN_PATH_FAIL);
}
/* Verify it is a Photon */
return (status);
}
return (L_ENCL_INVALID_PATH);
}
return (EINVAL);
}
sizeof (page4))) {
return (status);
}
/*
* Check the wwn really changed.
*/
return (status);
}
return (L_WARNING);
}
return (0);
}
/*
* Use a physical path to a disk in a Photon box
* as the base to genererate a path to a SES
* card in this box.
*
* path_phys: Physical path to a Photon disk.
* ses_path: This must be a pointer to an already allocated path string.
*
* RETURNS:
* 0 O.K.
* non-zero otherwise
*/
int
int verbose)
{
return (L_NO_SES_PATH);
}
return (L_INVLD_PATH_NO_SLASH_FND);
}
disk_flag++;
/*
* Figure out and create the boxes pathname.
*
* NOTE: This uses the fact that the disks's
* AL_PA and the boxes AL_PA must match
* the assigned hard address in the current
* implementations. This may not be true in the
* future.
*/
return (L_INVLD_PATH_NO_ATSIGN_FND);
}
char_ptr++; /* point to the loop identifier */
return (err);
}
case FC_TOP_PRIVATE_LOOP:
sf_inq_dtype == DTYPE_ESI) {
BOX_ID_MASK)) {
if (!found) {
ses_wwn = dev_addr_ptr->
ses_nwwn = dev_addr_ptr->
if (getenv("_LUX_P_DEBUG")) {
(void) g_ll_to_str(ses_wwn,
(char *)t_wwn);
(void) printf(
" l_get_ses_path: "
"Found ses wwn = %s "
}
} else {
ses_wwn1 = dev_addr_ptr->
if (getenv("_LUX_P_DEBUG")) {
(void) g_ll_to_str(ses_wwn1,
(char *)t_wwn);
(void) printf(
" l_get_ses_path: "
"Found second ses " "wwn = %s "
}
}
found++;
}
}
}
break;
case FC_TOP_FABRIC:
case FC_TOP_PUBLIC_LOOP:
DTYPE_ESI) {
/*
* We found an enclosure, lets match the
* area and domain codes for this enclosure with
* that of the ses path since there may be
* multiple enclosures with same box id on a
* fabric
*/
if ((al_pa & AREA_DOMAIN_ID) ==
(al_pa1 & AREA_DOMAIN_ID)) {
/*
* The area and domain matched. Now, we
* match the box id of the disk with
* this enclosure
*/
if (box_id ==
0xFF] & BOX_ID_MASK)) {
if (!found) {
ses_wwn = dev_addr_ptr->
ses_nwwn = dev_addr_ptr->
if (getenv("_LUX_P_DEBUG")) {
(void) g_ll_to_str(ses_wwn,
(char *)t_wwn);
(void) printf(
" l_get_ses_path: "
"Found ses wwn = %s "
"al_pa 0x%x\n", t_wwn,
al_pa1);
}
} else {
ses_wwn1 = dev_addr_ptr->
if (getenv("_LUX_P_DEBUG")) {
(void) g_ll_to_str(ses_wwn1,
(char *)t_wwn);
(void) printf(
" l_get_ses_path: "
"Found second ses "
"wwn = %s "
"al_pa 0x%x\n", t_wwn,
al_pa1);
}
}
found++;
}
}
}
}
break;
case FC_TOP_PT_PT:
return (L_PT_PT_FC_TOP_NOT_SUPPORTED);
default:
return (L_UNEXPECTED_FC_TOPOLOGY);
} /* End of switch on port_topology */
if (!found) {
return (L_NO_SES_PATH);
}
} else {
}
if (verbose) {
ses_path);
}
/*
* see if these paths exist.
*/
return (L_INVALID_PATH);
}
*char_ptr = '\0';
if (found > 1) {
P_DPRINTF(" l_get_ses_path: "
"Using second path, ses wwn1 = %s\n",
wwn);
return (0);
} else {
return (L_NO_SES_PATH);
}
}
return (0);
}
/*
*
* path_struct->p_physical_path must be of a disk.
*
* OUTPUT: path_struct->slot_valid
* path_struct->slot
* path_struct->f_flag
*
* RETURN:
* 0 O.K.
* non-zero otherwise
*/
int
{
return (L_INVALID_PATH_FORMAT);
}
/* Double check to see if we need to calculate. */
if (path_struct->slot_valid)
return (0);
/* Programming error if this occures */
return (L_INVLD_PHYS_PATH_TO_DISK);
}
return (err);
}
/*
* Find the slot by searching for the matching hard address.
* Take only the low order byte ignoring area and domain code in
* fabric devices' 24 bit al_pa
*/
P_DPRINTF(" l_get_slot: Searching Receive Diagnostic page 2, "
"to find the slot number with this ID:0x%x\n",
select_id) {
found = 1;
break;
select_id) {
path_struct->f_flag = 0;
found = 1;
break;
}
}
if (!found) {
return (L_INVALID_SLOT); /* Failure */
}
strlen(DAK_OFF_NAME)) == 0) ||
strlen(DAK_OFF_NAME)) == 0)) {
P_DPRINTF(" l_get_slot: Found slot %d.\n",
} else {
}
return (0);
}
void
{
} else if (code == S_NOT_AVAILABLE) {
} else if (code == S_NOT_INSTALLED) {
} else if (code == S_NONCRITICAL) {
} else if (code == S_CRITICAL) {
} else {
}
}
/*
* Get all ses paths paths to a given box.
* The arg should be the physical path to one of the box's IB.
* NOTE: The caller must free the allocated lists.
*
* OUTPUT:
* a pointer to a list of ses paths if found
* NULL on error.
*
* RETURNS:
* 0 if O.K.
* non-zero otherwise
*/
int
{
return (L_INVALID_PATH_FORMAT);
}
node_wwn_s[0] = '\0';
H_DPRINTF(" l_get_allses: Looking for all ses paths for"
" box at path: %s\n", path);
H_DPRINTF(" l_get_allses: physical_path= %s\n",
break;
}
}
if (node_wwn_s[0] == '\0') {
H_DPRINTF("node_wwn_s is NULL!\n");
return (L_NO_NODE_WWN_IN_BOXLIST);
}
(void) g_destroy_data(dlt);
}
return (L_MALLOC_FAILED);
}
H_DPRINTF(" l_get_allses: Found ses=%s\n",
} else {
}
}
}
return (0);
}
/*
* Routine to return the enclosure type pointed to by the path.
* Inputs: The inquiry data for the device in question
*
* Return: >= 0 is the type:
*
*
* 0 -> default (SENA)
* 1 -> Daktari
* 2 -> Other Enclosures
*
*/
int
{
strlen(ENCLOSURE_PROD_ID)) == 0) {
return (SENA_ENC_TYPE);
}
strlen(DAK_OFF_NAME)) == 0) {
return (DAK_ENC_TYPE);
}
strlen(DAK_PROD_STR)) == 0) {
return (DAK_ENC_TYPE);
}
/*
* as noted above
*/
return (UNDEF_ENC_TYPE);
}
void
(void) g_destroy_data(*map_mp_ptr);
}
*map_mp_ptr = NULL;
}
/*
* This function will return a linked list of device maps
* An example of when this will be used is when we want to return the device
* map of a vhci path.
*/
int
return (L_INVALID_PATH);
}
for (i = 0; i < pathcnt; i++) {
/*
* only pay attention to paths that are either
* ONLINE or STANDBY
*/
if ((new_map_mp_ptr = (gfc_map_mp_t *)
g_zalloc(sizeof (gfc_map_mp_t)))
== NULL) {
return (L_MALLOC_FAILED);
}
&(new_map_mp_ptr->map),
verbose)) {
return (err);
}
/* add newly created map onto list */
if (*map_mp_ptr == NULL) {
} else {
}
}
}
}
} else {
(sizeof (gfc_map_mp_t))) == NULL) {
return (L_MALLOC_FAILED);
}
}
return (0);
}