root/usr/src/lib/storage/libg_fc/common/io.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*LINTLIBRARY*/

/*
 *
 *      This module is part of the photon Command Line
 *      Interface program.
 *
 */

/*
 * I18N message number ranges
 *  This file: 11500 - 11999
 *  Shared common messages: 1 - 1999
 */

/* #define              _POSIX_SOURCE 1 */

/*      Includes        */
#include        <stdlib.h>
#include        <stdio.h>
#include        <string.h>
#include        <sys/file.h>
#include        <sys/types.h>
#include        <fcntl.h>
#include        <sys/sunddi.h>
#include        <sys/systm.h>
#include        <sys/scsi/scsi.h>
#include        <nl_types.h>
#include        <unistd.h>
#include        <l_common.h>
#include        <stgcom.h>
#include        <l_error.h>
#include        <g_state.h>
#include        <errno.h>
#include        <devid.h>
#include        <libdevinfo.h>


/*      Defines         */
/* Because of a bug in Unisys Envsen card,  Bug ID:1266986. */
#define SCSI_ESI_PCV    0x01            /* Page Code Valid */
#define SCSI_ESI_PF     0x10            /* Page Format */
#define ACTION_MASK     0x1f            /* Persistent Reserve In command */
#define IMMED           1               /* make the stop immediate */
#define DAK_PROD_STR    "SUNWGS INT FCBPL"
#define DAK_BOXNAME_LEN 16              /* The length of the daktari boxname */
#define DAK_BOXNAME_OFF 36              /* The offset of the daktari boxname */



/*      Global variables        */
extern  nl_catd l_catd;


/*      Forward declarations    */
static int scsi_read_capacity_16_cmd(int, struct scsi_capacity_16 *, int);


/*      External functions      */


