diff options
Diffstat (limited to 'usr/src/lib/storage/liba5k/common/mon.c')
-rw-r--r-- | usr/src/lib/storage/liba5k/common/mon.c | 5129 |
1 files changed, 5129 insertions, 0 deletions
diff --git a/usr/src/lib/storage/liba5k/common/mon.c b/usr/src/lib/storage/liba5k/common/mon.c new file mode 100644 index 0000000000..bc389a89d9 --- /dev/null +++ b/usr/src/lib/storage/liba5k/common/mon.c @@ -0,0 +1,5129 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/*LINTLIBRARY*/ + +/* + * 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 <sys/file.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <assert.h> +#include <sys/scsi/scsi.h> +#include <dirent.h> /* for DIR */ +#include <sys/vtoc.h> +#include <sys/dkio.h> +#include <nl_types.h> +#include <strings.h> +#include <sys/ddi.h> /* for max */ +#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 */ +#define PLNDEF "SUNW,pln" /* check if box name starts with 'c' */ +#define DOWNLOAD_RETRIES 60*5 /* 5 minutes */ +#define IBFIRMWARE_FILE "/usr/lib/locale/C/LC_MESSAGES/ibfirmware" + +/* 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 copy_config_page(struct l_state_struct *, uchar_t *); +static void copy_page_7(struct l_state_struct *, uchar_t *); +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 +l_get_mode_pg(char *path, uchar_t **pg_buf, int verbose) +{ +Mode_header_10 *mode_header_ptr; +int status, size, fd; + + 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 */ + if (strstr(path, SLSH_DRV_NAME_ST)) { + return (-1); + } + + /* open controller */ + if ((fd = g_object_open(path, O_NDELAY | O_RDWR)) == -1) + return (L_OPEN_PATH_FAIL); + + /* + * Read the first part of the page to get the page size + */ + size = 20; + if ((*pg_buf = (uchar_t *)g_zalloc(size)) == NULL) { + (void) close(fd); + return (L_MALLOC_FAILED); + } + /* read page */ + if (status = g_scsi_mode_sense_cmd(fd, *pg_buf, size, + 0, MODEPAGE_ALLPAGES)) { + (void) close(fd); + (void) g_destroy_data((char *)*pg_buf); + return (status); + } + /* Now get the size for all pages */ + mode_header_ptr = (struct mode_header_10_struct *)(void *)*pg_buf; + size = mode_header_ptr->length + sizeof (mode_header_ptr->length); + (void) g_destroy_data((char *)*pg_buf); + if ((*pg_buf = (uchar_t *)g_zalloc(size)) == NULL) { + (void) close(fd); + return (L_MALLOC_FAILED); + } + /* read all pages */ + if (status = g_scsi_mode_sense_cmd(fd, *pg_buf, size, + 0, MODEPAGE_ALLPAGES)) { + (void) close(fd); + (void) g_destroy_data((char *)*pg_buf); + return (status); + } + (void) close(fd); + return (0); +} + + + +/* + * Format QLA21xx status + * + * INPUTS: message buffer + * Count + * status + * + * OUTPUT: Message of this format in message buffer + * "status type: 0xstatus count" + */ +int +l_format_ifp_status_msg(char *status_msg_buf, int count, int status) +{ + if (status_msg_buf == NULL) { + return (0); + } + + switch (status) { + case IFP_CMD_CMPLT: + (void) sprintf(status_msg_buf, + MSGSTR(9000, "O.K. 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_INCOMPLETE: + (void) sprintf(status_msg_buf, + MSGSTR(9001, "Cmd incomplete 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_DMA_DERR: + (void) sprintf(status_msg_buf, + MSGSTR(9002, "DMA direction error 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_TRAN_ERR: + (void) sprintf(status_msg_buf, + MSGSTR(9003, "Unspecified transport error 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_RESET: + (void) sprintf(status_msg_buf, + MSGSTR(9004, "Reset aborted transport 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_ABORTED: + (void) sprintf(status_msg_buf, + MSGSTR(9005, "Cmd aborted 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_TIMEOUT: + (void) sprintf(status_msg_buf, + MSGSTR(9006, "Cmd Timeout 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_DATA_OVR: + (void) sprintf(status_msg_buf, + MSGSTR(9007, "Data Overrun 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_ABORT_REJECTED: + (void) sprintf(status_msg_buf, + MSGSTR(9008, "Target rejected abort msg 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_RESET_REJECTED: + (void) sprintf(status_msg_buf, + MSGSTR(9009, "Target rejected reset msg 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_DATA_UNDER: + (void) sprintf(status_msg_buf, + MSGSTR(9010, "Data underrun 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_QUEUE_FULL: + (void) sprintf(status_msg_buf, + MSGSTR(9011, "Queue full SCSI status 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_PORT_UNAVAIL: + (void) sprintf(status_msg_buf, + MSGSTR(9012, "Port unavailable 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_PORT_LOGGED_OUT: + (void) sprintf(status_msg_buf, + MSGSTR(9013, "Port loged out 0x%-2x" + " %d"), status, count); + break; + case IFP_CMD_PORT_CONFIG_CHANGED: + /* Not enough packets for given request */ + (void) sprintf(status_msg_buf, + MSGSTR(9014, "Port name changed 0x%-2x" + " %d"), status, count); + break; + default: + (void) sprintf(status_msg_buf, + "%s 0x%-2x" + " %d", MSGSTR(4, "Unknown status"), + status, count); + + } /* 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 +l_format_fc_status_msg(char *status_msg_buf, int count, int status) +{ + if (status_msg_buf == NULL) { + return (0); + } + + switch (status) { + case FCAL_STATUS_OK: + (void) sprintf(status_msg_buf, + MSGSTR(9015, "O.K. 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_P_RJT: + (void) sprintf(status_msg_buf, + MSGSTR(9016, "P_RJT (Frame Rejected) 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_F_RJT: + (void) sprintf(status_msg_buf, + MSGSTR(9017, "F_RJT (Frame Rejected) 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_P_BSY: + (void) sprintf(status_msg_buf, + MSGSTR(9018, "P_BSY (Port Busy) 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_F_BSY: + (void) sprintf(status_msg_buf, + MSGSTR(9019, "F_BSY (Port Busy) 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_OLDPORT_ONLINE: + /* Should not happen. */ + (void) sprintf(status_msg_buf, + MSGSTR(9020, "Old port Online 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_ERR_OFFLINE: + (void) sprintf(status_msg_buf, + MSGSTR(9021, "Link Offline 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_TIMEOUT: + /* Should not happen. */ + (void) sprintf(status_msg_buf, + MSGSTR(9022, "Sequence Timeout 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_ERR_OVERRUN: + (void) sprintf(status_msg_buf, + MSGSTR(9023, "Sequence Payload Overrun 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_LOOP_ONLINE: + (void) sprintf(status_msg_buf, + MSGSTR(9060, "Loop Online 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_OLD_PORT: + (void) sprintf(status_msg_buf, + MSGSTR(9061, "Old port 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_AL_PORT: + (void) sprintf(status_msg_buf, + MSGSTR(9062, "AL port 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_UNKNOWN_CQ_TYPE: + (void) sprintf(status_msg_buf, + MSGSTR(9024, "Unknown request type 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_BAD_SEG_CNT: + (void) sprintf(status_msg_buf, + MSGSTR(9025, "Bad segment count 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_MAX_XCHG_EXCEEDED: + (void) sprintf(status_msg_buf, + MSGSTR(9026, "Maximum exchanges exceeded 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_BAD_XID: + (void) sprintf(status_msg_buf, + MSGSTR(9027, "Bad exchange identifier 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_XCHG_BUSY: + (void) sprintf(status_msg_buf, + MSGSTR(9028, "Duplicate exchange request 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_BAD_POOL_ID: + (void) sprintf(status_msg_buf, + MSGSTR(9029, "Bad memory pool ID 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_INSUFFICIENT_CQES: + /* Not enough packets for given request */ + (void) sprintf(status_msg_buf, + MSGSTR(9030, "Invalid # of segments for req 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_ALLOC_FAIL: + (void) sprintf(status_msg_buf, + MSGSTR(9031, "Resource allocation failure 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_BAD_SID: + (void) sprintf(status_msg_buf, + MSGSTR(9032, "Bad Source Identifier(S_ID) 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_NO_SEQ_INIT: + (void) sprintf(status_msg_buf, + MSGSTR(9033, "No sequence initiative 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_BAD_DID: + (void) sprintf(status_msg_buf, + MSGSTR(9034, "Bad Destination ID(D_ID) 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_ABORTED: + (void) sprintf(status_msg_buf, + MSGSTR(9035, "Received BA_ACC from abort 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_ABORT_FAILED: + (void) sprintf(status_msg_buf, + MSGSTR(9036, "Received BA_RJT from abort 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_DIAG_BUSY: + (void) sprintf(status_msg_buf, + MSGSTR(9037, "Diagnostics currently busy 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_DIAG_INVALID: + (void) sprintf(status_msg_buf, + MSGSTR(9038, "Diagnostics illegal request 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_INCOMPLETE_DMA_ERR: + (void) sprintf(status_msg_buf, + MSGSTR(9039, "SBus DMA did not complete 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_CRC_ERR: + (void) sprintf(status_msg_buf, + MSGSTR(9040, "CRC error detected 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_OPEN_FAIL: + (void) sprintf(status_msg_buf, + MSGSTR(9063, "Open failure 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_ERROR: + (void) sprintf(status_msg_buf, + MSGSTR(9041, "Invalid status error 0x%-2x" + " %d"), status, count); + break; + case FCAL_STATUS_ONLINE_TIMEOUT: + (void) sprintf(status_msg_buf, + MSGSTR(9042, "Timed out before ONLINE 0x%-2x" + " %d"), status, count); + break; + default: + (void) sprintf(status_msg_buf, + "%s 0x%-2x" + " %d", MSGSTR(4, "Unknown status"), + status, count); + + } /* 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 +l_get_disk_element_index(struct l_state_struct *l_state, int *front_index, + int *rear_index) +{ +int index = 0, front_flag = 0, local_front = 0, local_rear = 0; +int i, rear_flag = 0; + + if ((l_state == NULL) || (front_index == NULL) || + (rear_index == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + *front_index = *rear_index = 0; + /* Get the indexes to the disk device elements */ + for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) { + if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_DD) { + if (front_flag) { + local_rear = index; + rear_flag = 1; + break; + } else { + local_front = index; + front_flag = 1; + } + } + index += l_state->ib_tbl.config.type_hdr[i].num; + 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", + local_front, local_rear); + + if (!front_flag && !rear_flag) { /* neither is found */ + return (L_RD_NO_DISK_ELEM); + } + *front_index = local_front; + *rear_index = local_rear; + return (0); +} + + + +/* + * l_led() manage the device led's + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +int +l_led(struct path_struct *path_struct, int led_action, + struct device_element *status, + int verbose) +{ +gfc_map_t map; +char ses_path[MAXPATHLEN]; +uchar_t *page_buf; +int err, write, fd, front_index, rear_index, offset; +unsigned short page_len; +struct device_element *elem; +L_state *l_state; +int enc_type; + + if ((path_struct == NULL) || (status == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + /* + * Need to get a valid location, front/rear & slot. + * + * The path_struct will return a valid slot + * and the IB path or a disk path. + */ + + map.dev_addr = (gfc_port_dev_info_t *)NULL; + if (!path_struct->ib_path_flag) { + if ((err = g_get_dev_map(path_struct->p_physical_path, + &map, verbose)) != 0) + return (err); + if ((err = l_get_ses_path(path_struct->p_physical_path, + ses_path, &map, verbose)) != 0) { + free((void *)map.dev_addr); + return (err); + } + } else { + (void) strcpy(ses_path, path_struct->p_physical_path); + } + + if ((l_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) { + free((void *)map.dev_addr); + return (L_MALLOC_FAILED); + } + + if (!path_struct->slot_valid) { + if ((map.dev_addr != NULL) && + (err = g_get_dev_map(path_struct->p_physical_path, + &map, verbose)) != 0) { + (void) l_free_lstate(&l_state); + return (err); + } + if ((err = l_get_ses_path(path_struct->p_physical_path, + ses_path, &map, verbose)) != 0) { + (void) l_free_lstate(&l_state); + free((void *)map.dev_addr); + return (err); + } + if ((err = l_get_status(ses_path, l_state, verbose)) != 0) { + (void) l_free_lstate(&l_state); + free((void *)map.dev_addr); + return (err); + } + + /* We are passing the disks path */ + if (err = l_get_slot(path_struct, l_state, verbose)) { + (void) l_free_lstate(&l_state); + free((void *)map.dev_addr); + return (err); + } + } + if (map.dev_addr != NULL) + free((void *)map.dev_addr); /* Not used anymore */ + + if ((page_buf = (uchar_t *)calloc(1, + MAX_REC_DIAG_LENGTH)) == NULL) { + (void) l_free_lstate(&l_state); + return (L_MALLOC_FAILED); + } + + if ((fd = g_object_open(ses_path, O_NDELAY | O_RDWR)) == -1) { + (void) l_free_lstate(&l_state); + (void) g_destroy_data(page_buf); + return (L_OPEN_PATH_FAIL); + } + + if (err = l_get_envsen_page(fd, page_buf, MAX_REC_DIAG_LENGTH, + L_PAGE_2, verbose)) { + (void) l_free_lstate(&l_state); + (void) close(fd); + (void) g_destroy_data(page_buf); + return (err); + } + + page_len = (page_buf[2] << 8 | page_buf[3]) + HEADER_LEN; + + /* Get index to the disk we are interested in */ + if (err = l_get_status(ses_path, l_state, verbose)) { + (void) l_free_lstate(&l_state); + (void) close(fd); + (void) g_destroy_data(page_buf); + return (err); + } + + /* find enclosure type */ + if ((strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_OFF_NAME, + strlen(DAK_OFF_NAME)) == 0) || + (strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_PROD_STR, + strlen(DAK_PROD_STR)) == 0)) { + enc_type = DAK_ENC_TYPE; + } else { + enc_type = SENA_ENC_TYPE; + } + + /* Double check slot. */ + if (path_struct->slot >= l_state->total_num_drv/2) { + (void) l_free_lstate(&l_state); + return (L_INVALID_SLOT); + } + + if (err = l_get_disk_element_index(l_state, &front_index, + &rear_index)) { + (void) l_free_lstate(&l_state); + return (err); + } + + /* Skip global element */ + front_index++; + if (enc_type == DAK_ENC_TYPE) { + rear_index += l_state->total_num_drv/2 + 1; + } else { + rear_index++; + } + + if (path_struct->f_flag) { + offset = (8 + (front_index + path_struct->slot)*4); + } else { + offset = (8 + (rear_index + path_struct->slot)*4); + } + + elem = (struct device_element *)(page_buf + offset); + /* + * now do requested action. + */ + bcopy((const void *)elem, (void *)status, + sizeof (struct device_element)); /* save status */ + bzero(elem, sizeof (struct device_element)); + elem->select = 1; + elem->dev_off = status->dev_off; + elem->en_bypass_a = status->en_bypass_a; + elem->en_bypass_b = status->en_bypass_b; + 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) { + (void) fprintf(stdout, + MSGSTR(9043, " Blinking LED for slot %d in enclosure" + " %s\n"), path_struct->f_flag ? path_struct->slot : + path_struct->slot + (MAX_DRIVES_DAK/2), + l_state->ib_tbl.enclosure_name); + } else { + (void) fprintf(stdout, + MSGSTR(9043, " Blinking LED for slot %d in enclosure" + " %s\n"), path_struct->slot, + l_state->ib_tbl.enclosure_name); + } + } + break; + case L_LED_OFF: + if (verbose) { + if (enc_type == DAK_ENC_TYPE) { + (void) fprintf(stdout, + MSGSTR(9044, + " Turning off LED for slot %d in enclosure" + " %s\n"), path_struct->f_flag ? path_struct->slot + : path_struct->slot + (MAX_DRIVES_DAK/2), + l_state->ib_tbl.enclosure_name); + } else { + (void) fprintf(stdout, + MSGSTR(9044, + " Turning off LED for slot %d in enclosure" + " %s\n"), path_struct->slot, + l_state->ib_tbl.enclosure_name); + } + } + break; + default: + (void) l_free_lstate(&l_state); + return (L_INVALID_LED_RQST); + } /* End of switch */ + + if (write) { + if (getenv("_LUX_D_DEBUG") != NULL) { + g_dump(" l_led: Updating led state: " + "Device Status Element ", + (uchar_t *)elem, sizeof (struct device_element), + HEX_ONLY); + } + if (err = g_scsi_send_diag_cmd(fd, + (uchar_t *)page_buf, page_len)) { + (void) close(fd); + (void) g_destroy_data(page_buf); + (void) l_free_lstate(&l_state); + return (err); + } + + bzero(page_buf, MAX_REC_DIAG_LENGTH); + if (err = l_get_envsen_page(fd, page_buf, MAX_REC_DIAG_LENGTH, + L_PAGE_2, verbose)) { + (void) g_destroy_data(page_buf); + (void) close(fd); + (void) l_free_lstate(&l_state); + return (err); + } + elem = (struct device_element *)(page_buf + offset); + bcopy((const void *)elem, (void *)status, + sizeof (struct device_element)); + } + if (getenv("_LUX_D_DEBUG") != NULL) { + g_dump(" l_led: Device Status Element ", + (uchar_t *)status, sizeof (struct device_element), + HEX_ONLY); + } + + (void) l_free_lstate(&l_state); + (void) close(fd); + (void) g_destroy_data(page_buf); + return (0); +} + + +/* + * frees the previously alloced l_state + * structure. + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +int +l_free_lstate(L_state **l_state) +{ +int i; + + if ((l_state == NULL) || (*l_state == NULL)) + return (0); + + for (i = 0; i < (int)(*l_state)->total_num_drv/2; i++) { + if ((*l_state)->drv_front[i].g_disk_state.multipath_list != NULL) + (void) g_free_multipath( + (*l_state)->drv_front[i].g_disk_state.multipath_list); + if ((*l_state)->drv_rear[i].g_disk_state.multipath_list != NULL) + (void) g_free_multipath( + (*l_state)->drv_rear[i].g_disk_state.multipath_list); + } + (void) g_destroy_data (*l_state); + l_state = NULL; + + return (0); +} + + + +/* + * Set the state of an individual disk + * in the Photon enclosure the powered + * up/down mode. The path must point to + * a disk or the ib_path_flag must be set. + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +int +l_dev_pwr_up_down(char *path_phys, struct path_struct *path_struct, + int power_off_flag, int verbose, int force_flag) +/*ARGSUSED*/ +{ +gfc_map_t map; +char ses_path[MAXPATHLEN], dev_path[MAXPATHLEN]; +int slot, err = 0; +L_state *l_state = NULL; +struct l_disk_state_struct *drive; +struct dlist *dl, *dl1; +devctl_hdl_t devhdl; +WWN_list *wwn_list = NULL; +L_inquiry inq; + + if (path_struct == NULL) { + return (L_INVALID_PATH_FORMAT); + } + + dl = (struct dlist *)NULL; + map.dev_addr = (gfc_port_dev_info_t *)NULL; + + if (err = g_get_dev_map(path_struct->p_physical_path, + &map, verbose)) + return (err); + + if (err = l_get_ses_path(path_struct->p_physical_path, + ses_path, &map, verbose)) { + free((void *)map.dev_addr); + return (err); + } + free((void *)map.dev_addr); /* Not used anymore */ + + /* + * Check to see if we have a photon, and if not, don't allow + * this operation + */ + if (err = g_get_inquiry(ses_path, &inq)) { + return (err); + } + if (l_get_enc_type(inq) != SENA_ENC_TYPE) { + return (L_ENCL_INVALID_PATH); + } + /* + * OK, so we have a photon... we can continue + */ + + + if ((l_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) { + return (L_MALLOC_FAILED); + } + + if (err = l_get_status(ses_path, l_state, verbose)) { + (void) l_free_lstate(&l_state); + return (err); + } + + if (!path_struct->slot_valid) { + /* We are passing the disks path */ + if (err = l_get_slot(path_struct, l_state, verbose)) { + (void) l_free_lstate(&l_state); + return (err); + } + } + + slot = path_struct->slot; + (void) strcpy(dev_path, path_struct->p_physical_path); + + /* + * Either front or rear drive + */ + if (path_struct->f_flag) { + drive = &l_state->drv_front[slot]; + } else { + drive = &l_state->drv_rear[slot]; + } + + /* + * Check for drive presence always + */ + if (drive->ib_status.code == S_NOT_INSTALLED) { + (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; + } + +pre_pwr_dwn: + + /* + * Check whether disk + * is reserved by another + * host + */ + if ((drive->g_disk_state.d_state_flags[PORT_A] & L_RESERVED) || + (drive->g_disk_state.d_state_flags[PORT_B] & + L_RESERVED)) { + (void) l_free_lstate(&l_state); + return (L_DEVICE_RESERVED); + } + + + if ((dl = (struct dlist *)g_zalloc(sizeof (struct dlist))) == NULL) { + (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. + */ + if (err = g_get_wwn_list(&wwn_list, verbose)) { + (void) g_destroy_data(dl); + (void) l_free_lstate(&l_state); + return (err); /* Failure */ + } + + dl->dev_path = dev_path; + if ((err = g_get_multipath(dev_path, + &(dl->multipath), wwn_list, verbose)) != 0) { + (void) g_destroy_data(dl); + (void) g_free_wwn_list(&wwn_list); + (void) l_free_lstate(&l_state); + return (err); + } + + for (dl1 = dl->multipath; dl1 != NULL; dl1 = dl1->next) { + if ((devhdl = devctl_device_acquire(dl1->dev_path, + DC_EXCL)) == NULL) { + if (errno != EBUSY) { + ER_DPRINTF("%s could not acquire" + " the device: %s\n\n", + strerror(errno), dl1->dev_path); + continue; + } + } + if (devctl_device_offline(devhdl) != 0) { + (void) devctl_release(devhdl); + (void) g_free_multipath(dl->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); + } + (void) devctl_release(devhdl); + } + +pwr_up_dwn: + err = pwr_up_down(ses_path, l_state, path_struct->f_flag, + path_struct->slot, power_off_flag, verbose); + + if (dl != NULL) { + (void) g_free_multipath(dl->multipath); + (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 powered up/down mode. + * The path must point to an IB. + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +int +l_pho_pwr_up_down(char *dev_name, char *path_phys, int power_off_flag, + int verbose, int force_flag) +{ +L_state *l_state = NULL; +int i, err = 0; +struct dlist *dl, *dl1; +char dev_path[MAXPATHLEN]; +devctl_hdl_t devhdl; +WWN_list *wwn_list = NULL; + + if (path_phys == NULL) { + return (L_INVALID_PATH_FORMAT); + } + + dl = (struct dlist *)NULL; + if ((l_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) { + return (L_MALLOC_FAILED); + } + if (err = l_get_status(path_phys, l_state, verbose)) { + (void) l_free_lstate(&l_state); + return (err); + } + if (power_off_flag && !force_flag) { + goto pre_pwr_dwn; + } else { + goto pwr_up_dwn; + } + +pre_pwr_dwn: + + /* + * Check if any disk in this enclosure + * is reserved by another host before + * the power off. + */ + for (i = 0; i < l_state->total_num_drv/2; i++) { + if ((l_state->drv_front[i].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state->drv_front[i].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED) || + (l_state->drv_rear[i].g_disk_state.d_state_flags[PORT_A] & + L_RESERVED) || + (l_state->drv_rear[i].g_disk_state.d_state_flags[PORT_B] & + L_RESERVED)) { + 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. + */ + if (err = g_get_wwn_list(&wwn_list, verbose)) { + (void) l_free_lstate(&l_state); + return (err); /* Failure */ + } + for (i = 0; i < l_state->total_num_drv/2; i++) { + if (*l_state->drv_front[i].g_disk_state.physical_path) { + (void) memset(dev_path, 0, MAXPATHLEN); + (void) strcpy(dev_path, + (char *)&l_state->drv_front[i].g_disk_state.physical_path); + + if ((dl = (struct dlist *) + g_zalloc(sizeof (struct dlist))) == NULL) { + (void) g_free_wwn_list(&wwn_list); + (void) l_free_lstate(&l_state); + return (L_MALLOC_FAILED); + } + dl->dev_path = dev_path; + if (g_get_multipath(dev_path, &(dl->multipath), + wwn_list, verbose) != 0) { + (void) g_destroy_data(dl); + continue; + } + + for (dl1 = dl->multipath; + dl1 != NULL; + dl1 = dl1->next) { + + /* attempt to acquire the device */ + if ((devhdl = devctl_device_acquire( + dl1->dev_path, DC_EXCL)) == NULL) { + if (errno != EBUSY) { + ER_DPRINTF("%s: Could not " + "acquire the device: %s\n\n", + strerror(errno), + dl1->dev_path); + continue; + } + } + + /* attempt to offline the device */ + if (devctl_device_offline(devhdl) != 0) { + (void) devctl_release(devhdl); + (void) g_free_multipath( + dl->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_free_multipath(dl->multipath); + (void) g_destroy_data(dl); + + } + if (*l_state->drv_rear[i].g_disk_state.physical_path) { + (void) memset(dev_path, 0, MAXPATHLEN); + (void) strcpy(dev_path, + (char *)&l_state->drv_rear[i].g_disk_state.physical_path); + + if ((dl = (struct dlist *) + g_zalloc(sizeof (struct dlist))) == NULL) { + (void) g_free_wwn_list(&wwn_list); + (void) l_free_lstate(&l_state); + return (L_MALLOC_FAILED); + } + dl->dev_path = dev_path; + if (g_get_multipath(dev_path, &(dl->multipath), + wwn_list, verbose) != 0) { + (void) g_destroy_data(dl); + continue; + } + + + for (dl1 = dl->multipath; + dl1 != NULL; + dl1 = dl1->next) { + + /* attempt to acquire the device */ + if ((devhdl = devctl_device_acquire( + dl1->dev_path, DC_EXCL)) == NULL) { + if (errno != EBUSY) { + ER_DPRINTF("%s: Could not " + "acquire the device: %s\n\n", + strerror(errno), + dl1->dev_path); + continue; + } + } + /* attempt to offline the device */ + if (devctl_device_offline(devhdl) != 0) { + (void) devctl_release(devhdl); + (void) g_free_multipath( + dl->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_free_multipath(dl->multipath); + (void) g_destroy_data(dl); + + } + } + +pwr_up_dwn: + + (void) g_free_wwn_list(&wwn_list); + if ((err = pwr_up_down(path_phys, l_state, 0, -1, + 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 + * powered up/down mode. + * The path must point to an IB. + * slot == -1 implies entire enclosure. + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +static int +pwr_up_down(char *path_phys, L_state *l_state, int front, int slot, + int power_off_flag, int verbose) +{ +L_inquiry inq; +int fd, status, err; +uchar_t *page_buf; +int front_index, rear_index, front_offset, rear_offset; +unsigned short page_len; +struct device_element *front_elem, *rear_elem; + + (void) memset(&inq, 0, sizeof (inq)); + if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) { + return (L_OPEN_PATH_FAIL); + } + /* Verify it is a Photon */ + if (status = g_scsi_inquiry_cmd(fd, + (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { + (void) close(fd); + return (status); + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && + (!(strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { + (void) close(fd); + return (L_ENCL_INVALID_PATH); + } + + /* + * To power up/down a Photon we use the Driver Off + * bit in the global device control element. + */ + if ((page_buf = (uchar_t *)malloc(MAX_REC_DIAG_LENGTH)) == NULL) { + return (L_MALLOC_FAILED); + } + if (err = l_get_envsen_page(fd, page_buf, MAX_REC_DIAG_LENGTH, + L_PAGE_2, verbose)) { + (void) close(fd); + (void) g_destroy_data(page_buf); + return (err); + } + + page_len = (page_buf[2] << 8 | page_buf[3]) + HEADER_LEN; + + /* Double check slot as convert_name only does gross check */ + if (slot >= l_state->total_num_drv/2) { + (void) close(fd); + (void) g_destroy_data(page_buf); + return (L_INVALID_SLOT); + } + + if (err = l_get_disk_element_index(l_state, &front_index, + &rear_index)) { + (void) close(fd); + (void) g_destroy_data(page_buf); + return (err); + } + /* Skip global element */ + front_index++; + rear_index++; + + front_offset = (8 + (front_index + slot)*4); + rear_offset = (8 + (rear_index + slot)*4); + + front_elem = (struct device_element *)(page_buf + front_offset); + rear_elem = (struct device_element *)(page_buf + rear_offset); + + if (front || slot == -1) { + /* + * now do requested action. + */ + bzero(front_elem, sizeof (struct device_element)); + /* Set/reset power off bit */ + front_elem->dev_off = power_off_flag; + front_elem->select = 1; + } + if (!front || slot == -1) { + /* Now do rear */ + bzero(rear_elem, sizeof (struct device_element)); + /* Set/reset power off bit */ + rear_elem->dev_off = power_off_flag; + rear_elem->select = 1; + } + + if (getenv("_LUX_D_DEBUG") != NULL) { + if (front || slot == -1) { + g_dump(" pwr_up_down: " + "Front Device Status Element ", + (uchar_t *)front_elem, + sizeof (struct device_element), + HEX_ONLY); + } + if (!front || slot == -1) { + g_dump(" pwr_up_down: " + "Rear Device Status Element ", + (uchar_t *)rear_elem, + sizeof (struct device_element), + HEX_ONLY); + } + } + if (err = g_scsi_send_diag_cmd(fd, + (uchar_t *)page_buf, page_len)) { + (void) close(fd); + (void) g_destroy_data(page_buf); + return (err); + } + (void) close(fd); + (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 +l_new_password(char *path_phys, char *password) +{ +Page4_name page4; +L_inquiry inq; +int fd, status; + + (void) memset(&inq, 0, sizeof (inq)); + (void) memset(&page4, 0, sizeof (page4)); + + if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) { + return (L_OPEN_PATH_FAIL); + } + /* Verify it is a Photon */ + if (status = g_scsi_inquiry_cmd(fd, + (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { + (void) close(fd); + return (status); + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && + (!(strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { + (void) close(fd); + return (L_ENCL_INVALID_PATH); + } + + page4.page_code = L_PAGE_4; + page4.page_len = (ushort_t)max((strlen(password) + 4), 8); + /* Double check */ + if (strlen(password) > 8) { + return (L_INVALID_PASSWORD_LEN); + } + page4.string_code = L_PASSWORD; + page4.enable = 1; + (void) strcpy((char *)page4.name, password); + + if (status = g_scsi_send_diag_cmd(fd, (uchar_t *)&page4, + page4.page_len + HEADER_LEN)) { + (void) close(fd); + return (status); + } + + (void) close(fd); + 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 +l_new_name(char *path_phys, char *name) +{ +Page4_name page4; +L_inquiry inq; +int fd, status; + + if ((path_phys == NULL) || (name == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + (void) memset(&inq, 0, sizeof (inq)); + (void) memset(&page4, 0, sizeof (page4)); + + if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) { + return (L_OPEN_PATH_FAIL); + } + /* Verify it is a Photon */ + if (status = g_scsi_inquiry_cmd(fd, + (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { + (void) close(fd); + return (status); + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && + (!(strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { + (void) close(fd); + return (L_ENCL_INVALID_PATH); + } + + page4.page_code = L_PAGE_4; + page4.page_len = (ushort_t)((sizeof (struct page4_name) - 4)); + page4.string_code = L_ENCL_NAME; + page4.enable = 1; + strncpy((char *)page4.name, name, sizeof (page4.name)); + + if (status = g_scsi_send_diag_cmd(fd, (uchar_t *)&page4, + sizeof (page4))) { + (void) close(fd); + return (status); + } + + /* + * Check the name really changed. + */ + if (status = g_scsi_inquiry_cmd(fd, + (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { + (void) close(fd); + return (status); + } + if (strncmp((char *)inq.inq_box_name, name, sizeof (page4.name)) != 0) { + char name_buf[MAXNAMELEN]; + (void) close(fd); + strncpy((char *)name_buf, (char *)inq.inq_box_name, + sizeof (inq.inq_box_name)); + return (L_ENCL_NAME_CHANGE_FAIL); + } + + (void) close(fd); + return (0); +} + + + +/* + * Issue a Loop Port enable Primitive sequence + * to the device specified by the pathname. + */ +int +l_enable(char *path, int verbose) +/*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 +l_bypass(char *path, int verbose) +/*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 +l_get_box_list(struct box_list_struct **box_list_ptr, int verbose) +{ +char *dev_name; +DIR *dirp; +struct dirent *entp; +char namebuf[MAXPATHLEN]; +struct stat sb; +char *result = NULL; +int fd, status; +L_inquiry inq; +Box_list *box_list, *l1, *l2; +IB_page_config page1; +uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; +int al_pa; + + if (box_list_ptr == NULL) { + return (L_INVALID_PATH_FORMAT); + } + + box_list = *box_list_ptr = NULL; + if ((dev_name = (char *)g_zalloc(sizeof ("/dev/es"))) == NULL) { + return (L_MALLOC_FAILED); + } + (void) sprintf((char *)dev_name, "/dev/es"); + + if (verbose) { + (void) fprintf(stdout, + MSGSTR(9045, + " Searching directory %s for links to enclosures\n"), + dev_name); + } + + if ((dirp = opendir(dev_name)) == NULL) { + (void) g_destroy_data(dev_name); + /* No Photons found */ + B_DPRINTF(" l_get_box_list: No Photons found\n"); + return (0); + } + + + while ((entp = readdir(dirp)) != NULL) { + if (strcmp(entp->d_name, ".") == 0 || + strcmp(entp->d_name, "..") == 0) + continue; + + (void) sprintf(namebuf, "%s/%s", dev_name, entp->d_name); + + if ((lstat(namebuf, &sb)) < 0) { + ER_DPRINTF("Warning: Cannot stat %s\n", + namebuf); + continue; + } + + if (!S_ISLNK(sb.st_mode)) { + ER_DPRINTF("Warning: %s is not a symbolic link\n", + namebuf); + continue; + } + if ((result = g_get_physical_name_from_link(namebuf)) == NULL) { + 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", + dev_name, entp->d_name); + if ((fd = g_object_open(result, O_NDELAY | O_RDONLY)) == -1) { + g_destroy_data(result); + continue; /* Ignore errors */ + } + /* Get the box name */ + if (status = g_scsi_inquiry_cmd(fd, + (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { + (void) close(fd); + g_destroy_data(result); + continue; /* Ignore errors */ + } + + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != NULL) || + (((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI) && + (l_get_enc_type(inq) == DAK_ENC_TYPE))) { + /* + * Found Photon/Daktari + */ + + /* Get the port WWN from the IB, page 1 */ + if ((status = l_get_envsen_page(fd, (uchar_t *)&page1, + sizeof (page1), 1, 0)) != NULL) { + (void) close(fd); + g_destroy_data(result); + (void) g_destroy_data(dev_name); + closedir(dirp); + return (status); + } + + /* + * Build list of names. + */ + if ((l2 = (struct box_list_struct *) + g_zalloc(sizeof (struct box_list_struct))) + == NULL) { + (void) close(fd); + g_destroy_data(result); + g_destroy_data(dev_name); + closedir(dirp); + return (L_MALLOC_FAILED); + } + + /* Fill in structure */ + (void) strcpy((char *)l2->b_physical_path, + (char *)result); + (void) strcpy((char *)l2->logical_path, + (char *)namebuf); + bcopy((void *)page1.enc_node_wwn, + (void *)l2->b_node_wwn, WWN_SIZE); + (void) sprintf(l2->b_node_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + page1.enc_node_wwn[0], + page1.enc_node_wwn[1], + page1.enc_node_wwn[2], + page1.enc_node_wwn[3], + page1.enc_node_wwn[4], + page1.enc_node_wwn[5], + page1.enc_node_wwn[6], + page1.enc_node_wwn[7]); + strncpy((char *)l2->prod_id_s, + (char *)inq.inq_pid, + sizeof (inq.inq_pid)); + strncpy((char *)l2->b_name, + (char *)inq.inq_box_name, + sizeof (inq.inq_box_name)); + /* make sure null terminated */ + l2->b_name[sizeof (l2->b_name) - 1] = NULL; + + /* + * Now get the port WWN for the port + * we are connected to. + */ + status = g_get_wwn(result, port_wwn, node_wwn, + &al_pa, verbose); + if (status == 0) { + (void) sprintf(l2->b_port_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + port_wwn[0], port_wwn[1], port_wwn[2], + port_wwn[3], port_wwn[4], port_wwn[5], + port_wwn[6], port_wwn[7]); + bcopy((void *)port_wwn, + (void *)l2->b_port_wwn, WWN_SIZE); + + B_DPRINTF(" l_get_box_list:" + " Found enclosure named:%s\n", l2->b_name); + + if (box_list == NULL) { + l1 = box_list = l2; + } else { + l2->box_prev = l1; + l1 = l1->box_next = l2; + } + } else { + (void) close(fd); + g_destroy_data(result); + (void) g_destroy_data(dev_name); + (void) g_destroy_data(l2); + closedir(dirp); + return (status); + } + + } + g_destroy_data(result); + (void) close(fd); + *box_list_ptr = box_list; /* pass back ptr to list */ + } + (void) g_destroy_data(dev_name); + closedir(dirp); + return (0); +} + +void +l_free_box_list(struct box_list_struct **box_list) +{ +Box_list *next = NULL; + + if (box_list == NULL) { + return; + } + + for (; *box_list != NULL; *box_list = next) { + next = (*box_list)->box_next; + (void) g_destroy_data(*box_list); + } + + *box_list = NULL; +} + + + +/* + * 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 +l_duplicate_names(Box_list *b_list, char wwn[], char *name, int verbose) +{ +int dup_flag = 0; +Box_list *box_list_ptr = NULL; + + if ((name == NULL) || (wwn == NULL)) + return (0); + + box_list_ptr = b_list; + while (box_list_ptr != NULL) { + if ((strcmp(name, (const char *)box_list_ptr->b_name) == 0) && + (strcmp(box_list_ptr->b_node_wwn_s, wwn) != 0)) { + dup_flag++; + break; + } + box_list_ptr = box_list_ptr->box_next; + } + return (dup_flag); +} + + + +/* + * Checks for a name conflict with an SSA cN type name. + */ +int +l_get_conflict(char *name, char **result, int verbose) +{ +char s[MAXPATHLEN]; +char *p = NULL; +char *pp = NULL; +Box_list *box_list = NULL; +int found_box = 0, err = 0; + + (void) strcpy(s, name); + if ((*result = g_get_physical_name(s)) == NULL) { + return (0); + } + if ((strstr((const char *)*result, PLNDEF)) == NULL) { + (void) g_destroy_data(*result); + *result = NULL; + return (0); + } + P_DPRINTF(" l_get_conflict: Found " + "SSA path using %s\n", s); + /* Find path to IB */ + if ((err = l_get_box_list(&box_list, verbose)) != 0) { + return (err); /* Failure */ + } + /* + * Valid cN type name found. + */ + while (box_list != NULL) { + if ((strcmp((char *)s, + (char *)box_list->b_name)) == 0) { + found_box = 1; + if (p == NULL) { + if ((p = g_zalloc(strlen( + box_list->b_physical_path) + + 2)) == NULL) { + (void) l_free_box_list(&box_list); + return (errno); + } + } else { + if ((pp = g_zalloc(strlen( + box_list->b_physical_path) + + strlen(p) + + 2)) == NULL) { + (void) l_free_box_list(&box_list); + return (errno); + } + (void) strcpy(pp, p); + (void) g_destroy_data(p); + p = pp; + } + (void) strcat(p, box_list->b_physical_path); + (void) strcat(p, "\n"); + } + box_list = box_list->box_next; + } + 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 : + * if phys_path is that of an A5K/Daktari disk + * 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 +load_flds_if_enc_disk(char *phys_path, struct path_struct **path_struct) +{ + int err = 0, verbose = 0; + char ses_path[MAXPATHLEN]; + gfc_map_t map; + L_inquiry inq; + L_state *l_state = NULL; + + if ((path_struct == NULL) || (*path_struct == NULL) || + (phys_path == NULL) || (*phys_path == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + if ((strstr(phys_path, SLSH_DRV_NAME_SSD) == NULL) || + (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); + } + + if ((err = g_get_dev_map(phys_path, &map, verbose)) != 0) { + return (err); + } + + if ((err = l_get_ses_path(phys_path, ses_path, &map, verbose)) != 0) { + (void) free(map.dev_addr); + 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 + * SES node is not of a photon/Daktari, we dont proceed + */ + if ((err = g_get_inquiry(ses_path, &inq)) != 0) { + (void) free(map.dev_addr); + 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 + */ + if (((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) || + (strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) != 0)) && + ((l_get_enc_type(inq) != DAK_ENC_TYPE))) { + /* Not a photon/Daktari */ + (void) free(map.dev_addr); + return (0); + } + + /* Now, set some fields that l_get_slot() uses and then call it */ + if ((l_state = (L_state *)g_zalloc(sizeof (L_state))) == NULL) { + (void) free(map.dev_addr); + return (L_MALLOC_FAILED); + } + + if ((err = l_get_ib_status(ses_path, l_state, verbose)) != 0) { + (void) free(map.dev_addr); + (void) l_free_lstate(&l_state); + return (err); + } + + if ((err = l_get_slot(*path_struct, l_state, verbose)) != 0) { + (void) free(map.dev_addr); + (void) l_free_lstate(&l_state); + return (err); + } + + (void) free(map.dev_addr); + (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 +l_convert_name(char *name, char **phys_path, + struct path_struct **path_struct, int verbose) +{ +char tmp_name[MAXPATHLEN], ses_path[MAXPATHLEN]; +char *char_ptr, *ptr = NULL; +char *result = NULL; +char *env = NULL; +char save_frd; /* which designator was it? */ +int slot = 0, slot_flag = 0, found_box = 0, found_comma = 0; +int err = 0, enc_type = 0; +hrtime_t start_time, end_time; +Box_list *box_list = NULL, *box_list_ptr = NULL; +L_inquiry inq; +L_state *l_state = NULL; +Path_struct *path_ptr = NULL; +WWN_list *wwn_list, *wwn_list_ptr; + + if ((name == NULL) || (phys_path == NULL) || + (path_struct == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + if ((env = getenv("_LUX_T_DEBUG")) != NULL) { + start_time = gethrtime(); + } + + if ((*path_struct = path_ptr = (struct path_struct *) + g_zalloc(sizeof (struct path_struct))) == NULL) { + return (L_MALLOC_FAILED); + } + + *phys_path = NULL; + /* + * If the path contains a "/" then assume + * it is a logical or physical path as the + * box name or wwn can not contain "/"s. + */ + if (strchr(name, '/') != NULL) { + if ((result = g_get_physical_name(name)) == NULL) { + return (L_NO_PHYS_PATH); + } + + path_ptr->p_physical_path = result; + /* + * Make sure it's a disk or tape path + */ + if (strstr(name, DEV_RDIR) || strstr(name, SLSH_DRV_NAME_SSD) || + strstr(name, DEV_TAPE_DIR) || + strstr(name, SLSH_DRV_NAME_ST)) { + if ((err = g_get_inquiry(result, &inq)) != 0) { + (void) free(result); + return (L_SCSI_ERROR); + } + /* + * Check to see if it is not a + * A5K/v880/v890 disk + * + */ + if (!g_enclDiskChk((char *)inq.inq_vid, + (char *)inq.inq_pid)) { + path_ptr->argv = name; + *phys_path = result; + return (0); + } + } + + if (err = load_flds_if_enc_disk(result, path_struct)) { + (void) free(result); + return (err); + } + goto done; + } + + (void) strcpy(tmp_name, name); + if ((tmp_name[0] == 'c') && + ((int)strlen(tmp_name) > 1) && ((int)strlen(tmp_name) < 5)) { + if ((err = l_get_conflict(tmp_name, &result, verbose)) != 0) { + if (result != NULL) { + (void) g_destroy_data(result); + } + return (err); + } + if (result != NULL) { + path_ptr->p_physical_path = result; + if ((err = g_get_inquiry(result, &inq)) != 0) { + (void) free(result); + return (L_SCSI_ERROR); + } + /* + * Check to see if it is a supported + * A5K/v880/v890 storage subsystem disk + */ + if (g_enclDiskChk((char *)inq.inq_vid, + (char *)inq.inq_pid)) { + if (err = load_flds_if_enc_disk( + result, path_struct)) { + (void) free(result); + 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 + */ + if (((char_ptr = strstr(tmp_name, ",")) != NULL) && + ((*(char_ptr + 1) == 'f') || (*(char_ptr + 1) == 'r') || + (*(char_ptr + 1) == 's'))) { + char_ptr++; /* point to f/r */ + if ((*char_ptr == 'f') || (*char_ptr == 's')) { + path_ptr->f_flag = 1; + } else if (*char_ptr != 'r') { + return (L_INVALID_PATH_FORMAT); + } + save_frd = (char)*char_ptr; /* save it */ + char_ptr++; + slot = strtol(char_ptr, &ptr, 10); + /* + * NOTE: Need to double check the slot when we get + * the number of the devices actually in the box. + */ + if ((slot < 0) || (ptr == char_ptr) || + ((save_frd == 's' && slot >= MAX_DRIVES_DAK) || + ((save_frd != 's' && slot >= (MAX_DRIVES_PER_BOX/2))))) { + return (L_INVALID_SLOT); + } + /* Say slot valid. */ + slot_flag = path_ptr->slot_valid = 1; + if (save_frd == 's' && slot >= (MAX_DRIVES_DAK/2)) { + path_ptr->slot = slot = slot % (MAX_DRIVES_DAK/2); + path_ptr->f_flag = 0; + } else + path_ptr->slot = slot; + } + + if (((char_ptr = strstr(tmp_name, ",")) != NULL) && + ((*(char_ptr + 1) == 'f') || (*(char_ptr + 1) == 'r') || + (*(char_ptr + 1) == 's'))) { + *char_ptr = NULL; /* make just box name */ + found_comma = 1; + } + /* Find path to IB */ + if ((err = l_get_box_list(&box_list, verbose)) != 0) { + (void) l_free_box_list(&box_list); + return (err); + } + box_list_ptr = box_list; + /* Look for box name. */ + while (box_list != NULL) { + if ((strcmp((char *)tmp_name, (char *)box_list->b_name)) == 0) { + result = + g_alloc_string(box_list->b_physical_path); + L_DPRINTF(" l_convert_name:" + " Found subsystem: name %s WWN %s\n", + box_list->b_name, box_list->b_node_wwn_s); + /* + * Check for another box with this name. + */ + if (l_duplicate_names(box_list_ptr, + box_list->b_node_wwn_s, + (char *)box_list->b_name, + verbose)) { + (void) l_free_box_list(&box_list_ptr); + (void) g_destroy_data(result); + return (L_DUPLICATE_ENCLOSURES); + } + found_box = 1; + break; + } + box_list = box_list->box_next; + } + /* + * Check to see if we must get individual disks path. + */ + + if (found_box && slot_flag) { + if ((l_state = (L_state *)g_zalloc(sizeof (L_state))) == NULL) { + (void) g_destroy_data(result); + (void) l_free_box_list(&box_list_ptr); + return (L_MALLOC_FAILED); + } + (void) strcpy(ses_path, result); + if ((err = l_get_status(ses_path, l_state, + 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. + */ + if (slot >= l_state->total_num_drv/2) { + 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 */ + if (g_get_inquiry(ses_path, &inq)) { + return (L_SCSI_ERROR); + } + enc_type = l_get_enc_type(inq); + if (((enc_type == DAK_ENC_TYPE) && (save_frd != 's')) || + ((enc_type != DAK_ENC_TYPE) && (save_frd == 's'))) { + 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); + } + + if (path_ptr->f_flag) { + if (*l_state->drv_front[slot].g_disk_state.physical_path) { + result = + g_alloc_string(l_state->drv_front[slot].g_disk_state.physical_path); + } else { + /* Result is the IB path */ + path_ptr->ib_path_flag = 1; + path_ptr->p_physical_path = + g_alloc_string(result); + (void) g_destroy_data(result); + result = NULL; + } + } else { + if (*l_state->drv_rear[slot].g_disk_state.physical_path) { + result = + g_alloc_string(l_state->drv_rear[slot].g_disk_state.physical_path); + } else { + /* Result is the IB path */ + path_ptr->ib_path_flag = 1; + path_ptr->p_physical_path = + g_alloc_string(result); + (void) g_destroy_data(result); + result = NULL; + } + } + (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 */ + box_list = box_list_ptr; + while (box_list != NULL) { + 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 = + g_alloc_string(box_list->b_physical_path); + L_DPRINTF(" l_convert_name:" + " Found subsystem using the WWN" + ": name %s WWN %s\n", + box_list->b_name, box_list->b_node_wwn_s); + goto done; + } + box_list = box_list->box_next; + } + /* Look for a device's WWN */ + if (strlen(tmp_name) <= L_WWN_LENGTH) { + if ((err = g_get_wwn_list(&wwn_list, verbose)) != 0) { + (void) l_free_box_list(&box_list_ptr); + return (err); + } + for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL; + wwn_list_ptr = wwn_list_ptr->wwn_next) { + 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. + * It MAY be in a photon/Daktari. If it is, we'll set + * additional fields in path_struct. + */ + result = g_alloc_string(wwn_list_ptr->physical_path); + L_DPRINTF(" l_convert_name:" + " Found device: WWN %s Path %s\n", + tmp_name, wwn_list_ptr->logical_path); + + (void) g_free_wwn_list(&wwn_list); + + /* + * Now check if it is a disk in an A5K and set + * path_struct fields + */ + path_ptr->p_physical_path = result; + if ((err = g_get_inquiry(result, &inq)) != 0) { + (void) free(result); + return (L_SCSI_ERROR); + } + /* + * Check to see if it is a supported + * A5K/v880/v890 storage subsystem disk + */ + if (g_enclDiskChk((char *)inq.inq_vid, + (char *)inq.inq_pid)) { + if (err = load_flds_if_enc_disk( + result, path_struct)) { + (void) free(result); + return (err); + } + } + goto done; + } + } + } + + /* + * Try again in case we were in the /dev + * or /devices directory. + */ + result = g_get_physical_name(name); + +done: + (void) l_free_box_list(&box_list_ptr); + path_ptr->argv = name; + if (result == NULL) { + if (!path_ptr->ib_path_flag) + return (-1); + } else { + path_ptr->p_physical_path = result; + } + + 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", + path_ptr->p_physical_path, + path_ptr->argv, + path_ptr->slot_valid, + path_ptr->slot, + path_ptr->f_flag, + path_ptr->ib_path_flag); + if (env != NULL) { + end_time = gethrtime(); + (void) fprintf(stdout, " l_convert_name: " + "Time = %lld millisec\n", + (end_time - start_time)/1000000); + } + + if (path_ptr->ib_path_flag) + return (-1); + *phys_path = result; + return (0); +} + + +/* + * Gets envsen information of an enclosure from IB + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +int +l_get_envsen_page(int fd, uchar_t *buf, int buf_size, uchar_t page_code, + int verbose) +{ +Rec_diag_hdr hdr; +uchar_t *pg; +int size, new_size, status; + + if (buf == NULL) { + return (L_INVALID_BUF_LEN); + } + + if (verbose) { + (void) fprintf(stdout, + MSGSTR(9046, " Reading SES page %x\n"), page_code); + } + + (void) memset(&hdr, 0, sizeof (struct rec_diag_hdr)); + if (status = g_scsi_rec_diag_cmd(fd, (uchar_t *)&hdr, + sizeof (struct rec_diag_hdr), page_code)) { + return (status); + } + + /* Check */ + if ((hdr.page_code != page_code) || (hdr.page_len == 0)) { + return (L_RD_PG_INVLD_CODE); + } + size = HEADER_LEN + hdr.page_len; + /* + * Because of a hardware restriction in the soc+ chip + * the transfers must be word aligned. + */ + while (size & 0x03) { + size++; + if (size > buf_size) { + return (L_RD_PG_MIN_BUFF); + } + P_DPRINTF(" l_get_envsen_page: Adjusting size of the " + "g_scsi_rec_diag_cmd buffer.\n"); + } + + if ((pg = (uchar_t *)g_zalloc(size)) == NULL) { + return (L_MALLOC_FAILED); + } + + P_DPRINTF(" l_get_envsen_page: Reading page %x of size 0x%x\n", + page_code, size); + if (status = g_scsi_rec_diag_cmd(fd, pg, size, page_code)) { + (void) g_destroy_data((char *)pg); + return (status); + } + + new_size = MIN(size, buf_size); + bcopy((const void *)pg, (void *)buf, (size_t)new_size); + + (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 +l_get_envsen(char *path_phys, uchar_t *buf, int size, int verbose) +{ +int fd, rval; +uchar_t *page_list_ptr, page_code, *local_buf_ptr = buf; +Rec_diag_hdr *hdr = (struct rec_diag_hdr *)(void *)buf; +ushort_t num_pages; + + if ((path_phys == NULL) || (buf == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + page_code = L_PAGE_PAGE_LIST; + + /* open IB */ + if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) + return (L_OPEN_PATH_FAIL); + + P_DPRINTF(" l_get_envsen: Getting list of supported" + " pages from IB\n"); + if (verbose) { + (void) fprintf(stdout, + MSGSTR(9047, " Getting list of supported pages from IB\n")); + } + + /* Get page 0 */ + if ((rval = l_get_envsen_page(fd, local_buf_ptr, + size, page_code, verbose)) != NULL) { + (void) close(fd); + return (rval); + } + + page_list_ptr = buf + HEADER_LEN + 1; /* +1 to skip page 0 */ + + num_pages = hdr->page_len - 1; + + /* + * 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 + * X3T10/Project 1212-D/Rev 8a, the enclosure supported + * 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. + */ + if (num_pages < 1 || num_pages >= L_MAX_POSSIBLE_PAGES) { + 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 */ + while (hdr->page_len & 0x03) { + hdr->page_len++; + } + local_buf_ptr += HEADER_LEN + hdr->page_len; + + /* + * 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. + */ + page_code = *page_list_ptr; + + if ((rval = l_get_envsen_page(fd, local_buf_ptr, + size, page_code, verbose)) != NULL) { + (void) close(fd); + return (rval); + } + hdr = (struct rec_diag_hdr *)(void *)local_buf_ptr; + local_buf_ptr += HEADER_LEN + hdr->page_len; + } + + (void) close(fd); + 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 +l_get_disk_status(char *path, struct l_disk_state_struct *l_disk_state, + WWN_list *wwn_list, int verbose) +{ +struct dlist *ml; +char path_a[MAXPATHLEN], path_b[MAXPATHLEN], ses_path[MAXPATHLEN]; +gfc_map_t map; +int path_a_found = 0, path_b_found = 0, local_port_a_flag; +uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; +int al_pa, err, pathcnt = 1; +int i = 0; +char temppath[MAXPATHLEN]; +mp_pathlist_t pathlist; +char pwwn[WWN_S_LEN]; +struct stat sbuf; + + if ((path == NULL) || (l_disk_state == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + /* Check device name */ + if (stat(path, &sbuf) || (sbuf.st_rdev == NODEV)) { + G_DPRINTF(" l_get_disk_status: invalid device %s\n", path); + return (L_INVALID_PATH); + } + + /* Initialize */ + *path_a = *path_b = NULL; + l_disk_state->g_disk_state.num_blocks = 0; + + /* Get paths. */ + g_get_multipath(path, + &(l_disk_state->g_disk_state.multipath_list), + wwn_list, verbose); + ml = l_disk_state->g_disk_state.multipath_list; + if (ml == NULL) { + l_disk_state->l_state_flag = L_NO_PATH_FOUND; + G_DPRINTF(" l_get_disk_status: Error finding a " + "multipath to the disk.\n"); + return (0); + } + + if (strstr(path, SCSI_VHCI) != NULL) { + /* + * It is an MPXIO Path + */ + (void) strcpy(temppath, path); + if (g_get_pathlist(temppath, &pathlist)) { + return (0); + } + pathcnt = pathlist.path_count; + 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 + */ + if (pathlist.path_info[i].path_state != + MDI_PATHINFO_STATE_ONLINE && + pathlist.path_info[i].path_state != + MDI_PATHINFO_STATE_STANDBY) { + continue; + } + (void) strncpy(pwwn, pathlist.path_info[i].path_addr, + L_WWN_LENGTH); + pwwn[L_WWN_LENGTH] = '\0'; + if (!(path_a_found || path_b_found)) { + if (pwwn[1] == '1') { + local_port_a_flag = 1; + } else { + local_port_a_flag = 0; + } + } else if (path_a_found && + (strstr(l_disk_state->g_disk_state.port_a_wwn_s, + pwwn) == NULL)) { + /* do port b */ + local_port_a_flag = 0; + } else if (path_b_found && + (strstr(l_disk_state->g_disk_state.port_b_wwn_s, + pwwn) == NULL)) { + /* do port a */ + local_port_a_flag = 1; + } + + if (err = l_get_disk_port_status(path, + l_disk_state, local_port_a_flag, verbose)) { + return (err); + } + + if (local_port_a_flag && (!path_a_found)) { + (void) strcpy(l_disk_state-> + g_disk_state.port_a_wwn_s, pwwn); + l_disk_state->g_disk_state.port_a_valid++; + path_a_found++; + } + + if ((!local_port_a_flag) && (!path_b_found)) { + (void) strcpy(l_disk_state-> + g_disk_state.port_b_wwn_s, pwwn); + l_disk_state->g_disk_state.port_b_valid++; + path_b_found++; + } + } + free(pathlist.path_info); + return (0); + } + + while (ml && (!(path_a_found && path_b_found))) { + if (err = g_get_dev_map(ml->dev_path, &map, verbose)) { + (void) g_free_multipath(ml); + return (err); + } + if ((err = l_get_ses_path(ml->dev_path, ses_path, + &map, verbose)) != 0) { + (void) g_free_multipath(ml); + free((void *)map.dev_addr); + return (err); + } + free((void *)map.dev_addr); /* Not used anymore */ + + /* + * Get the port, A or B, of the disk, + * by passing the IB path. + */ + if (err = l_get_port(ses_path, &local_port_a_flag, verbose)) { + (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 " + "found: %s\n", ml->dev_path); + if (err = l_get_disk_port_status(ml->dev_path, + l_disk_state, local_port_a_flag, verbose)) { + (void) g_free_multipath(ml); + return (err); + } + if (err = g_get_wwn(ml->dev_path, + port_wwn, node_wwn, + &al_pa, verbose)) { + (void) g_free_multipath(ml); + return (err); + } + (void) sprintf(l_disk_state->g_disk_state.port_a_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + port_wwn[0], port_wwn[1], port_wwn[2], port_wwn[3], + port_wwn[4], port_wwn[5], port_wwn[6], port_wwn[7]); + + l_disk_state->g_disk_state.port_a_valid++; + path_a_found++; + } + if ((!local_port_a_flag) && (!path_b_found)) { + G_DPRINTF(" l_get_disk_status: Path to Port B " + "found: %s\n", ml->dev_path); + if (err = l_get_disk_port_status(ml->dev_path, + l_disk_state, local_port_a_flag, verbose)) { + return (err); + } + if (err = g_get_wwn(ml->dev_path, + port_wwn, node_wwn, + &al_pa, verbose)) { + (void) g_free_multipath(ml); + return (err); + } + (void) sprintf(l_disk_state->g_disk_state.port_b_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + port_wwn[0], port_wwn[1], port_wwn[2], port_wwn[3], + port_wwn[4], port_wwn[5], port_wwn[6], port_wwn[7]); + + l_disk_state->g_disk_state.port_b_valid++; + path_b_found++; + } + ml = ml->next; + } + return (0); + + +} + + + +/* + * Check for Persistent Reservations. + */ +int +l_persistent_check(int fd, struct l_disk_state_struct *l_disk_state, + int verbose) +{ +int status; +Read_keys read_key_buf; +Read_reserv read_reserv_buf; + + (void) memset(&read_key_buf, 0, sizeof (struct read_keys_struct)); + if ((status = g_scsi_persistent_reserve_in_cmd(fd, + (uchar_t *)&read_key_buf, sizeof (struct read_keys_struct), + ACTION_READ_KEYS))) { + return (status); + } + /* This means persistent reservations are supported by the disk. */ + l_disk_state->g_disk_state.persistent_reserv_flag = 1; + + if (read_key_buf.rk_length) { + l_disk_state->g_disk_state.persistent_registered = 1; + } + + (void) memset(&read_reserv_buf, 0, + sizeof (struct read_reserv_struct)); + if ((status = g_scsi_persistent_reserve_in_cmd(fd, + (uchar_t *)&read_reserv_buf, + sizeof (struct read_reserv_struct), + ACTION_READ_RESERV))) { + return (status); + } + if (read_reserv_buf.rr_length) { + l_disk_state->g_disk_state.persistent_active = 1; + } + if (verbose) { + (void) fprintf(stdout, + MSGSTR(9048, " Checking for Persistent " + "Reservations:")); + if (l_disk_state->g_disk_state.persistent_reserv_flag) { + if (l_disk_state->g_disk_state.persistent_active != NULL) { + (void) fprintf(stdout, MSGSTR(39, "Active")); + } else { + (void) fprintf(stdout, MSGSTR(9049, "Registered")); + } + } else { + (void) fprintf(stdout, + MSGSTR(87, + "Not being used")); + } + (void) fprintf(stdout, "\n"); + } + 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 +l_get_disk_port_status(char *path, struct l_disk_state_struct *l_disk_state, + int port_a_flag, int verbose) +{ +int fd, status = 0, local_state = 0; +Read_capacity_data capacity; /* local read capacity buffer */ +struct vtoc vtoc; + + if ((path == NULL) || (l_disk_state == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + /* + * Try to open drive. + */ + if ((fd = g_object_open(path, O_RDONLY)) == -1) { + if ((fd = g_object_open(path, + O_RDONLY | O_NDELAY)) == -1) { + G_DPRINTF(" l_get_disk_port_status: Error " + "opening drive %s\n", path); + local_state = L_OPEN_FAIL; + } else { + /* See if drive ready */ + if (status = g_scsi_tur(fd)) { + if ((status & L_SCSI_ERROR) && + ((status & ~L_SCSI_ERROR) == STATUS_CHECK)) { + /* + * TBD + * This is where I should figure out + * if the device is Not Ready or whatever. + */ + local_state = L_NOT_READY; + } else if ((status & L_SCSI_ERROR) && + ((status & ~L_SCSI_ERROR) == + STATUS_RESERVATION_CONFLICT)) { + /* mark reserved */ + local_state = L_RESERVED; + } else { + local_state = L_SCSI_ERR; + } + + /* + * There may not be a label on the drive - check + */ + } else if (ioctl(fd, DKIOCGVTOC, &vtoc) == 0) { + /* + * Sanity-check the vtoc + */ + if (vtoc.v_sanity != VTOC_SANE || + vtoc.v_sectorsz != DEV_BSIZE) { + local_state = L_NO_LABEL; + G_DPRINTF(" l_get_disk_port_status: " + "Checking vtoc - No Label found.\n"); + } + } else if (errno != ENOTSUP) { + I_DPRINTF("\t- DKIOCGVTOC ioctl failed: " + " invalid geometry\n"); + local_state = L_NO_LABEL; + } + } + } + /* + * Need an extra check for tape devices + * read capacity should not be run on tape devices. + * It will always return Not Readable + */ + if (((local_state == 0) || (local_state == L_NO_LABEL)) && + ! (strstr(path, SLSH_DRV_NAME_ST))) { + + if (status = g_scsi_read_capacity_cmd(fd, (uchar_t *)&capacity, + sizeof (capacity))) { + G_DPRINTF(" l_get_disk_port_status: " + "Read Capacity failed.\n"); + if (status & L_SCSI_ERROR) { + if ((status & ~L_SCSI_ERROR) == + STATUS_RESERVATION_CONFLICT) { + /* mark reserved */ + local_state |= L_RESERVED; + } else + /* mark bad */ + local_state |= L_NOT_READABLE; + } else { + /* + * TBD + * Need a more complete state definition here. + */ + l_disk_state->g_disk_state.d_state_flags[port_a_flag] = + L_SCSI_ERR; + (void) close(fd); + return (0); + } + } else { + /* save capacity */ + l_disk_state->g_disk_state.num_blocks = + capacity.last_block_addr + 1; + } + + } + (void) close(fd); + + l_disk_state->g_disk_state.d_state_flags[port_a_flag] = local_state; + G_DPRINTF(" l_get_disk_port_status: Individual Disk" + " Status: 0x%x for" + " port %s for path:" + " %s\n", local_state, + port_a_flag ? "A" : "B", path); + + return (0); +} + + + +/* + * Copy and format page 1 from big buffer to state structure. + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ + +static int +copy_config_page(struct l_state_struct *l_state, uchar_t *from_ptr) +{ +IB_page_config *encl_ptr; +int size, i; + + + encl_ptr = (struct ib_page_config *)(void *)from_ptr; + + /* Sanity check. */ + if ((encl_ptr->enc_len > MAX_VEND_SPECIFIC_ENC) || + (encl_ptr->enc_len == 0)) { + return (L_REC_DIAG_PG1); + } + if ((encl_ptr->enc_num_elem > MAX_IB_ELEMENTS) || + (encl_ptr->enc_num_elem == 0)) { + return (L_REC_DIAG_PG1); + } + + size = HEADER_LEN + 4 + HEADER_LEN + encl_ptr->enc_len; + bcopy((void *)(from_ptr), + (void *)&l_state->ib_tbl.config, (size_t)size); + /* + * Copy Type Descriptors seperately to get aligned. + */ + from_ptr += size; + size = (sizeof (struct type_desc_hdr))*encl_ptr->enc_num_elem; + bcopy((void *)(from_ptr), + (void *)&l_state->ib_tbl.config.type_hdr, (size_t)size); + + /* + * Copy Text Descriptors seperately to get aligned. + * + * Must use the text size from the Type Descriptors. + */ + from_ptr += size; + for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) { + size = l_state->ib_tbl.config.type_hdr[i].text_len; + bcopy((void *)(from_ptr), + (void *)&l_state->ib_tbl.config.text[i], (size_t)size); + from_ptr += size; + } + 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 +copy_page_7(struct l_state_struct *l_state, uchar_t *from_ptr) +{ +uchar_t *my_from_ptr; +int size, j, k, p7_index; + + size = HEADER_LEN + + sizeof (l_state->ib_tbl.p7_s.gen_code); + bcopy((void *)(from_ptr), + (void *)&l_state->ib_tbl.p7_s, (size_t)size); + my_from_ptr = from_ptr + size; + if (getenv("_LUX_D_DEBUG") != NULL) { + g_dump(" copy_page_7: Page 7 header: ", + (uchar_t *)&l_state->ib_tbl.p7_s, size, + HEX_ASCII); + (void) fprintf(stdout, + " copy_page_7: Elements being stored " + "in state table\n" + " "); + } + /* I am assuming page 1 has been read. */ + for (j = 0, p7_index = 0; + j < (int)l_state->ib_tbl.config.enc_num_elem; j++) { + /* Copy global element */ + size = HEADER_LEN + + ((*(my_from_ptr + 2) << 8) | *(my_from_ptr + 3)); + bcopy((void *)(my_from_ptr), + (void *)&l_state->ib_tbl.p7_s.element_desc[p7_index++], + (size_t)size); + my_from_ptr += size; + for (k = 0; k < (int)l_state->ib_tbl.config.type_hdr[j].num; + k++) { + /* Copy individual elements */ + size = HEADER_LEN + + ((*(my_from_ptr + 2) << 8) | + *(my_from_ptr + 3)); + bcopy((void *)(my_from_ptr), + (void *)&l_state->ib_tbl.p7_s.element_desc[p7_index++], + (size_t)size); + 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. + * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0 + * or + * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@WWN,0:0 + * + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +int +l_get_ib_status(char *path, struct l_state_struct *l_state, + int verbose) +{ +L_inquiry inq; +uchar_t *ib_buf, *from_ptr; +int num_pages, i, size, err; +IB_page_2 *encl_ptr; +int front_index, rear_index; +int enc_type = 0; + + if ((path == NULL) || (l_state == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + /* + * get big buffer + */ + if ((ib_buf = (uchar_t *)calloc(1, + 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. + */ + if (err = l_get_envsen(path, ib_buf, MAX_REC_DIAG_LENGTH, + verbose)) { + (void) g_destroy_data(ib_buf); + return (err); + } + + /* + * Set up state structure + */ + bcopy((void *)ib_buf, (void *)&l_state->ib_tbl.p0, + (size_t)sizeof (struct ib_page_0)); + + num_pages = l_state->ib_tbl.p0.page_len; + from_ptr = ib_buf + HEADER_LEN + l_state->ib_tbl.p0.page_len; + + for (i = 1; i < num_pages; i++) { + if (l_state->ib_tbl.p0.sup_page_codes[i] == L_PAGE_1) { + if (err = copy_config_page(l_state, from_ptr)) { + return (err); + } + } else if (l_state->ib_tbl.p0.sup_page_codes[i] == + L_PAGE_2) { + encl_ptr = (struct ib_page_2 *)(void *)from_ptr; + size = HEADER_LEN + encl_ptr->page_len; + bcopy((void *)(from_ptr), + (void *)&l_state->ib_tbl.p2_s, (size_t)size); + if (getenv("_LUX_D_DEBUG") != NULL) { + g_dump(" l_get_ib_status: Page 2: ", + (uchar_t *)&l_state->ib_tbl.p2_s, size, + HEX_ONLY); + } + + } else if (l_state->ib_tbl.p0.sup_page_codes[i] == + L_PAGE_7) { + (void) copy_page_7(l_state, from_ptr); + } + from_ptr += ((*(from_ptr + 2) << 8) | *(from_ptr + 3)); + 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); + + if (err = g_get_inquiry(path, &inq)) { + return (err); + } + enc_type = l_get_enc_type(inq); + /* + * Get the total number of drives per box. + * This assumes front & rear are the same. + */ + l_state->total_num_drv = 0; /* default to use as a flag */ + for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) { + if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_DD) { + if (l_state->total_num_drv) { + if (l_state->total_num_drv != + (l_state->ib_tbl.config.type_hdr[i].num * 2)) { + return (L_INVALID_NUM_DISKS_ENCL); + } + } else { + if (enc_type == DAK_ENC_TYPE) { + l_state->total_num_drv = + l_state->ib_tbl.config.type_hdr[i].num; + } else { + l_state->total_num_drv = + l_state->ib_tbl.config.type_hdr[i].num * 2; + } + } + } + } + + /* + * transfer the individual drive Device Element information + * from IB state to drive state. + */ + if (err = l_get_disk_element_index(l_state, &front_index, + &rear_index)) { + return (err); + } + /* Skip global element */ + front_index++; + if (enc_type == DAK_ENC_TYPE) { + rear_index += l_state->total_num_drv/2 + 1; + } else { + rear_index++; + } + + for (i = 0; i < l_state->total_num_drv/2; i++) { + bcopy((void *)&l_state->ib_tbl.p2_s.element[front_index + i], + (void *)&l_state->drv_front[i].ib_status, + (size_t)sizeof (struct device_element)); + bcopy((void *)&l_state->ib_tbl.p2_s.element[rear_index + i], + (void *)&l_state->drv_rear[i].ib_status, + (size_t)sizeof (struct device_element)); + } + if (getenv("_LUX_G_DEBUG") != NULL) { + g_dump(" l_get_ib_status: disk elements: ", + (uchar_t *)&l_state->ib_tbl.p2_s.element[front_index], + ((sizeof (struct device_element)) * (l_state->total_num_drv)), + 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 +l_get_port(char *ses_path, int *port_a, int verbose) +{ +L_state *ib_state = NULL; +Ctlr_elem_st ctlr; +int i, err, elem_index = 0; + + if ((ses_path == NULL) || (port_a == NULL)) { + return (L_NO_SES_PATH); + } + + if ((ib_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) { + return (L_MALLOC_FAILED); + } + + bzero(&ctlr, sizeof (ctlr)); + if (err = l_get_ib_status(ses_path, ib_state, verbose)) { + (void) l_free_lstate(&ib_state); + return (err); + } + + for (i = 0; i < (int)ib_state->ib_tbl.config.enc_num_elem; i++) { + elem_index++; /* skip global */ + if (ib_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_IB) { + bcopy((const void *) + &ib_state->ib_tbl.p2_s.element[elem_index], + (void *)&ctlr, sizeof (ctlr)); + break; + } + elem_index += ib_state->ib_tbl.config.type_hdr[i].num; + } + *port_a = ctlr.report; + G_DPRINTF(" l_get_port: Found ses is the %s card.\n", + ctlr.report ? "A" : "B"); + (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) + * + * This function is to be called only for public/fabric topologies + * + * 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: + * path - pointer to the enclosure/disk device path + * 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 +l_get_pid_from_path(const char *path, const gfc_map_t *map, int *pid) +{ +int i; +unsigned long long ll_wwn; +char *char_ptr, wwn_str[WWN_SIZE * 2 + 1]; +char *byte_ptr, *temp_ptr; +gfc_port_dev_info_t *dev_addr_ptr; +mp_pathlist_t pathlist; +char path0[MAXPATHLEN], pwwn0[WWN_S_LEN]; + + /* if mpxio device */ + if (strstr(path, SCSI_VHCI) != NULL) { + (void) strcpy(path0, path); + if (g_get_pathlist(path0, &pathlist)) { + return (L_INVALID_PATH); + } else { + (void) strncpy(pwwn0, pathlist.path_info[0]. + path_addr, L_WWN_LENGTH); + pwwn0[L_WWN_LENGTH] = '\0'; + free(pathlist.path_info); + char_ptr = pwwn0; + } + } else { + /* First a quick check on the path */ + if (((char_ptr = strrchr(path, '@')) == NULL) || + (*++char_ptr != 'w')) { + return (L_INVALID_PATH); + } else { + char_ptr++; + } + } + + if (strlen(char_ptr) < (WWN_SIZE * 2)) { + return (L_INVALID_PATH); + } + (void) strncpy(wwn_str, char_ptr, WWN_SIZE * 2); + wwn_str[WWN_SIZE * 2] = '\0'; + errno = 0; /* For error checking */ + ll_wwn = strtoull(wwn_str, &temp_ptr, L_WWN_LENGTH); + + if (errno || (temp_ptr != (wwn_str + (WWN_SIZE * 2)))) { + return (L_INVALID_PATH); + } + + byte_ptr = (char *)&ll_wwn; + + /* + * Search for the ses's node wwn in map to get the area and + * domain ids from the corresponding port id (phys address). + */ + for (dev_addr_ptr = map->dev_addr, i = 0; i < map->count; + dev_addr_ptr++, i++) { + if (bcmp((char *)dev_addr_ptr->gfc_port_dev. + pub_port.dev_nwwn.raw_wwn, byte_ptr, WWN_SIZE) == 0) + break; + } + if (i >= map->count) + return (L_INVALID_PATH); + *pid = dev_addr_ptr->gfc_port_dev.pub_port.dev_did.port_id; + 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 +l_get_node_status(char *path, struct l_disk_state_struct *state, + int *found_flag, WWN_list *wwn_list, int verbose) +{ +int j, select_id, err; +int path_pid; +char temp_path[MAXPATHLEN]; +char sbuf[MAXPATHLEN], *char_ptr; +gfc_map_mp_t *map_mp, *map_ptr; +struct stat stat_buf; +WWN_list *wwnlp; +char wwnp[WWN_S_LEN]; + + /* + * Get a new map. + */ + map_mp = NULL; + if (err = get_mp_dev_map(path, &map_mp, verbose)) + return (err); + + for (map_ptr = map_mp; map_ptr != NULL; map_ptr = map_ptr->map_next) { + switch (map_ptr->map.hba_addr.port_topology) { + case FC_TOP_PRIVATE_LOOP: + for (j = 0; j < map_ptr->map.count; j++) { + /* + * Get a generic path to a device + * + * This assumes the path looks something like this + * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/... + * ...ses@x,0:0 + * then creates a path that looks like + * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ssd@ + */ + (void) strcpy(temp_path, path); + if ((char_ptr = strrchr(temp_path, '/')) == NULL) { + free_mp_dev_map(&map_mp); + return (L_INVALID_PATH); + } + *char_ptr = '\0'; /* Terminate sting */ + (void) strcat(temp_path, SLSH_DRV_NAME_SSD); + /* + * Create complete path. + * + * Build entry ssd@xx,0:c,raw + * where xx is the WWN. + */ + select_id = g_sf_alpa_to_switch[map_ptr->map. + dev_addr[j].gfc_port_dev.priv_port.sf_al_pa]; + G_DPRINTF(" l_get_node_status: Searching loop map " + "to find disk: ID:0x%x" + " AL_PA:0x%x\n", select_id, + state->ib_status.sel_id); + + if (strstr(path, SCSI_VHCI) == NULL) { + + (void) sprintf(sbuf, + "w%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x,0:c,raw", + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_port_wwn[0], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_port_wwn[1], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_port_wwn[2], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_port_wwn[3], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_port_wwn[4], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_port_wwn[5], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_port_wwn[6], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_port_wwn[7]); + (void) strcat(temp_path, sbuf); + + } + /* + * If we find a device on this loop in this box + * update its status. + */ + if (state->ib_status.sel_id == select_id) { + /* + * Found a device on this loop in this box. + * + * Update state. + */ + (void) sprintf(state->g_disk_state.node_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[0], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[1], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[2], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[3], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[4], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[5], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[6], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[7]); + + if (strstr(path, SCSI_VHCI) != NULL) { + (void) g_ll_to_str(map_ptr->map.dev_addr[j].gfc_port_dev. + priv_port.sf_node_wwn, wwnp); + for (wwnlp = wwn_list; wwnlp != NULL; + wwnlp = wwnlp->wwn_next) { + if (strcmp(wwnlp->node_wwn_s, wwnp) == 0) { + (void) strcpy(temp_path, wwnlp->physical_path); + break; + } + } + if (wwnlp == NULL) { + (void) sprintf(sbuf, + "g%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x:c,raw", + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[0], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[1], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[2], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[3], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[4], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[5], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[6], + map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. + sf_node_wwn[7]); + (void) strcat(temp_path, sbuf); + /* + * check to make sure this is a valid path. + * Paths may not always be created on the + * host. So, we make a quick check. + */ + if (stat(temp_path, &stat_buf) == -1) { + free_mp_dev_map(&map_mp); + return (errno); + } + + } + } + (void) strcpy(state->g_disk_state.physical_path, + temp_path); + + + /* Bad if WWN is all zeros. */ + if (is_null_wwn(map_ptr->map.dev_addr[j]. + gfc_port_dev.priv_port. + sf_node_wwn)) { + state->l_state_flag = L_INVALID_WWN; + G_DPRINTF(" l_get_node_status: " + "Disk state was " + " Invalid WWN.\n"); + (*found_flag)++; + free_mp_dev_map(&map_mp); + return (0); + } + + /* get device status */ + if (err = l_get_disk_status(temp_path, state, + wwn_list, verbose)) { + free_mp_dev_map(&map_mp); + return (err); + } + /* + * found device in map. Don't need to look + * any further + */ + (*found_flag)++; + free_mp_dev_map(&map_mp); + 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 + * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@wWWN,0:0 + * then creates a path that looks like + * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ssd@ + */ + (void) strcpy(temp_path, path); + if ((char_ptr = strrchr(temp_path, '/')) == NULL) { + free_mp_dev_map(&map_mp); + return (L_INVALID_PATH); + } + *char_ptr = '\0'; /* Terminate sting */ + + if (err = l_get_pid_from_path(path, &map_ptr->map, &path_pid)) { + free_mp_dev_map(&map_mp); + return (err); + } + + /* Now append the ssd string */ + (void) strcat(temp_path, SLSH_DRV_NAME_SSD); + + /* + * 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'. + */ + for (j = 0; j < map_ptr->map.count; j++) { + if (map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. + dev_dtype != DTYPE_ESI) { + select_id = g_sf_alpa_to_switch[map_ptr->map. + dev_addr[j].gfc_port_dev.pub_port.dev_did. + port_id & 0xFF]; + + if (((map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_did.port_id & + AREA_DOMAIN_ID) == + (path_pid & AREA_DOMAIN_ID)) && + (state->ib_status.sel_id == select_id)) { + /* + * Found the device. Update state. + */ + if (strstr(temp_path, SCSI_VHCI) == NULL) { + (void) sprintf(sbuf, + "w%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x,0:c,raw", + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_pwwn.raw_wwn[0], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_pwwn.raw_wwn[1], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_pwwn.raw_wwn[2], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_pwwn.raw_wwn[3], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_pwwn.raw_wwn[4], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_pwwn.raw_wwn[5], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_pwwn.raw_wwn[6], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_pwwn.raw_wwn[7]); + (void) strcat(temp_path, sbuf); + + /* + * Paths for fabric cases may not always + * be created on the host. So, we make a + * quick check. + */ + if (stat(temp_path, &stat_buf) == -1) { + free_mp_dev_map(&map_mp); + return (errno); + } + + (void) sprintf(state-> + g_disk_state.node_wwn_s, + "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_nwwn.raw_wwn[0], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_nwwn.raw_wwn[1], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_nwwn.raw_wwn[2], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_nwwn.raw_wwn[3], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_nwwn.raw_wwn[4], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_nwwn.raw_wwn[5], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_nwwn.raw_wwn[6], + map_ptr->map.dev_addr[j].gfc_port_dev. + pub_port.dev_nwwn.raw_wwn[7]); + + } else { + (void) g_ll_to_str(map_ptr->map.dev_addr[j].gfc_port_dev. + priv_port.sf_node_wwn, wwnp); + for (wwnlp = wwn_list; wwnlp != NULL; + wwnlp = wwnlp->wwn_next) { + if (strcmp(wwnlp->node_wwn_s, wwnp) == 0) { + (void) strcpy(temp_path, wwnlp->physical_path); + break; + } + } + if (wwnlp == NULL) { + (void) sprintf(sbuf, + "w%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x,0:c,raw", + map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. + dev_nwwn.raw_wwn[0], + map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. + dev_nwwn.raw_wwn[1], + map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. + dev_nwwn.raw_wwn[2], + map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. + dev_nwwn.raw_wwn[3], + map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. + dev_nwwn.raw_wwn[4], + map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. + dev_nwwn.raw_wwn[5], + map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. + dev_nwwn.raw_wwn[6], + map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. + dev_nwwn.raw_wwn[7]); + (void) strcat(temp_path, sbuf); + } + } + (void) strcpy(state->g_disk_state.physical_path, + temp_path); + + /* Bad if WWN is all zeros. */ + if (is_null_wwn(map_ptr->map. + dev_addr[j].gfc_port_dev. + pub_port.dev_nwwn. + raw_wwn)) { + state->l_state_flag = + L_INVALID_WWN; + G_DPRINTF( + " l_get_node_status: " + "Disk state was " + " Invalid WWN.\n"); + (*found_flag)++; + free_mp_dev_map(&map_mp); + return (0); + } + + /* get device status */ + if (err = l_get_disk_status(temp_path, + state, wwn_list, verbose)) { + free_mp_dev_map(&map_mp); + return (err); + } + + (*found_flag)++; + free_mp_dev_map(&map_mp); + return (0); + } /* if select_id match */ + } /* if !DTYPE_ESI */ + } /* for loop */ + break; + case FC_TOP_PT_PT: + free_mp_dev_map(&map_mp); + return (L_PT_PT_FC_TOP_NOT_SUPPORTED); + default: + free_mp_dev_map(&map_mp); + return (L_UNEXPECTED_FC_TOPOLOGY); + } /* End of switch on port_topology */ + + } + free_mp_dev_map(&map_mp); + 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 +l_get_individual_state(char *path, + struct l_disk_state_struct *state, Ib_state *ib_state, + int front_flag, struct box_list_struct *box_list, + struct wwn_list_struct *wwn_list, int verbose) +{ +int found_flag = 0, elem_index = 0; +int port_a_flag, err, j; +struct dlist *seslist = NULL; +Bp_elem_st bpf, bpr; +hrtime_t start_time, end_time; + + if ((path == NULL) || (state == NULL) || + (ib_state == NULL) || (box_list == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + start_time = gethrtime(); + + + if ((state->ib_status.code != S_NOT_INSTALLED) && + (state->ib_status.code != S_NOT_AVAILABLE)) { + + /* + * 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 < (int)ib_state->config.enc_num_elem; + j++) { + elem_index++; + if (ib_state->config.type_hdr[j].type == + ELM_TYP_BP) + break; + elem_index += + ib_state->config.type_hdr[j].num; + } + + /* + * check if port A & B of backplane are bypassed. + * If so, do not bother. + */ + if (front_flag) { + bcopy((const void *) + &(ib_state->p2_s.element[elem_index]), + (void *)&bpf, sizeof (bpf)); + + if ((bpf.byp_a_enabled || bpf.en_bypass_a) && + (bpf.byp_b_enabled || bpf.en_bypass_b)) + return (0); + } else { + /* if disk is in rear slot */ + bcopy((const void *) + &(ib_state->p2_s.element[elem_index+1]), + (void *)&bpr, sizeof (bpr)); + + if ((bpr.byp_b_enabled || bpr.en_bypass_b) && + (bpr.byp_a_enabled || bpr.en_bypass_a)) + return (0); + } + + if ((err = l_get_node_status(path, state, + &found_flag, wwn_list, verbose)) != 0) + return (err); + + if (!found_flag) { + if ((err = l_get_allses(path, box_list, + &seslist, 0)) != 0) { + return (err); + } + + if (err = l_get_port(path, &port_a_flag, verbose)) + goto done; + + if (port_a_flag) { + if ((state->ib_status.bypass_a_en && + !(state->ib_status.bypass_b_en)) || + !(state->ib_status.bypass_b_en)) { + while (seslist != NULL && !found_flag) { + if (err = l_get_port( + seslist->dev_path, + &port_a_flag, verbose)) { + goto done; + } + if ((strcmp(seslist->dev_path, + path) != 0) && + !port_a_flag) { + *path = NULL; + (void) strcpy(path, + seslist->dev_path); + if (err = + l_get_node_status(path, + state, &found_flag, + wwn_list, verbose)) { + goto done; + } + } + seslist = seslist->next; + } + } + } else { + if ((state->ib_status.bypass_b_en && + !(state->ib_status.bypass_a_en)) || + !(state->ib_status.bypass_a_en)) { + while (seslist != NULL && !found_flag) { + if (err = l_get_port( + seslist->dev_path, + &port_a_flag, verbose)) { + goto done; + } + if ((strcmp(seslist->dev_path, + path) != 0) && port_a_flag) { + *path = NULL; + (void) strcpy(path, + seslist->dev_path); + if (err = + l_get_node_status(path, + state, &found_flag, + wwn_list, verbose)) { + goto done; + } + } + seslist = seslist->next; + } + } + } + if (!found_flag) { + state->l_state_flag = L_INVALID_MAP; + 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"); + } + + if (seslist != NULL) + (void) g_free_multipath(seslist); + + } + + } else { + G_DPRINTF(" l_get_individual_state: Disk state was %s.\n", + (state->ib_status.code == S_NOT_INSTALLED) ? + "Not Installed" : "Not Available"); + } + + if (getenv("_LUX_T_DEBUG") != NULL) { + end_time = gethrtime(); + (void) fprintf(stdout, " l_get_individual_state:" + "\tTime = %lld millisec\n", + (end_time - start_time)/1000000); + } + + 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. + * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0 + * or + * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@WWN,0:0 + * + * OUTPUT: + * The struct l_state (which was passed in) has the status info + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +int +l_get_status(char *path, struct l_state_struct *l_state, int verbose) +{ +int err = 0, i, count; +L_inquiry inq; +uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; +int al_pa, found_front, found_rear, front_flag, enc_type; +char ses_path_front[MAXPATHLEN]; +char ses_path_rear[MAXPATHLEN]; +Box_list *b_list = NULL; +Box_list *o_list = NULL; +char node_wwn_s[(WWN_SIZE*2)+1]; +uint_t select_id; +hrtime_t start_time, end_time; +WWN_list *wwn_list = NULL; + + if ((path == NULL) || (l_state == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + start_time = gethrtime(); + + G_DPRINTF(" l_get_status: Get Status for enclosure at: " + " %s\n", path); + + /* initialization */ + (void) memset(l_state, 0, sizeof (struct l_state_struct)); + + if (err = g_get_inquiry(path, &inq)) { + return (err); + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && + (!(strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { + return (L_ENCL_INVALID_PATH); + } + + (void) strncpy((char *)l_state->ib_tbl.enclosure_name, + (char *)inq.inq_box_name, sizeof (inq.inq_box_name)); + + /* + * Get all of the IB Receive Diagnostic pages. + */ + if (err = l_get_ib_status(path, l_state, verbose)) { + 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. + */ + if (err = g_get_wwn(path, port_wwn, node_wwn, &al_pa, verbose)) { + return (err); + } + if (err = l_get_box_list(&o_list, verbose)) { + (void) l_free_box_list(&o_list); + return (err); /* Failure */ + } + + found_front = found_rear = 0; + for (i = 0; i < WWN_SIZE; i++) { + (void) sprintf(&node_wwn_s[i << 1], "%02x", node_wwn[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. + */ + select_id = g_sf_alpa_to_switch[al_pa & 0xFF]; + l_state->ib_tbl.box_id = (select_id & BOX_ID_MASK) >> 5; + + G_DPRINTF(" l_get_status: Using this select_id 0x%x " + "and node WWN %s\n", + select_id, node_wwn_s); + + if (strstr(path, SCSI_VHCI) != NULL) { + /* 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; + (void) strcpy(ses_path_rear, path); + (void) strcpy(ses_path_front, path); + } else { + + if (select_id & ALT_BOX_ID) { + found_rear = 1; + (void) strcpy(ses_path_rear, path); + b_list = o_list; + while (b_list) { + if (strcmp(b_list->b_node_wwn_s, node_wwn_s) == 0) { + if (err = g_get_wwn(b_list->b_physical_path, + port_wwn, node_wwn, + &al_pa, verbose)) { + (void) l_free_box_list(&o_list); + return (err); + } + + /* Take the low order byte of al_pa */ + select_id = g_sf_alpa_to_switch[al_pa & 0xFF]; + if (!(select_id & ALT_BOX_ID)) { + (void) strcpy(ses_path_front, + b_list->b_physical_path); + found_front = 1; + break; + } + } + b_list = b_list->box_next; + } + } else { + (void) strcpy(ses_path_front, path); + found_front = 1; + b_list = o_list; + while (b_list) { + if (strcmp(b_list->b_node_wwn_s, node_wwn_s) == 0) { + if (err = g_get_wwn(b_list->b_physical_path, + port_wwn, node_wwn, + &al_pa, verbose)) { + (void) l_free_box_list(&o_list); + return (err); + } + select_id = g_sf_alpa_to_switch[al_pa & 0xFF]; + if (select_id & ALT_BOX_ID) { + (void) strcpy(ses_path_rear, + b_list->b_physical_path); + found_rear = 1; + break; + } + } + b_list = b_list->box_next; + } + } + } + + if (getenv("_LUX_G_DEBUG") != NULL) { + 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. + */ + if (err = g_get_wwn_list(&wwn_list, verbose)) { + return (err); /* Failure */ + } + + enc_type = l_get_enc_type(inq); + if (found_front) { + front_flag = 1; + for (i = 0, count = 0; i < l_state->total_num_drv/2; + 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); + } + if (err = l_get_individual_state(ses_path_front, + (struct l_disk_state_struct *)&l_state->drv_front[i], + &l_state->ib_tbl, front_flag, o_list, + wwn_list, verbose)) { + (void) l_free_box_list(&o_list); + (void) g_free_wwn_list(&wwn_list); + return (err); + } + } + } else { + /* Set to loop not accessable. */ + for (i = 0; i < l_state->total_num_drv/2; i++) { + l_state->drv_front[i].l_state_flag = L_NO_LOOP; + } + } + /* + * 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 + */ + + if (enc_type == DAK_ENC_TYPE && found_front) { + front_flag = 1; + for (i = 0; i < l_state->total_num_drv/2; i++, count++) { + G_DPRINTF(" l_get_status: Getting individual" + " State for disk in slot %d\n", count); + if (err = l_get_individual_state(ses_path_front, + (struct l_disk_state_struct *)&l_state->drv_rear[i], + &l_state->ib_tbl, front_flag, o_list, + wwn_list, verbose)) { + (void) l_free_box_list(&o_list); + (void) g_free_wwn_list(&wwn_list); + return (err); + } + } + } else if (enc_type != DAK_ENC_TYPE && found_rear) { + for (i = 0; i < l_state->total_num_drv/2; i++, count++) { + G_DPRINTF(" l_get_status: Getting individual" + " State for rear disk in slot %d\n", i); + if (err = l_get_individual_state(ses_path_rear, + (struct l_disk_state_struct *)&l_state->drv_rear[i], + &l_state->ib_tbl, front_flag, o_list, + wwn_list, verbose)) { + (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. */ + for (i = 0; i < l_state->total_num_drv/2; i++) { + l_state->drv_rear[i].l_state_flag = L_NO_LOOP; + } + } + + (void) l_free_box_list(&o_list); + (void) g_free_wwn_list(&wwn_list); + if (getenv("_LUX_T_DEBUG") != NULL) { + end_time = gethrtime(); + (void) fprintf(stdout, " l_get_status: " + "Time = %lld millisec\n", + (end_time - start_time)/1000000); + } + + 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 +check_file(int fd, int verbose, uchar_t **buf_ptr, int dl_info_offset) +{ +struct exec the_exec; +int temp, i, j, *p, size, *start; +uchar_t *buf; +char *date_str; +struct dl_info *dl_info; + + *buf_ptr = NULL; + + /* read exec header */ + if (lseek(fd, 0, SEEK_SET) == -1) + return (errno); + if ((temp = read(fd, (char *)&the_exec, sizeof (the_exec))) == -1) { + return (L_DWNLD_READ_HEADER_FAIL); + } + if (temp != sizeof (the_exec)) { + return (L_DWNLD_READ_INCORRECT_BYTES); + } + + if (the_exec.a_text != PROMSIZE) { + return (L_DWNLD_INVALID_TEXT_SIZE); + } + + if (!(buf = (uchar_t *)g_zalloc(PROMSIZE))) + return (L_MALLOC_FAILED); + + if ((temp = read(fd, buf, PROMSIZE)) == -1) { + return (L_DWNLD_READ_ERROR); + } + + if (temp != PROMSIZE) { + return (L_DWNLD_READ_INCORRECT_BYTES); + } + + + + /* check the IB firmware MAGIC */ + dl_info = (struct dl_info *)(unsigned long)(buf + dl_info_offset); + if (dl_info->magic != PROM_MAGIC) { + return (L_DWNLD_BAD_FRMWARE); + } + + /* + * Get the date + */ + + date_str = ctime(&dl_info->datecode); + + if (verbose) { + (void) fprintf(stdout, + MSGSTR(9050, " IB Prom Date: %s"), + date_str); + } + + /* + * verify checksum + */ + + if (dl_info_offset == FPM_DL_INFO) { + start = (int *)(long)(buf + FPM_OFFSET); + size = FPM_SZ; + } else { + start = (int *)(long)buf; + size = TEXT_SZ + IDATA_SZ; + } + + for (j = 0, p = start, i = 0; i < (size/ 4); i++, j ^= *p++); + + if (j != 0) { + return (L_DWNLD_CHKSUM_FAILED); + } + + /* file verified */ + *buf_ptr = buf; + + return (0); +} + +/* + * Check the DPM file for validity: + * + * RETURNS: + * 0 O.K. + * non-zero otherwise + */ +#define dakstring "64616B74617269" +#define dakoffs "BFC00000" + +static int +check_dpm_file(int fd) +{ + struct s3hdr { + char rtype[2]; + char rlen[2]; + char data[255]; + } theRec; + int nread; + int reclen; + + if (fd < 0) { + return (L_DWNLD_READ_ERROR); + } + lseek(fd, 0, SEEK_SET); + + /* First record */ + memset((void*)&theRec, 0, sizeof (struct s3hdr)); + nread = read(fd, (void *)&theRec, 4); + if (nread != 4) { + /* error reading first record/length */ + return (L_DWNLD_READ_ERROR); + } + if (strncmp((char *)&theRec.rtype[0], "S0", 2) != 0) { + /* error in first record type */ + return (L_DWNLD_READ_HEADER_FAIL); + } + reclen = strtol(&theRec.rlen[0], (char **)NULL, 16); + if (reclen == 0) { + /* error in length == 0 */ + return (L_DWNLD_READ_HEADER_FAIL); + } + nread = read(fd, (void *)&theRec.data[0], ((reclen*2) +1)); + if (nread != ((reclen*2) +1)) { + /* error in trying to read data */ + return (L_DWNLD_READ_HEADER_FAIL); + } + if (strncmp(&theRec.data[4], dakstring, 14) != 0) { + /* error in compiled file name */ + return (L_DWNLD_READ_HEADER_FAIL); + } + + /* Second record */ + memset((void*)&theRec, 0, sizeof (struct s3hdr)); + nread = read(fd, (void *)&theRec, 4); + if (nread != 4) { + /* error reading second record/length */ + return (L_DWNLD_READ_ERROR); + } + if (strncmp((char *)&theRec.rtype[0], "S3", 2) != 0) { + /* error in second record type */ + return (L_DWNLD_READ_HEADER_FAIL); + } + reclen = strtol(&theRec.rlen[0], (char **)NULL, 16); + if (reclen == 0) { + /* error in length == 0 */ + return (L_DWNLD_READ_HEADER_FAIL); + } + nread = read(fd, (void *)&theRec.data[0], ((reclen*2) +1)); + if (nread != ((reclen*2) +1)) { + /* error in trying to read data */ + return (L_DWNLD_READ_HEADER_FAIL); + } + if (strncmp(&theRec.data[0], dakoffs, 8) != 0) { + /* error in SSC100 offset pointer */ + return (L_DWNLD_READ_HEADER_FAIL); + } + lseek(fd, 0, SEEK_SET); + return (0); +} + + + +int +l_check_file(char *file, int verbose) +{ +int file_fd; +int err; +uchar_t *buf; + + if ((file_fd = g_object_open(file, O_RDONLY)) == -1) { + return (L_OPEN_PATH_FAIL); + } + err = check_file(file_fd, verbose, &buf, FW_DL_INFO); + 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 +ib_download_code_cmd(int fd, int promid, int off, uchar_t *buf_ptr, + int buf_len, int sp) +{ +int status, sz; + + while (buf_len) { + sz = MIN(256, buf_len); + buf_len -= sz; + status = g_scsi_writebuffer_cmd(fd, off, buf_ptr, sz, + (sp) ? 3 : 2, promid); + if (status) + return (status); + buf_ptr += sz; + off += sz; + } + + return (status); +} + +/* + * + * Downloads the code to the DAKTARI/DPM with the hdr set correctly + * + * + * 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 +dak_download_code_cmd(int fd, uchar_t *buf_ptr, int buf_len) +{ + int status = 0; + int sz = 0; + int offs = 0; + + while (buf_len > 0) { + sz = MIN(256, buf_len); + buf_len -= sz; + status = g_scsi_writebuffer_cmd(fd, offs, buf_ptr, sz, 0x07, 0); + if (status != 0) { + return (status); + } + buf_ptr += sz; + offs += sz; + } + 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 +l_download(char *path_phys, char *file, int ps, int verbose) +{ +int file_fd, controller_fd; +int err, status; +uchar_t *buf_ptr; +char printbuf[MAXPATHLEN]; +int retry; +char file_path[MAXPATHLEN]; +struct stat statbuf; +int enc_type; +L_inquiry inq; + + if (path_phys == NULL) { + return (L_INVALID_PATH_FORMAT); + } + + if (!file) { + (void) strcpy(file_path, IBFIRMWARE_FILE); + } else { + (void) strncpy(file_path, file, sizeof (file_path)); + } + if (verbose) + (void) fprintf(stdout, "%s\n", + MSGSTR(9051, " Opening the IB for I/O.")); + + if ((controller_fd = g_object_open(path_phys, O_NDELAY | O_RDWR)) == -1) + return (L_OPEN_PATH_FAIL); + + (void) sprintf(printbuf, MSGSTR(9052, " Doing download to:" + "\n\t%s.\n From file: %s."), path_phys, file_path); + + if (verbose) + (void) fprintf(stdout, "%s\n", printbuf); + P_DPRINTF(" Doing download to:" + "\n\t%s\n From file: %s\n", path_phys, file_path); + + if ((file_fd = g_object_open(file_path, O_NDELAY | O_RDONLY)) == -1) { + /* + * Return a different error code here to differentiate between + * this failure in g_object_open() and the one above. + */ + return (L_INVALID_PATH); + } + + if (g_scsi_inquiry_cmd(controller_fd, (uchar_t *)&inq, sizeof (inq))) { + return (L_SCSI_ERROR); + } + enc_type = l_get_enc_type(inq); + 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 */ + if ((err = check_dpm_file(file_fd))) { + return (err); + } + /* Now go ahead and load up the data */ + if (fstat(file_fd, &statbuf) == -1) { + err = errno; + (void) fprintf(stdout, "%s %s\n", + MSGSTR(9101, " Stat'ing the F/W file:"), strerror(err)); + return (L_OPEN_PATH_FAIL); + } + buf_ptr = (uchar_t *)g_zalloc(statbuf.st_size); + if (buf_ptr == NULL) { + err = errno; + (void) fprintf(stdout, "%s %s\n", + MSGSTR(9102, " Cannot alloc mem to read F/W file:"), + strerror(err)); + return (L_MALLOC_FAILED); + } + if (read(file_fd, buf_ptr, statbuf.st_size) == -1) { + err = errno; + (void) fprintf(stdout, "%s %s\n", + MSGSTR(9103, " Reading F/W file:"), strerror(err)); + g_destroy_data((char *)buf_ptr); + return (L_DWNLD_READ_ERROR); + } + break; + default: + if (err = check_file(file_fd, verbose, &buf_ptr, FW_DL_INFO)) { + if (buf_ptr) { + (void) g_destroy_data((char *)buf_ptr); + return (err); + } + } + break; + } + + if (verbose) { + (void) fprintf(stdout, " "); + (void) fprintf(stdout, MSGSTR(127, "Checkfile O.K.")); + (void) fprintf(stdout, "\n"); + } + P_DPRINTF(" Checkfile OK.\n"); + (void) close(file_fd); + + if (verbose) { + (void) fprintf(stdout, MSGSTR(9053, + " Verifying the IB is available.\n")); + } + + retry = DOWNLOAD_RETRIES; + while (retry) { + if ((status = g_scsi_tur(controller_fd)) == 0) { + 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) + (void) fprintf(stdout, "%s\n", + MSGSTR(9054, " Writing new text image to IB.")); + P_DPRINTF(" Writing new image to IB\n"); + switch (enc_type) { + case DAK_ENC_TYPE: + status = dak_download_code_cmd(controller_fd, buf_ptr, + statbuf.st_size); + if (status != 0) { + if (buf_ptr != NULL) { + g_destroy_data((char *)buf_ptr); + } + (void) close(controller_fd); + return (status); + } + break; + default: + status = ib_download_code_cmd(controller_fd, IBEEPROM, TEXT_OFFSET, + (uchar_t *)(buf_ptr + TEXT_OFFSET), TEXT_SZ, ps); + if (status) { + (void) close(controller_fd); + (void) g_destroy_data((char *)buf_ptr); + return (status); + } + if (verbose) { + (void) fprintf(stdout, "%s\n", + MSGSTR(9055, " Writing new data image to IB.")); + } + status = ib_download_code_cmd(controller_fd, IBEEPROM, IDATA_OFFSET, + (uchar_t *)(buf_ptr + IDATA_OFFSET), IDATA_SZ, ps); + if (status) { + (void) close(controller_fd); + (void) g_destroy_data((char *)buf_ptr); + return (status); + } + break; + } + + + if (verbose) { + (void) fprintf(stdout, MSGSTR(9056, + " Re-verifying the IB is available.\n")); + } + + retry = DOWNLOAD_RETRIES; + while (retry) { + if ((status = g_scsi_tur(controller_fd)) == 0) { + 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) { + (void) fprintf(stdout, "%s\n", + MSGSTR(9057, " Writing new image to FPM.")); + } + status = ib_download_code_cmd(controller_fd, MBEEPROM, FPM_OFFSET, + (uchar_t *)(buf_ptr + FPM_OFFSET), FPM_SZ, ps); + break; + } + + if ((!status) && ps) { + /* + * Reset the IB + */ + status = g_scsi_reset(controller_fd); + } + + (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 +l_set_wwn(char *path_phys, char *wwn) +{ +Page4_name page4; +L_inquiry inq; +int fd, status; +char wwnp[WWN_SIZE]; + + (void) memset(&inq, 0, sizeof (inq)); + (void) memset(&page4, 0, sizeof (page4)); + + if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) { + return (L_OPEN_PATH_FAIL); + } + /* Verify it is a Photon */ + if (status = g_scsi_inquiry_cmd(fd, + (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { + (void) close(fd); + return (status); + } + if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && + (!(strncmp((char *)inq.inq_vid, "SUN ", + sizeof (inq.inq_vid)) && + ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { + (void) close(fd); + return (L_ENCL_INVALID_PATH); + } + + page4.page_code = L_PAGE_4; + page4.page_len = (ushort_t)((sizeof (struct page4_name) - 4)); + page4.string_code = L_WWN; + page4.enable = 1; + if (g_string_to_wwn((uchar_t *)wwn, (uchar_t *)&page4.name)) { + close(fd); + return (EINVAL); + } + bcopy((void *)wwnp, (void *)page4.name, (size_t)WWN_SIZE); + + if (status = g_scsi_send_diag_cmd(fd, (uchar_t *)&page4, + sizeof (page4))) { + (void) close(fd); + return (status); + } + + /* + * Check the wwn really changed. + */ + bzero((char *)page4.name, 32); + if (status = g_scsi_rec_diag_cmd(fd, (uchar_t *)&page4, + sizeof (page4), L_PAGE_4)) { + (void) close(fd); + return (status); + } + if (bcmp((char *)page4.name, wwnp, WWN_SIZE)) { + (void) close(fd); + return (L_WARNING); + } + + (void) close(fd); + 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 +l_get_ses_path(char *path_phys, char *ses_path, gfc_map_t *map, + int verbose) +{ +char *char_ptr, id_buf[MAXPATHLEN], wwn[20]; +uchar_t t_wwn[20], *ses_wwn, *ses_wwn1, *ses_nwwn; +int j, al_pa, al_pa1, box_id, fd, disk_flag = 0; +int err, found = 0; +gfc_port_dev_info_t *dev_addr_ptr; + + if ((path_phys == NULL) || (ses_path == NULL) || (map == NULL)) { + return (L_NO_SES_PATH); + } + + (void) strcpy(ses_path, path_phys); + if ((char_ptr = strrchr(ses_path, '/')) == NULL) { + return (L_INVLD_PATH_NO_SLASH_FND); + } + disk_flag++; + *char_ptr = '\0'; /* Terminate sting */ + (void) strcat(ses_path, SLSH_SES_NAME); + + /* + * 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. + */ + if ((char_ptr = strrchr(path_phys, '@')) == NULL) { + return (L_INVLD_PATH_NO_ATSIGN_FND); + } + char_ptr++; /* point to the loop identifier */ + + if ((err = g_get_wwn(path_phys, t_wwn, t_wwn, + &al_pa, verbose)) != 0) { + return (err); + } + box_id = g_sf_alpa_to_switch[al_pa & 0xFF] & BOX_ID_MASK; + + switch (map->hba_addr.port_topology) { + case FC_TOP_PRIVATE_LOOP: + for (j = 0, dev_addr_ptr = map->dev_addr; + j < map->count; j++, dev_addr_ptr++) { + if (dev_addr_ptr->gfc_port_dev.priv_port. + sf_inq_dtype == DTYPE_ESI) { + al_pa1 = dev_addr_ptr->gfc_port_dev. + priv_port.sf_al_pa; + if (box_id == (g_sf_alpa_to_switch[al_pa1] & + BOX_ID_MASK)) { + if (!found) { + ses_wwn = dev_addr_ptr-> + gfc_port_dev.priv_port.sf_port_wwn; + ses_nwwn = dev_addr_ptr-> + gfc_port_dev.priv_port.sf_node_wwn; + 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-> + gfc_port_dev.priv_port.sf_port_wwn; + 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_FABRIC: + case FC_TOP_PUBLIC_LOOP: + for (j = 0, dev_addr_ptr = map->dev_addr; + j < map->count; j++, dev_addr_ptr++) { + if (dev_addr_ptr->gfc_port_dev.pub_port.dev_dtype == + 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 + */ + al_pa1 = dev_addr_ptr->gfc_port_dev. + pub_port.dev_did.port_id; + 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 == + (g_sf_alpa_to_switch[al_pa1 & + 0xFF] & BOX_ID_MASK)) { + if (!found) { + ses_wwn = dev_addr_ptr-> + gfc_port_dev.pub_port. + dev_pwwn.raw_wwn; + ses_nwwn = dev_addr_ptr-> + gfc_port_dev.pub_port. + dev_nwwn.raw_wwn; + 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-> + gfc_port_dev.pub_port. + dev_pwwn.raw_wwn; + 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); + } + + if (strstr(path_phys, SCSI_VHCI) != NULL) { + (void) g_ll_to_str(ses_nwwn, wwn); + (void) sprintf(id_buf, "g%s:0", wwn); + } else { + (void) g_ll_to_str(ses_wwn, wwn); + (void) sprintf(id_buf, "w%s,0:0", wwn); + } + (void) strcat(ses_path, id_buf); + if (verbose) { + (void) fprintf(stdout, + MSGSTR(9058, " Creating enclosure path:\n %s\n"), + ses_path); + } + + /* + * see if these paths exist. + */ + if ((fd = g_object_open(ses_path, O_NDELAY | O_RDONLY)) == -1) { + + if (strstr(path_phys, SCSI_VHCI) != NULL) { + return (L_INVALID_PATH); + } + + char_ptr = strrchr(ses_path, '/'); + *char_ptr = '\0'; + (void) strcat(ses_path, SLSH_SES_NAME); + if (found > 1) { + (void) g_ll_to_str(ses_wwn1, wwn); + P_DPRINTF(" l_get_ses_path: " + "Using second path, ses wwn1 = %s\n", + wwn); + (void) sprintf(id_buf, "w%s,0:0", wwn); + strcat(ses_path, id_buf); + return (0); + } else { + return (L_NO_SES_PATH); + } + } + close(fd); + return (0); +} + + + +/* + * Get a valid location, front/rear & slot. + * + * 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 +l_get_slot(struct path_struct *path_struct, L_state *l_state, int verbose) +{ +int err, al_pa, slot, found = 0; +uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; +uint_t select_id; + + if ((path_struct == NULL) || (l_state == NULL)) { + 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 */ + assert(path_struct->ib_path_flag == 0); + + if (strstr(path_struct->p_physical_path, "ssd") == NULL) { + return (L_INVLD_PHYS_PATH_TO_DISK); + } + if (err = g_get_wwn(path_struct->p_physical_path, port_wwn, node_wwn, + &al_pa, verbose)) { + 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 + */ + select_id = g_sf_alpa_to_switch[al_pa & 0xFF]; + P_DPRINTF(" l_get_slot: Searching Receive Diagnostic page 2, " + "to find the slot number with this ID:0x%x\n", + select_id); + + for (slot = 0; slot < l_state->total_num_drv/2; slot++) { + if (l_state->drv_front[slot].ib_status.sel_id == + select_id) { + path_struct->f_flag = 1; + found = 1; + break; + } else if (l_state->drv_rear[slot].ib_status.sel_id == + select_id) { + path_struct->f_flag = 0; + found = 1; + break; + } + } + if (!found) { + return (L_INVALID_SLOT); /* Failure */ + } + if ((strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_OFF_NAME, + strlen(DAK_OFF_NAME)) == 0) || + (strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_PROD_STR, + strlen(DAK_OFF_NAME)) == 0)) { + P_DPRINTF(" l_get_slot: Found slot %d.\n", + path_struct->f_flag ? slot : slot + (MAX_DRIVES_DAK/2)); + } else { + P_DPRINTF(" l_get_slot: Found slot %d %s.\n", slot, + path_struct->f_flag ? "Front" : "Rear"); + } + path_struct->slot = slot; + path_struct->slot_valid = 1; + return (0); +} + + +void +l_element_msg_string(uchar_t code, char *es) +{ + if (code == S_OK) { + (void) sprintf(es, MSGSTR(29, "O.K.")); + } else if (code == S_NOT_AVAILABLE) { + (void) sprintf(es, MSGSTR(34, "Disabled")); + } else if (code == S_NOT_INSTALLED) { + (void) sprintf(es, MSGSTR(30, "Not Installed")); + } else if (code == S_NONCRITICAL) { + (void) sprintf(es, MSGSTR(9059, "Noncritical failure")); + } else if (code == S_CRITICAL) { + (void) sprintf(es, MSGSTR(122, "Critical failure")); + } else { + (void) sprintf(es, MSGSTR(4, "Unknown status")); + } +} + + +/* + * 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 +l_get_allses(char *path, struct box_list_struct *box_list, + struct dlist **ses_list, int verbose) +{ +struct box_list_struct *box_list_ptr; +char node_wwn_s[WWN_S_LEN]; +struct dlist *dlt, *dl; + + if ((path == NULL) || (box_list == NULL) || (ses_list == NULL)) { + return (L_INVALID_PATH_FORMAT); + } + + /* Initialize lists/arrays */ + *ses_list = dlt = dl = (struct dlist *)NULL; + node_wwn_s[0] = '\0'; + + H_DPRINTF(" l_get_allses: Looking for all ses paths for" + " box at path: %s\n", path); + + for (box_list_ptr = box_list; box_list_ptr != NULL; + box_list_ptr = box_list_ptr->box_next) { + H_DPRINTF(" l_get_allses: physical_path= %s\n", + box_list_ptr->b_physical_path); + if (strcmp(path, box_list_ptr->b_physical_path) == 0) { + (void) strcpy(node_wwn_s, box_list_ptr->b_node_wwn_s); + break; + } + } + if (node_wwn_s[0] == '\0') { + H_DPRINTF("node_wwn_s is NULL!\n"); + return (L_NO_NODE_WWN_IN_BOXLIST); + } + H_DPRINTF(" l_get_allses: node_wwn=%s\n", node_wwn_s); + for (box_list_ptr = box_list; box_list_ptr != NULL; + box_list_ptr = box_list_ptr->box_next) { + if (strcmp(node_wwn_s, box_list_ptr->b_node_wwn_s) == 0) { + if ((dl = (struct dlist *) + g_zalloc(sizeof (struct dlist))) == NULL) { + while (*ses_list != NULL) { + dl = dlt->next; + (void) g_destroy_data(dlt); + dlt = dl; + } + return (L_MALLOC_FAILED); + } + H_DPRINTF(" l_get_allses: Found ses=%s\n", + box_list_ptr->b_physical_path); + dl->dev_path = strdup(box_list_ptr->b_physical_path); + dl->logical_path = strdup(box_list_ptr->logical_path); + if (*ses_list == NULL) { + *ses_list = dlt = dl; + } else { + dlt->next = dl; + dl->prev = dlt; + dlt = dl; + } + } + } + + 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: + * + * Types are defined in storage/libg_fc/common/hdrs/g_state.h: + * + * 0 -> default (SENA) + * 1 -> Daktari + * 2 -> Other Enclosures + * + */ +int +l_get_enc_type(L_inquiry inq) +{ + if (strncmp((char *)&inq.inq_pid[0], ENCLOSURE_PROD_ID, + strlen(ENCLOSURE_PROD_ID)) == 0) { + return (SENA_ENC_TYPE); + } + if (strncmp((char *)&inq.inq_pid[0], DAK_OFF_NAME, + strlen(DAK_OFF_NAME)) == 0) { + return (DAK_ENC_TYPE); + } + if (strncmp((char *)&inq.inq_pid[0], DAK_PROD_STR, + strlen(DAK_PROD_STR)) == 0) { + return (DAK_ENC_TYPE); + } + /* + * ADD OTHERS here if ever needed/wanted, and add to def's + * as noted above + */ + return (UNDEF_ENC_TYPE); +} + +void +free_mp_dev_map(gfc_map_mp_t **map_mp_ptr) { + gfc_map_mp_t *next = NULL; + + for (; *map_mp_ptr != NULL; *map_mp_ptr = next) { + next = (*map_mp_ptr)->map_next; + (void) g_destroy_data((*map_mp_ptr)->map.dev_addr); + (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 +get_mp_dev_map(char *path, gfc_map_mp_t **map_mp_ptr, int verbose) { + + int pathcnt, i, err; + mp_pathlist_t pathlist; + gfc_map_mp_t *new_map_mp_ptr; + char drvr_path[MAXPATHLEN]; + if (strstr(path, SCSI_VHCI)) { + if (g_get_pathlist(path, &pathlist)) { + return (L_INVALID_PATH); + } + pathcnt = pathlist.path_count; + for (i = 0; i < pathcnt; i++) { + if (pathlist.path_info[i].path_state < MAXPATHSTATE) { + /* + * only pay attention to paths that are either + * ONLINE or STANDBY + */ + if ((pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_ONLINE) || + (pathlist.path_info[i].path_state == + MDI_PATHINFO_STATE_STANDBY)) { + if ((new_map_mp_ptr = (gfc_map_mp_t *) + g_zalloc(sizeof (gfc_map_mp_t))) + == NULL) { + free(pathlist.path_info); + free_mp_dev_map(map_mp_ptr); + return (L_MALLOC_FAILED); + } + (void) strcpy(drvr_path, + pathlist.path_info[i].path_hba); + (void) strcat(drvr_path, FC_CTLR); + if (err = g_get_dev_map(drvr_path, + &(new_map_mp_ptr->map), + verbose)) { + free(pathlist.path_info); + free_mp_dev_map(map_mp_ptr); + return (err); + } + /* add newly created map onto list */ + if (*map_mp_ptr == NULL) { + new_map_mp_ptr->map_next = NULL; + *map_mp_ptr = new_map_mp_ptr; + } else { + new_map_mp_ptr->map_next = + *map_mp_ptr; + *map_mp_ptr = new_map_mp_ptr; + } + } + } + } + free(pathlist.path_info); + } else { + if ((new_map_mp_ptr = (gfc_map_mp_t *)g_zalloc + (sizeof (gfc_map_mp_t))) == NULL) { + return (L_MALLOC_FAILED); + } + g_get_dev_map(path, &(new_map_mp_ptr->map), verbose); + *map_mp_ptr = new_map_mp_ptr; + } + return (0); +} |