int
g_scsi_persistent_reserve_in_cmd(int fd, uchar_t *buf_ptr,
        int buf_len, uchar_t action)
{
struct uscsi_cmd        ucmd;
my_cdb_g1       cdb = {SCMD_PERS_RESERV_IN, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        (void) memset(buf_ptr, 0, buf_len);
        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        cdb.byte1 = action & ACTION_MASK;
        cdb.byte7 = (buf_len>>8) & 0xff;
        cdb.byte8 = buf_len & 0xff;
        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP1;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;

        if (buf_len & 0x03) {
                return (L_PR_INVLD_TRNSFR_LEN);
        }
        /* Do in SILENT mode as cmd may not be supported. */
        return (cmd(fd, &ucmd, USCSI_READ | USCSI_SILENT));
}
/*
 *      Send Diagnostic command
 *
 *      NOTE: This function includes a delay.
 */
int
g_scsi_send_diag_cmd(int fd, uchar_t *buf_ptr, int buf_len)
{
struct uscsi_cmd        ucmd;
uchar_t cdb[] = {SCMD_SDIAG, SCSI_ESI_PF, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;
int             err;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        cdb[3] = (buf_len>>8) & 0xff;
        cdb[4] = buf_len & 0xff;
        ucmd.uscsi_cdb = (caddr_t)cdb;
        ucmd.uscsi_cdblen = CDB_GROUP0;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;

        if (err = cmd(fd, &ucmd, USCSI_WRITE)) {
                return (err);
        }
        /*
         * Allow time for things to stabilize.
         */
        sleep(5);
        return (0);
}

/*
 * Internal routine to allow manipulation of the cdb[1] byte
 * in receive diag.
 */
static int
rec_diag_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t page_code,
        uchar_t cdb_one)
{
struct uscsi_cmd        ucmd;
uchar_t cdb[] = {SCMD_GDIAG, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        (void) memset(buf_ptr, 0, buf_len);
        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        cdb[1] = cdb_one;
        cdb[2] = page_code;
        cdb[3] = (buf_len>>8) & 0xff;
        cdb[4] = buf_len & 0xff;
        ucmd.uscsi_cdb = (caddr_t)cdb;
        ucmd.uscsi_cdblen = CDB_GROUP0;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;
        return (cmd(fd, &ucmd, USCSI_READ));
}


/*
 *      Receive Diagnostic command
 */
int
g_scsi_rec_diag_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t page_code)
{
int     status;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        if (buf_len & 0x03) {
                return (L_RD_INVLD_TRNSFR_LEN);
        }

        /*
         * The a5k and newer enclosures abide by the SCSI spec
         * (SPC-2: 7.15) but the SSA does not.  It requires
         * 0x10 to be present in cdb[1].
         *
         * For enclosures that abide by the spec, the first call
         * will work.  For SSAs the first call will fail, at which
         * point we try again with the SSA specific value.
         */
        status = rec_diag_cmd(fd, buf_ptr, buf_len, page_code, SCSI_ESI_PCV);
        if (status != 0) {
            status = rec_diag_cmd(fd, buf_ptr, buf_len, page_code, SCSI_ESI_PF);
        }
        return (status);
}

/*
 *              Write buffer command set up to download firmware
 */
int
g_scsi_writebuffer_cmd(int fd, int off, uchar_t *buf_ptr, int buf_len,
                                int sp, int bid)
{
struct uscsi_cmd        ucmd;
my_cdb_g1       cdb = {SCMD_WRITE_BUFFER, 0x4, 0, 0, 0, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        cdb.byte1 |= sp;                /* set the save bit */
        cdb.byte2 = (char)(bid & 0xff);
        cdb.byte3 = off>>16;    /* bytes 3-5 contain file offset */
        cdb.byte4 = (off>>8) & 0xff;
        cdb.byte5 = off & 0xff;
        cdb.byte6 = buf_len>>16;        /* bytes 6-8 contain file length */
        cdb.byte7 = (buf_len>>8) & 0xff;
        cdb.byte8 = buf_len & 0xff;
        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP1;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 240;       /* long timeout required */

        return (cmd(fd, &ucmd, USCSI_WRITE));
}

/*
 *      Read buffer command set up to upload firmware
 *      Reads from code image starting at offset
 *      "code_off" for "buf_len" bytes.
 */
int
g_scsi_readbuffer_cmd(int fd, uchar_t *buf_ptr, int buf_len, int code_off)
{
struct uscsi_cmd        ucmd;
my_cdb_g1       cdb = {SCMD_READ_BUFFER, 0x5, 0, 0, 0, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        cdb.byte3 = (code_off >> 16) & 0xff;
        cdb.byte4 = (code_off >> 8) & 0xff;
        cdb.byte5 = code_off & 0xff;
        cdb.byte6 = buf_len>>16;        /* bytes 6-8 contain file length */
        cdb.byte7 = (buf_len>>8) & 0xff;
        cdb.byte8 = buf_len & 0xff;
        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP1;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 120;

        return (cmd(fd, &ucmd, USCSI_READ));
}

int
g_scsi_inquiry_cmd(int fd, uchar_t *buf_ptr, int buf_len)
{
struct uscsi_cmd        ucmd;
my_cdb_g0       cdb = {SCMD_INQUIRY, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;
int     myreturn;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        (void) memset(buf_ptr, 0, buf_len);
        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        cdb.count = (uchar_t)buf_len;
        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP0;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;

        myreturn = cmd(fd, &ucmd, USCSI_READ | USCSI_SILENT);
        if (myreturn) {
            return (myreturn);      /* != 0, error just return */
        }

        /*
         * This is a work around for the format of Daktari's
         * SCSI inquiry page information.  The name of the enclosure
         * is not in the same place that products like the a5000 place it
         * so we have to copy the string to the expected location.
         */
        if (strncmp((char *)&buf_ptr[16], DAK_PROD_STR,
                        strlen(DAK_PROD_STR)) == 0) {
                strncpy((char *)&buf_ptr[96], (char *)&buf_ptr[DAK_BOXNAME_OFF],
                    DAK_BOXNAME_LEN);
        }

        return (myreturn);
}

int
g_scsi_log_sense_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t page_code)
{
struct uscsi_cmd        ucmd;
my_cdb_g1       cdb =  {SCMD_LOG_SENSE, 0, 0x40, 0, 0, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        /* clear buffers on cmds that read data */
        (void) memset(buf_ptr, 0, buf_len);
        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        cdb.byte2 |= page_code;                 /* requested page */
        cdb.byte7 = buf_len>>8;
        cdb.byte8 = buf_len & 0xff;
        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP1;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 120;
        return (cmd(fd, &ucmd, USCSI_READ));
}

/*
 *              MODE SELECT
 *
 *              MODE SELECT USCSI command
 *
 *              sp is the save pages bit  - Must be bit 0 -
 *
 */
int
g_scsi_mode_select_cmd(int fd, uchar_t *buf_ptr, int buf_len, uchar_t sp)
{
struct uscsi_cmd        ucmd;
/* 10 byte Mode Select cmd */
my_cdb_g1       cdb =  {SCMD_MODE_SELECT_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        cdb.byte1 = (sp & 1) | 0x10;            /* 0x10 is the PF bit  */
        cdb.byte7 = buf_len>>8;
        cdb.byte8 = buf_len & 0xff;

        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP1;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 120;

        return (cmd(fd, &ucmd, USCSI_WRITE));
}


/*
 *              MODE SENSE USCSI command
 *
 *
 *              pc = page control field
 *              page_code = Pages to return
 */
int
g_scsi_mode_sense_cmd(int fd,
        uchar_t *buf_ptr,
        int buf_len,
        uchar_t pc,
        uchar_t page_code)
{
struct uscsi_cmd        ucmd;
/* 10 byte Mode Select cmd */
my_cdb_g1       cdb =  {SCMD_MODE_SENSE_G1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;
int             status;
static  int     uscsi_count;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        (void) memset(buf_ptr, 0, buf_len);
        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        /* Just for me  - a sanity check */
        if ((page_code > MODEPAGE_ALLPAGES) || (pc > 3) ||
                (buf_len > MAX_MODE_SENSE_LEN)) {
                return (L_ILLEGAL_MODE_SENSE_PAGE);
        }
        cdb.byte2 = (pc << 6) + page_code;
        cdb.byte7 = buf_len>>8;
        cdb.byte8 = buf_len & 0xff;
        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP1;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 120;

        status = cmd(fd, &ucmd, USCSI_READ);
        /* Bytes actually transfered */
        if (status == 0) {
                uscsi_count = buf_len - ucmd.uscsi_resid;
                S_DPRINTF("  Number of bytes read on "
                        "Mode Sense 0x%x\n", uscsi_count);
                if (getenv("_LUX_D_DEBUG") != NULL) {
                        (void) g_dump("  Mode Sense data: ", buf_ptr,
                        uscsi_count, HEX_ASCII);
                }
        }
        return (status);
}

int
g_scsi_read_capacity_cmd(int fd, uchar_t *buf_ptr, int buf_len)
{
struct uscsi_cmd        ucmd;
my_cdb_g1       cdb = {SCMD_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if ((fd < 0) || (buf_ptr == NULL) || (buf_len < 0)) {
                return (L_INVALID_ARG);
        }

        /* clear buffers on on cmds that read data */
        (void) memset(buf_ptr, 0, buf_len);
        (void) memset((char *)&ucmd, 0, sizeof (ucmd));

        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP1;
        ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;
        return (cmd(fd, &ucmd, USCSI_READ));
}

int
g_scsi_read_capacity_1016_cmd(int fd,
                struct scsi_capacity_16 *cap_ptr, int buf_len)
{
struct uscsi_cmd        ucmd;
my_cdb_g1       cdb = {SCMD_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0};
struct scsi_extended_sense      sense;
struct scsi_capacity    cap_old;
int     ret;

        if ((fd < 0) || (cap_ptr == NULL) ||
                (buf_len < sizeof (struct scsi_capacity_16))) {
                return (L_INVALID_ARG);
        }

        /* clear buffers on on cmds that read data */
        (void) memset((char *)&cap_old, 0, sizeof (cap_old));
        (void) memset((char *)&ucmd, 0, sizeof (ucmd));

        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP1;
        ucmd.uscsi_bufaddr = (caddr_t)&cap_old;
        ucmd.uscsi_buflen = sizeof (cap_old);
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;

        ret = cmd(fd, &ucmd, USCSI_READ);
        if (cap_old.capacity == 0xffffffff) {
                /*
                 * A capacity of 0xffffffff in response to a
                 * READ CAPACITY 10 indicates that the lun
                 * is too large to report the size in a 32 bit
                 * value, and a READ CAPACITY 16 is required
                 * to get the correct size.
                 */
                ret = scsi_read_capacity_16_cmd(fd, cap_ptr, buf_len);
        } else {
                cap_ptr->sc_capacity = cap_old.capacity;
                cap_ptr->sc_lbasize = cap_old.lbasize;
        }
        return (ret);
}

static int
scsi_read_capacity_16_cmd(int fd,
                struct scsi_capacity_16 *cap_ptr, int buf_len)
{
struct uscsi_cmd        ucmd;
union scsi_cdb          cdb;
struct scsi_extended_sense      sense;

        if ((fd < 0) || (cap_ptr == NULL) ||
                (buf_len < sizeof (struct scsi_capacity_16))) {
                return (L_INVALID_ARG);
        }
        /* clear buffers on on cmds that read data */
        (void) memset((char *)cap_ptr, 0, buf_len);
        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));

        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP4;
        ucmd.uscsi_bufaddr = (caddr_t)cap_ptr;
        ucmd.uscsi_buflen = buf_len;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;

        /*
         * Read Capacity (16) is a Service Action In command.  One
         * command byte (0x9E) is overloaded for multiple operations,
         * with the second CDB byte specifying the desired operation
         */
        cdb.scc_cmd = SCMD_SVC_ACTION_IN_G4;
        cdb.cdb_opaque[1] = SSVC_ACTION_READ_CAPACITY_G4;

        /*
         * Fill in allocation length field
         */
        cdb.cdb_opaque[10] =
                (uchar_t)((ucmd.uscsi_buflen & 0xff000000) >> 24);
        cdb.cdb_opaque[11] =
                (uchar_t)((ucmd.uscsi_buflen & 0x00ff0000) >> 16);
        cdb.cdb_opaque[12] =
                (uchar_t)((ucmd.uscsi_buflen & 0x0000ff00) >> 8);
        cdb.cdb_opaque[13] =
                (uchar_t)(ucmd.uscsi_buflen & 0x000000ff);

        return (cmd(fd, &ucmd, USCSI_READ));
}

int
g_scsi_release_cmd(int fd)
{
struct uscsi_cmd        ucmd;
const my_cdb_g0 cdb = {SCMD_RELEASE, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if (fd < 0) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));

        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP0;
        ucmd.uscsi_bufaddr = NULL;
        ucmd.uscsi_buflen = 0;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;
        return (cmd(fd, &ucmd, 0));
}

int
g_scsi_reserve_cmd(int fd)
{
struct uscsi_cmd        ucmd;
const my_cdb_g0 cdb = {SCMD_RESERVE, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if (fd < 0) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));

        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP0;
        ucmd.uscsi_bufaddr = NULL;
        ucmd.uscsi_buflen = 0;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;
        return (cmd(fd, &ucmd, 0));
}

int
g_scsi_start_cmd(int fd)
{
struct uscsi_cmd        ucmd;
/*
 * Use this to induce a SCSI error
 *      const my_cdb_g0 cdb = {SCMD_START_STOP, 0, 0xff, 0, 1, 0};
 */
const my_cdb_g0 cdb = {SCMD_START_STOP, 0, 0, 0, 1, 0};
struct  scsi_extended_sense     sense;

        if (fd < 0) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP0;
        ucmd.uscsi_bufaddr = NULL;
        ucmd.uscsi_buflen = 0;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 240;       /* takes a while to start all */
        return (cmd(fd, &ucmd, 0));
}

int
g_scsi_stop_cmd(int fd, int immediate_flag)
{
struct uscsi_cmd        ucmd;
my_cdb_g0       cdb = {SCMD_START_STOP, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if (fd < 0) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));
        if (immediate_flag) {
                cdb.lba_msb = IMMED;
        }
        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP0;
        ucmd.uscsi_bufaddr = NULL;
        ucmd.uscsi_buflen = 0;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 120;
        return (cmd(fd, &ucmd, 0));
}

int
g_scsi_tur(int fd)
{
struct uscsi_cmd        ucmd;
const my_cdb_g0 cdb = {SCMD_TEST_UNIT_READY, 0, 0, 0, 0, 0};
struct  scsi_extended_sense     sense;

        if (fd < 0) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));

        ucmd.uscsi_cdb = (caddr_t)&cdb;
        ucmd.uscsi_cdblen = CDB_GROUP0;
        ucmd.uscsi_bufaddr = NULL;
        ucmd.uscsi_buflen = 0;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;
        return (cmd(fd, &ucmd, 0));
}

/*
 * NOTE: This function includes a delay.
 */
int
g_scsi_reset(int fd)
{
struct uscsi_cmd        ucmd;
struct  scsi_extended_sense     sense;
int     err;

        if (fd < 0) {
                return (L_INVALID_ARG);
        }

        (void) memset((char *)&ucmd, 0, sizeof (ucmd));

        ucmd.uscsi_cdb = NULL;
        ucmd.uscsi_cdblen = 0;
        ucmd.uscsi_bufaddr = NULL;
        ucmd.uscsi_buflen = 0;
        ucmd.uscsi_rqbuf = (caddr_t)&sense;
        ucmd.uscsi_rqlen = sizeof (struct  scsi_extended_sense);
        ucmd.uscsi_timeout = 60;
        if (err = cmd(fd, &ucmd, USCSI_RESET)) {
                return (err);
        }
        /*
         * Allow time for things to stabilize.
         */
        sleep(20);
        return (0);
}


/*
 * Description:
 *    Retrieves a devid from a device path.
 *
 * Input Values:
 *
 *    devpath: Valid block device path.
 *        Example:/devices/scsi_vhci/ssd@g280000602200416d6257333030303353:c,raw
 *
 *    devid: ptr to ddi_devid_t struct
 *    root: root handle to device tree snapshot
 *    drvr_name: driver name to start the node tree search
 * On success, devid points to device tree handle to devid
 * di_fini on root will invalidate devid pointer
 *
 * Return Value:
 *    0 on success
 *    non-zero on failure
 */
int
g_devid_get(char *devpath, ddi_devid_t *devid, di_node_t root,
                const char *drvr_name)
{
char *cptr;
char rootpath[MAXPATHLEN];
di_node_t node;
char *devfs_path = NULL;
hrtime_t        start_time, end_time;
char *env = NULL;

        if (devpath == NULL || devid == NULL || drvr_name == NULL) {
                return (L_INVALID_ARG);
        }

        if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
                start_time = gethrtime();
        }

        *devid = NULL;
        rootpath[0] = '\0';

        /*
         * Form a valid root path by stripping off the /devices/ mount point
         * prefix and the minor name (:a[,raw]).
         */
        if (strstr(devpath, DEV_PREFIX)) {
                strcat(rootpath, devpath + strlen(DEV_PREFIX) - 1);
                if (strchr(devpath, ':')) {
                        cptr = strrchr(rootpath, ':');
                        *cptr = '\0';
                } else {
                        return (L_INVALID_PATH);
                }
        } else {
                return (L_INVALID_PATH);
        }

        /* point to first node which matches portdrvr */
        node = di_drv_first_node(drvr_name, root);
        if (node == DI_NODE_NIL) {
                /*
                 * Could not find driver node
                 */
                return (L_NO_DEVID);
        }

        while (node != DI_NODE_NIL) {
                if ((devfs_path = di_devfs_path(node)) != NULL) {
                        if (strcmp(rootpath, devfs_path) == 0) {
                                *devid = di_devid(node);
                                di_devfs_path_free(devfs_path);
                                break;
                        }
                        di_devfs_path_free(devfs_path);
                }
                node = di_drv_next_node(node);
        }

        if (env != NULL) {
                end_time = gethrtime();
                (void) fprintf(stdout,
                "      g_devid_get: "
                "\t\tTime = %lld millisec\n",
                (end_time - start_time)/1000000);
        }
        /* Did we get back a handle? */
        if (*devid != NULL) {
                return (0);
        } else { /* Couldn't get a devid. */
                return (L_NO_DEVID);
        }
}