#include <sys/scsi/scsi.h>
#include <sys/modctl.h>
#include <sys/bitmap.h>
#include <sys/fm/protocol.h>
#define FILL_SCSI1_LUN(sd, pkt) \
if ((sd->sd_address.a_lun > 0) && \
(sd->sd_inq->inq_ansi == 0x1)) { \
((union scsi_cdb *)(pkt)->pkt_cdbp)->scc_lun = \
sd->sd_address.a_lun; \
}
extern struct mod_ops mod_miscops;
static struct modlmisc modlmisc = {
&mod_miscops,
"SCSI Bus Utility Routines"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
enum scsi_test_ctxt {
STC_PROBE_FIRST_INQ,
STC_PROBE_FIRST_INQ_RETRY,
STC_PROBE_PARTIAL_SUCCESS,
STC_PROBE_RQSENSE1,
STC_PROBE_CHK_CLEARED,
STC_PROBE_RQSENSE2,
STC_PROBE_INQ_FINAL,
STC_VPD_CHECK,
STC_IDENTITY_PG80,
STC_IDENTITY_PG83,
};
static void create_inquiry_props(struct scsi_device *);
static int scsi_check_ss2_LUN_limit(struct scsi_device *);
static void scsi_establish_LUN_limit(struct scsi_device *);
static void scsi_update_parent_ss2_prop(dev_info_t *, int, int);
static int check_vpd_page_support8083(struct scsi_device *sd,
int (*callback)(), int *, int *);
static int send_scsi_INQUIRY(struct scsi_device *sd,
int (*callback)(), uchar_t *bufaddr, size_t buflen,
uchar_t evpd, uchar_t page_code, size_t *lenp,
enum scsi_test_ctxt);
#define SS2_LUN0_TGT_LIST_PROP "ss2-targets"
typedef struct ss2_lun0_info {
const char *sli_vid;
const char *sli_pid;
const char *sli_rev;
} ss2_lun0_info_t;
#define SES_D1000_VID "SYMBIOS"
#define SES_D1000_PID "D1000"
#define SES_D1000_REV "2"
#define SES_D240_VID "SUN"
#define SES_D240_PID "D240"
#define SES_D240_REV "2"
static const ss2_lun0_info_t scsi_probe_strict_s2_list[] = {
{SES_D1000_VID, SES_D1000_PID, SES_D1000_REV},
{SES_D240_VID, SES_D240_PID, SES_D240_REV},
};
static const int scsi_probe_strict_s2_size =
sizeof (scsi_probe_strict_s2_list) / sizeof (struct ss2_lun0_info);
#ifdef DEBUG
int scsi_probe_debug = 0;
#define SCSI_PROBE_DEBUG0(l, s) \
if (scsi_probe_debug >= (l)) printf(s)
#define SCSI_PROBE_DEBUG1(l, s, a1) \
if (scsi_probe_debug >= (l)) printf(s, a1)
#define SCSI_PROBE_DEBUG2(l, s, a1, a2) \
if (scsi_probe_debug >= (l)) printf(s, a1, a2)
#define SCSI_PROBE_DEBUG3(l, s, a1, a2, a3) \
if (scsi_probe_debug >= (l)) printf(s, a1, a2, a3)
#else
#define SCSI_PROBE_DEBUG0(l, s)
#define SCSI_PROBE_DEBUG1(l, s, a1)
#define SCSI_PROBE_DEBUG2(l, s, a1, a2)
#define SCSI_PROBE_DEBUG3(l, s, a1, a2, a3)
#endif
int scsi_test_busy_timeout = SCSI_POLL_TIMEOUT;
int scsi_test_busy_delay = 10000;
#define SCSI_TEST_CMPLT_GOOD 0x01U
#define SCSI_TEST_CMPLT_BUSY 0x02U
#define SCSI_TEST_CMPLT_CHECK 0x04U
#define SCSI_TEST_CMPLT_OTHER 0x08U
#define SCSI_TEST_CMPLTMASK \
(SCSI_TEST_CMPLT_GOOD | SCSI_TEST_CMPLT_BUSY | \
SCSI_TEST_CMPLT_CHECK | SCSI_TEST_CMPLT_OTHER)
#define SCSI_TEST_PARTCMPLTMASK \
(SCSI_TEST_CMPLTMASK & ~SCSI_TEST_CMPLT_GOOD)
#define SCSI_TEST_CMD_INCOMPLETE 0x10U
#define SCSI_TEST_NOTCMPLT 0x20U
#define SCSI_TEST_TRAN_BUSY 0x40U
#define SCSI_TEST_TRAN_REJECT 0x80U
#define SCSI_TEST_FAILMASK \
(SCSI_TEST_CMD_INCOMPLETE | SCSI_TEST_NOTCMPLT | \
SCSI_TEST_TRAN_BUSY | SCSI_TEST_TRAN_REJECT)
#define SCSI_TEST_FAILURE(x) (((x) & SCSI_TEST_FAILMASK) != 0)
#if defined(__sparc)
ddi_dma_attr_t scsi_alloc_attr = {
DMA_ATTR_V0,
0x0,
0xFFFFFFFFull,
0xFFFFFFFFull,
1,
1,
1,
0xFFFFFFFFull,
0xFFFFFFFFull,
1,
512,
0
};
#elif defined(__x86)
ddi_dma_attr_t scsi_alloc_attr = {
DMA_ATTR_V0,
0x0,
0x0,
0xFFFFull,
1,
1,
1,
0xFFFFFFFFull,
0xFFFFFFFFull,
0,
512,
0
};
uint64_t scsi_max_phys_addr = 0xFFFFFFFFull;
int scsi_sgl_size = 0xFF;
#endif
ulong_t *scsi_pkt_bad_alloc_bitmap;
int
_init()
{
scsi_initialize_hba_interface();
scsi_watch_init();
#if defined(__x86)
scsi_alloc_attr.dma_attr_addr_hi = scsi_max_phys_addr;
scsi_alloc_attr.dma_attr_sgllen = scsi_sgl_size;
#endif
scsi_pkt_bad_alloc_bitmap = kmem_zalloc(BT_SIZEOFMAP(devcnt), KM_SLEEP);
return (mod_install(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
#define ROUTE (&sd->sd_address)
static int
scsi_slave_do_rqsense(struct scsi_device *sd, int (*callback)())
{
struct scsi_pkt *rq_pkt = NULL;
struct buf *rq_bp = NULL;
int rval = SCSIPROBE_EXISTS;
rq_bp = scsi_alloc_consistent_buf(ROUTE, (struct buf *)NULL,
(uint_t)SENSE_LENGTH, B_READ, callback, NULL);
if (rq_bp == NULL) {
rval = SCSIPROBE_NOMEM;
goto out;
}
rq_pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL,
rq_bp, CDB_GROUP0, 1, 0, PKT_CONSISTENT,
callback, NULL);
if (rq_pkt == NULL) {
if (rq_bp->b_error == 0)
rval = SCSIPROBE_NOMEM_CB;
else
rval = SCSIPROBE_NOMEM;
goto out;
}
ASSERT(rq_bp->b_error == 0);
(void) scsi_setup_cdb((union scsi_cdb *)rq_pkt->
pkt_cdbp, SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0);
FILL_SCSI1_LUN(sd, rq_pkt);
rq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY|FLAG_SENSING;
if (scsi_poll(rq_pkt) < 0) {
rval = SCSIPROBE_FAILURE;
}
out:
if (rq_pkt) {
scsi_destroy_pkt(rq_pkt);
}
if (rq_bp) {
scsi_free_consistent_buf(rq_bp);
}
return (rval);
}
int
scsi_slave(struct scsi_device *sd, int (*callback)())
{
struct scsi_pkt *pkt;
int rval = SCSIPROBE_EXISTS;
pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL, NULL,
CDB_GROUP0, sizeof (struct scsi_arq_status), 0, 0, callback, NULL);
if (pkt == NULL) {
return (SCSIPROBE_NOMEM_CB);
}
(void) scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp,
SCMD_TEST_UNIT_READY, 0, 0, 0);
FILL_SCSI1_LUN(sd, pkt);
pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY;
if (scsi_poll(pkt) < 0) {
if (pkt->pkt_reason == CMD_INCOMPLETE)
rval = SCSIPROBE_NORESP;
else
rval = SCSIPROBE_FAILURE;
if ((pkt->pkt_state & STATE_ARQ_DONE) == 0) {
if (((struct scsi_status *)pkt->pkt_scbp)->sts_chk)
rval = scsi_slave_do_rqsense(sd, callback);
}
if (rval != SCSIPROBE_EXISTS) {
scsi_destroy_pkt(pkt);
return (rval);
}
}
if (scsi_poll(pkt) < 0) {
if (pkt->pkt_reason == CMD_INCOMPLETE)
rval = SCSIPROBE_NORESP;
else
rval = SCSIPROBE_FAILURE;
}
if ((pkt->pkt_state & STATE_ARQ_DONE) == 0) {
if (((struct scsi_status *)pkt->pkt_scbp)->sts_chk) {
rval = scsi_slave_do_rqsense(sd, callback);
}
}
scsi_destroy_pkt(pkt);
if (rval == SCSIPROBE_EXISTS) {
return (scsi_probe(sd, callback));
} else {
return (rval);
}
}
void
scsi_unslave(struct scsi_device *sd)
{
}
void
scsi_unprobe(struct scsi_device *sd)
{
}
static const struct scsi_test_profile {
enum scsi_test_ctxt stp_ctxt;
uint32_t stp_retrymask;
uint32_t stp_fatalmask;
} scsi_test_profile[] = {
{
STC_PROBE_FIRST_INQ,
SCSI_TEST_FAILMASK & ~SCSI_TEST_CMD_INCOMPLETE |
SCSI_TEST_PARTCMPLTMASK,
SCSI_TEST_CMD_INCOMPLETE
},
{
STC_PROBE_FIRST_INQ_RETRY,
SCSI_TEST_PARTCMPLTMASK,
SCSI_TEST_FAILMASK
},
{
STC_PROBE_PARTIAL_SUCCESS,
SCSI_TEST_PARTCMPLTMASK & ~SCSI_TEST_CMPLT_BUSY,
SCSI_TEST_FAILMASK | SCSI_TEST_CMPLT_BUSY
},
{
STC_PROBE_RQSENSE1,
0,
0
},
{
STC_PROBE_CHK_CLEARED,
SCSI_TEST_PARTCMPLTMASK & ~SCSI_TEST_CMPLT_BUSY,
SCSI_TEST_FAILMASK | SCSI_TEST_CMPLT_BUSY
},
{
STC_PROBE_RQSENSE2,
0,
0
},
{
STC_PROBE_INQ_FINAL,
0,
SCSI_TEST_FAILMASK | SCSI_TEST_CMPLT_CHECK
},
{
STC_VPD_CHECK,
0,
0
},
{
STC_IDENTITY_PG80,
0,
SCSI_TEST_FAILMASK | SCSI_TEST_CMPLTMASK
},
{
STC_IDENTITY_PG83,
0,
SCSI_TEST_FAILMASK | SCSI_TEST_CMPLTMASK
}
};
int scsi_test_ereport_disable = 0;
extern int e_devid_cache_path_to_devid(char *, char *, char *, ddi_devid_t *);
static void
scsi_test_ereport_post(struct scsi_pkt *pkt, enum scsi_test_ctxt ctxt,
uint32_t stresult)
{
char *nodename = NULL, *devidstr_buf = NULL, *devidstr = NULL;
const struct scsi_test_profile *tp = &scsi_test_profile[ctxt];
char ua[SCSI_MAXNAMELEN], nodenamebuf[SCSI_MAXNAMELEN];
union scsi_cdb *cdbp = (union scsi_cdb *)pkt->pkt_cdbp;
struct scsi_address *ap = &pkt->pkt_address;
char *tgt_port, *tpl0 = NULL;
ddi_devid_t devid = NULL;
dev_info_t *probe, *hba;
struct scsi_device *sd;
scsi_lun64_t lun64;
const char *d_ass;
const char *class;
char *pathbuf;
nvlist_t *pl;
uint64_t wwn;
int err = 0;
int dad = 0;
size_t len;
int lun;
if (scsi_test_ereport_disable)
return;
ASSERT(tp->stp_ctxt == ctxt);
if ((sd = scsi_address_device(ap)) == NULL)
return;
probe = sd->sd_dev;
hba = ddi_get_parent(probe);
if (!ndi_dev_is_hotplug_node(hba))
return;
if (!DDI_FM_EREPORT_CAP(ddi_fm_capable(hba)))
return;
if (!scsi_ua_get(sd, ua, sizeof (ua)) ||
scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_PROP_SUCCESS)
return;
if (scsi_wwnstr_to_wwn(tgt_port, &wwn) == DDI_FAILURE)
return;
lun = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_LUN, 0);
lun64 = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_LUN64, lun);
if (nvlist_alloc(&pl, NV_UNIQUE_NAME, 0) != 0)
return;
err |= nvlist_add_uint8(pl, "op-code", cdbp->scc_cmd);
err |= nvlist_add_uint8_array(pl, "cdb", pkt->pkt_cdbp,
pkt->pkt_cdblen);
err |= nvlist_add_uint8(pl, "pkt-reason", pkt->pkt_reason);
err |= nvlist_add_uint32(pl, "pkt-state", pkt->pkt_state);
err |= nvlist_add_uint32(pl, "pkt-stats", pkt->pkt_statistics);
err |= nvlist_add_uint32(pl, "stat-code", *pkt->pkt_scbp);
err |= nvlist_add_uint32(pl, "scsi-test-return", stresult);
err |= nvlist_add_int32(pl, "scsi-test-context", ctxt);
switch (stresult) {
case SCSI_TEST_CMPLT_BUSY:
dad = 1;
class = "cmd.disk.dev.serr";
break;
case SCSI_TEST_CMPLT_CHECK:
dad = 1;
if ((pkt->pkt_state & STATE_ARQ_DONE)) {
struct scsi_arq_status *arqstat;
uint8_t key, asc, ascq;
uint8_t *sensep;
class = "cmd.disk.dev.rqs.derr";
arqstat = (struct scsi_arq_status *)pkt->pkt_scbp;
sensep = (uint8_t *)&arqstat->sts_sensedata;
key = scsi_sense_key(sensep);
asc = scsi_sense_asc(sensep);
ascq = scsi_sense_ascq(sensep);
err |= nvlist_add_uint8(pl, "key", key);
err |= nvlist_add_uint8(pl, "asc", asc);
err |= nvlist_add_uint8(pl, "ascq", ascq);
err |= nvlist_add_uint8_array(pl, "sense-data",
sensep, sizeof (arqstat->sts_sensedata));
} else {
class = "cmd.disk.dev.serr";
}
break;
case SCSI_TEST_CMPLT_OTHER:
dad = 1;
class = "cmd.disk.dev.serr";
break;
case SCSI_TEST_CMD_INCOMPLETE:
case SCSI_TEST_NOTCMPLT:
case SCSI_TEST_TRAN_BUSY:
case SCSI_TEST_TRAN_REJECT:
class = "cmd.disk.tran";
break;
}
if (dad) {
if (stresult & tp->stp_fatalmask)
d_ass = (const char *)"fatal";
else if (stresult & tp->stp_retrymask)
d_ass = (const char *)"retry";
else
d_ass = (const char *)"retry";
} else {
d_ass = (const char *)"retry";
}
err |= nvlist_add_string(pl, "driver-assessment", d_ass);
pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
(void) ddi_pathname(hba, pathbuf);
if (e_devid_cache_path_to_devid(pathbuf, ua, nodenamebuf,
&devid) == DDI_SUCCESS) {
nodename = nodenamebuf;
devidstr = devidstr_buf = ddi_devid_str_encode(devid, NULL);
kmem_free(devid, ddi_devid_sizeof(devid));
err |= nvlist_add_string(pl, "devid", devidstr);
}
if (dad && (lun == 0 || lun64 == 0))
tpl0 = tgt_port;
(void) ddi_pathname(hba, pathbuf);
len = strlen(pathbuf);
(void) snprintf(pathbuf + len, MAXPATHLEN - len, "/%s@%s",
nodename ? nodename : "unknown", ua);
if (err)
(void) nvlist_add_boolean_value(pl, "payload-incomplete",
B_TRUE);
scsi_fm_ereport_post(
sd,
0,
pathbuf,
class,
0,
dad ? devidstr : NULL,
tpl0,
DDI_SLEEP,
pl,
FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0,
NULL);
nvlist_free(pl);
if (devidstr_buf)
ddi_devid_str_free(devidstr_buf);
kmem_free(pathbuf, MAXPATHLEN);
}
#ifdef DEBUG
char scsi_test_fail_ua[SCSI_MAXNAMELEN];
int scsi_test_fail_rc = TRAN_ACCEPT;
uchar_t scsi_test_fail_pkt_reason = CMD_CMPLT;
uchar_t scsi_test_fail_status = STATUS_BUSY;
uint_t scsi_test_fail_repeat = (uint_t)-1;
#endif
static uint32_t
scsi_test(struct scsi_pkt *pkt, enum scsi_test_ctxt ctxt)
{
uint32_t rval;
int wait_usec;
int rc;
extern int do_polled_io;
pkt->pkt_flags |= FLAG_NOINTR;
pkt->pkt_time = SCSI_POLL_TIMEOUT;
if (scsi_ifgetcap(&pkt->pkt_address, "tagged-qing", 1) == 1) {
pkt->pkt_flags |= FLAG_STAG;
}
for (wait_usec = 0; (wait_usec / 1000000) <= scsi_test_busy_timeout;
wait_usec += scsi_test_busy_delay) {
*pkt->pkt_scbp = pkt->pkt_reason = pkt->pkt_state = 0;
rc = scsi_transport(pkt);
if ((rc != TRAN_BUSY) || (scsi_test_busy_delay == 0) ||
(scsi_test_busy_timeout == 0))
break;
if ((curthread->t_flag & T_INTR_THREAD) == 0 && !do_polled_io) {
delay(drv_usectohz(scsi_test_busy_delay));
} else {
drv_usecwait(scsi_test_busy_delay);
}
}
#ifdef DEBUG
if (scsi_test_fail_ua[0] != '\0' && scsi_test_fail_repeat > 0) {
struct scsi_address *ap = &pkt->pkt_address;
struct scsi_device *sd;
dev_info_t *probe;
char ua[SCSI_MAXNAMELEN];
if ((sd = scsi_address_device(ap)) != NULL) {
probe = sd->sd_dev;
if (probe && scsi_ua_get(sd, ua, sizeof (ua)) &&
strncmp(ua, scsi_test_fail_ua, sizeof (ua)) == 0) {
scsi_test_fail_repeat--;
rc = scsi_test_fail_rc;
if (rc == TRAN_ACCEPT)
pkt->pkt_reason =
scsi_test_fail_pkt_reason;
*pkt->pkt_scbp = scsi_test_fail_status;
if (scsi_test_fail_status == STATUS_CHECK)
pkt->pkt_state |= STATE_ARQ_DONE;
}
}
}
#endif
switch (rc) {
case TRAN_ACCEPT:
switch (pkt->pkt_reason) {
case CMD_CMPLT:
switch ((*pkt->pkt_scbp) & STATUS_MASK) {
case STATUS_GOOD:
rval = SCSI_TEST_CMPLT_GOOD;
break;
case STATUS_BUSY:
rval = SCSI_TEST_CMPLT_BUSY;
break;
case STATUS_CHECK:
rval = SCSI_TEST_CMPLT_CHECK;
break;
default:
rval = SCSI_TEST_CMPLT_OTHER;
break;
}
break;
case CMD_INCOMPLETE:
rval = SCSI_TEST_CMD_INCOMPLETE;
break;
default:
rval = SCSI_TEST_NOTCMPLT;
break;
}
break;
case TRAN_BUSY:
rval = SCSI_TEST_TRAN_BUSY;
break;
default:
rval = SCSI_TEST_TRAN_REJECT;
break;
}
if (rval != SCSI_TEST_CMPLT_GOOD)
scsi_test_ereport_post(pkt, ctxt, rval);
return (rval);
}
int
scsi_probe(struct scsi_device *sd, int (*callback)())
{
int ret, retry = 0;
int lr_cap, sr_ret;
scsi_hba_tran_t *tran = sd->sd_address.a_hba_tran;
if (scsi_check_ss2_LUN_limit(sd) != 0) {
return (SCSIPROBE_NORESP);
}
again:
ret = lr_cap = sr_ret = -1;
if (tran->tran_tgt_probe != NULL) {
ret = (*tran->tran_tgt_probe)(sd, callback);
} else {
ret = scsi_hba_probe(sd, callback);
}
if ((ret != SCSIPROBE_EXISTS) && (retry == 0)) {
lr_cap = (*tran->tran_getcap)(&sd->sd_address, "lun-reset", 1);
sr_ret = scsi_reset(&sd->sd_address, RESET_LUN);
if ((sr_ret != 1) && (lr_cap == 1)) {
cmn_err(CE_WARN, "scsi_probe(%d): scsi_reset failed(%d)"
" lun-reset cap(%d)", ret, sr_ret, lr_cap);
}
retry = 1;
goto again;
}
if (ret == SCSIPROBE_EXISTS) {
create_inquiry_props(sd);
scsi_establish_LUN_limit(sd);
}
return (ret);
}
int
scsi_hba_probe(struct scsi_device *sd, int (*callback)())
{
return (scsi_hba_probe_pi(sd, callback, 0));
}
int
scsi_hba_probe_pi(struct scsi_device *sd, int (*callback)(), int pi)
{
struct scsi_pkt *inq_pkt = NULL;
struct scsi_pkt *rq_pkt = NULL;
int rval = SCSIPROBE_NOMEM;
struct buf *inq_bp = NULL;
struct buf *rq_bp = NULL;
int (*cb_flag)();
int pass = 1;
uint32_t str;
if (sd->sd_inq == NULL) {
sd->sd_inq = (struct scsi_inquiry *)
kmem_alloc(SUN_INQSIZE, ((callback == SLEEP_FUNC) ?
KM_SLEEP : KM_NOSLEEP));
if (sd->sd_inq == NULL) {
goto out;
}
}
if (callback != SLEEP_FUNC && callback != NULL_FUNC) {
cb_flag = NULL_FUNC;
} else {
cb_flag = callback;
}
inq_bp = scsi_alloc_consistent_buf(ROUTE,
(struct buf *)NULL, SUN_INQSIZE, B_READ, cb_flag, NULL);
if (inq_bp == NULL) {
goto out;
}
inq_pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL,
inq_bp, CDB_GROUP0, sizeof (struct scsi_arq_status),
0, PKT_CONSISTENT, callback, NULL);
if (inq_pkt == NULL) {
if (inq_bp->b_error == 0)
rval = SCSIPROBE_NOMEM_CB;
goto out;
}
ASSERT(inq_bp->b_error == 0);
(void) scsi_setup_cdb((union scsi_cdb *)inq_pkt->pkt_cdbp,
SCMD_INQUIRY, 0, SUN_INQSIZE, 0);
inq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY;
if (pi && scsi_pkt_allocated_correctly(inq_pkt)) {
inq_pkt->pkt_path_instance = pi;
inq_pkt->pkt_flags |= FLAG_PKT_PATH_INSTANCE;
}
bzero((caddr_t)sd->sd_inq, SUN_INQSIZE);
again: FILL_SCSI1_LUN(sd, inq_pkt);
str = scsi_test(inq_pkt, STC_PROBE_FIRST_INQ);
if (SCSI_TEST_FAILURE(str)) {
if (str == SCSI_TEST_CMD_INCOMPLETE) {
rval = SCSIPROBE_NORESP;
goto out;
}
str = scsi_test(inq_pkt, STC_PROBE_FIRST_INQ_RETRY);
if (SCSI_TEST_FAILURE(str)) {
rval = SCSIPROBE_FAILURE;
goto out;
}
}
if (str == SCSI_TEST_CMPLT_GOOD)
goto done;
str = scsi_test(inq_pkt, STC_PROBE_PARTIAL_SUCCESS);
if (SCSI_TEST_FAILURE(str)) {
if (str == SCSI_TEST_CMD_INCOMPLETE)
rval = SCSIPROBE_NORESP;
else
rval = SCSIPROBE_FAILURE;
goto out;
}
if (str == SCSI_TEST_CMPLT_BUSY) {
rval = SCSIPROBE_BUSY;
goto out;
}
if (str == SCSI_TEST_CMPLT_CHECK &&
(inq_pkt->pkt_state & STATE_ARQ_DONE) == 0) {
rq_bp = scsi_alloc_consistent_buf(ROUTE, (struct buf *)NULL,
(uint_t)SENSE_LENGTH, B_READ, cb_flag, NULL);
if (rq_bp == NULL) {
goto out;
}
rq_pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL,
rq_bp, CDB_GROUP0, 1, 0, PKT_CONSISTENT, callback, NULL);
if (rq_pkt == NULL) {
if (rq_bp->b_error == 0)
rval = SCSIPROBE_NOMEM_CB;
goto out;
}
ASSERT(rq_bp->b_error == 0);
(void) scsi_setup_cdb((union scsi_cdb *)rq_pkt->
pkt_cdbp, SCMD_REQUEST_SENSE, 0, SENSE_LENGTH, 0);
FILL_SCSI1_LUN(sd, rq_pkt);
rq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY;
if (pi && scsi_pkt_allocated_correctly(rq_pkt)) {
rq_pkt->pkt_path_instance = pi;
rq_pkt->pkt_flags |= FLAG_PKT_PATH_INSTANCE;
}
if (SCSI_TEST_FAILURE(scsi_test(rq_pkt, STC_PROBE_RQSENSE1))) {
rval = SCSIPROBE_FAILURE;
goto out;
}
}
str = scsi_test(inq_pkt, STC_PROBE_CHK_CLEARED);
if (SCSI_TEST_FAILURE(str)) {
rval = SCSIPROBE_FAILURE;
goto out;
}
if (str == SCSI_TEST_CMPLT_BUSY) {
rval = SCSIPROBE_BUSY;
goto out;
}
if (str == SCSI_TEST_CMPLT_CHECK) {
if (rq_pkt)
(void) scsi_test(rq_pkt, STC_PROBE_RQSENSE2);
str = scsi_test(inq_pkt, STC_PROBE_INQ_FINAL);
if (SCSI_TEST_FAILURE(str)) {
rval = SCSIPROBE_FAILURE;
goto out;
} else if (str == SCSI_TEST_CMPLT_CHECK) {
rval = SCSIPROBE_FAILURE;
goto out;
}
}
done:
if ((inq_pkt->pkt_state & STATE_XFERRED_DATA) == 0 ||
((SUN_INQSIZE - inq_pkt->pkt_resid) < SUN_MIN_INQLEN)) {
rval = SCSIPROBE_NONCCS;
} else {
ASSERT(inq_pkt->pkt_resid >= 0);
bcopy((caddr_t)inq_bp->b_un.b_addr,
(caddr_t)sd->sd_inq, (SUN_INQSIZE - inq_pkt->pkt_resid));
rval = SCSIPROBE_EXISTS;
}
out:
if ((rval == SCSIPROBE_EXISTS) && (pass == 1) &&
(sd->sd_address.a_lun > 0) && (sd->sd_inq->inq_ansi == 0x1)) {
pass++;
if (sd->sd_address.a_lun <= 7)
goto again;
rval = SCSIPROBE_FAILURE;
}
if (rq_pkt) {
scsi_destroy_pkt(rq_pkt);
}
if (inq_pkt) {
scsi_destroy_pkt(inq_pkt);
}
if (rq_bp) {
scsi_free_consistent_buf(rq_bp);
}
if (inq_bp) {
scsi_free_consistent_buf(inq_bp);
}
return (rval);
}
#define DEVP_TO_TRAN(sd) ((sd)->sd_tran_safe)
int
scsi_ua_get_reportdev(struct scsi_device *sd, char *ra, int len)
{
if (DEVP_TO_TRAN(sd)->tran_get_bus_addr)
return ((*DEVP_TO_TRAN(sd)->tran_get_bus_addr)(sd, ra, len));
return (scsi_hba_ua_get_reportdev(sd, ra, len));
}
int
scsi_hba_ua_get_reportdev(struct scsi_device *sd, char *ra, int len)
{
int tgt, lun, sfunc;
char *tgt_port;
scsi_lun64_t lun64;
tgt = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_TARGET, -1);
if (scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_PROP_SUCCESS)
tgt_port = NULL;
if ((tgt == -1) && (tgt_port == NULL))
return (0);
lun = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_LUN, 0);
lun64 = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_LUN64, lun);
sfunc = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_SFUNC, -1);
if (tgt_port) {
if (sfunc == -1)
(void) snprintf(ra, len,
"%s %s lun %" PRIx64,
SCSI_ADDR_PROP_TARGET_PORT, tgt_port, lun64);
else
(void) snprintf(ra, len,
"%s %s lun %" PRIx64 " sfunc %x",
SCSI_ADDR_PROP_TARGET_PORT, tgt_port, lun64, sfunc);
scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
} else {
if (sfunc == -1)
(void) snprintf(ra, len,
"%s %x lun %" PRIx64,
SCSI_ADDR_PROP_TARGET, tgt, lun64);
else
(void) snprintf(ra, len,
"%s %x lun %" PRIx64 " sfunc %x",
SCSI_ADDR_PROP_TARGET, tgt, lun64, sfunc);
}
return (1);
}
int
scsi_ua_get(struct scsi_device *sd, char *ua, int len)
{
char *eua;
if ((eua = scsi_device_unit_address(sd)) != NULL) {
(void) strlcpy(ua, eua, len);
return (1);
}
if (DEVP_TO_TRAN(sd)->tran_get_name)
return ((*DEVP_TO_TRAN(sd)->tran_get_name)(sd, ua, len));
return (scsi_hba_ua_get(sd, ua, len));
}
int
scsi_hba_ua_get(struct scsi_device *sd, char *ua, int len)
{
int tgt, lun, sfunc;
char *tgt_port;
scsi_lun64_t lun64;
tgt = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_TARGET, -1);
if (scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_PROP_SUCCESS)
tgt_port = NULL;
if ((tgt == -1) && (tgt_port == NULL))
return (0);
lun = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_LUN, 0);
lun64 = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_LUN64, lun);
sfunc = scsi_device_prop_get_int(sd, SCSI_DEVICE_PROP_PATH,
SCSI_ADDR_PROP_SFUNC, -1);
if (tgt_port) {
if (sfunc == -1)
(void) snprintf(ua, len, "%s,%" PRIx64,
tgt_port, lun64);
else
(void) snprintf(ua, len, "%s,%" PRIx64 ",%x",
tgt_port, lun64, sfunc);
scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
} else {
if (sfunc == -1)
(void) snprintf(ua, len, "%x,%" PRIx64, tgt, lun64);
else
(void) snprintf(ua, len, "%x,%" PRIx64 ",%x",
tgt, lun64, sfunc);
}
return (1);
}
static void
create_inquiry_props(struct scsi_device *sd)
{
struct scsi_inquiry *inq = sd->sd_inq;
(void) ndi_prop_update_int(DDI_DEV_T_NONE, sd->sd_dev,
INQUIRY_DEVICE_TYPE, (int)inq->inq_dtype);
if (inq->inq_ansi != 1) {
if (ddi_prop_exists(DDI_DEV_T_NONE, sd->sd_dev,
DDI_PROP_TYPE_STRING, INQUIRY_VENDOR_ID) == 0)
(void) scsi_device_prop_update_inqstring(sd,
INQUIRY_VENDOR_ID,
inq->inq_vid, sizeof (inq->inq_vid));
if (ddi_prop_exists(DDI_DEV_T_NONE, sd->sd_dev,
DDI_PROP_TYPE_STRING, INQUIRY_PRODUCT_ID) == 0)
(void) scsi_device_prop_update_inqstring(sd,
INQUIRY_PRODUCT_ID,
inq->inq_pid, sizeof (inq->inq_pid));
if (ddi_prop_exists(DDI_DEV_T_NONE, sd->sd_dev,
DDI_PROP_TYPE_STRING, INQUIRY_REVISION_ID) == 0)
(void) scsi_device_prop_update_inqstring(sd,
INQUIRY_REVISION_ID,
inq->inq_revision, sizeof (inq->inq_revision));
}
}
int
scsi_device_prop_update_inqstring(struct scsi_device *sd,
char *name, char *data, size_t len)
{
int ilen;
char *data_string;
int rv;
ilen = scsi_ascii_inquiry_len(data, len);
ASSERT(ilen <= (int)len);
if (ilen <= 0)
return (DDI_PROP_INVAL_ARG);
data_string = kmem_zalloc(ilen + 1, KM_SLEEP);
bcopy(data, data_string, ilen);
rv = ndi_prop_update_string(DDI_DEV_T_NONE,
sd->sd_dev, name, data_string);
kmem_free(data_string, ilen + 1);
return (rv);
}
struct scsi_device *
scsi_address_device(struct scsi_address *sa)
{
return ((sa->a_hba_tran->tran_hba_flags & SCSI_HBA_ADDR_COMPLEX) ?
sa->a.a_sd : NULL);
}
void
scsi_device_hba_private_set(struct scsi_device *sd, void *data)
{
ASSERT(sd->sd_address.a_hba_tran->tran_hba_flags &
SCSI_HBA_ADDR_COMPLEX);
sd->sd_hba_private = data;
}
void *
scsi_device_hba_private_get(struct scsi_device *sd)
{
ASSERT(sd->sd_address.a_hba_tran->tran_hba_flags &
SCSI_HBA_ADDR_COMPLEX);
return (sd->sd_hba_private);
}
static int
scsi_check_ss2_LUN_limit(struct scsi_device *sd)
{
struct scsi_address *ap = &(sd->sd_address);
dev_info_t *pdevi =
(dev_info_t *)DEVI(sd->sd_dev)->devi_parent;
int ret_val = 0;
uchar_t *tgt_list;
uint_t tgt_nelements;
int i;
if ((ap->a_target >= NTARGETS_WIDE) ||
(ap->a_lun < NLUNS_PER_TARGET)) {
return (0);
}
SCSI_PROBE_DEBUG2(1, "SCSA pre-probe: checking tgt.LUN=%d.%d\n",
ap->a_target, ap->a_lun);
SCSI_PROBE_DEBUG1(2,
"SCSA pre-probe: scanning parent node name: %s ...\n",
ddi_node_name(pdevi));
if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, pdevi,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, SS2_LUN0_TGT_LIST_PROP,
&tgt_list, &tgt_nelements) != DDI_PROP_SUCCESS) {
SCSI_PROBE_DEBUG0(3,
"SCSA pre-probe: NO parent prop found\n");
} else {
for (i = 0; i < tgt_nelements; i++) {
if (tgt_list[i] == ap->a_target) {
ret_val = 1;
break;
}
}
ddi_prop_free(tgt_list);
#ifdef DEBUG
if (ret_val == 1) {
SCSI_PROBE_DEBUG2(1,
"SCSA pre-probe: marker node FOUND for "
"tgt.LUN=%d.%d, so SKIPPING it\n",
ap->a_target, ap->a_lun);
} else {
SCSI_PROBE_DEBUG0(2,
"SCSA pre-probe: NO marker node found"
" -- OK to probe\n");
}
#endif
}
return (ret_val);
}
static void
scsi_establish_LUN_limit(struct scsi_device *sd)
{
struct scsi_address *ap = &(sd->sd_address);
struct scsi_inquiry *inq = sd->sd_inq;
dev_info_t *devi = sd->sd_dev;
char *vid = NULL;
char *pid = NULL;
char *rev = NULL;
int i;
const ss2_lun0_info_t *p;
int bad_target_found = 0;
if ((ap->a_lun != 0) ||
(ap->a_target >= NTARGETS_WIDE) ||
(inq->inq_dtype != DTYPE_PROCESSOR) ||
(inq->inq_ansi != 2)) {
return;
}
SCSI_PROBE_DEBUG2(1, "SCSA post-probe: LUN limit on tgt.LUN=%d.%d, "
"SCSI-2 PROCESSOR?\n", ap->a_target, ap->a_lun);
ASSERT(devi != NULL);
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
INQUIRY_VENDOR_ID, &vid) != DDI_PROP_SUCCESS) {
SCSI_PROBE_DEBUG1(2, "SCSA post-probe: prop \"%s\" missing\n",
INQUIRY_VENDOR_ID);
goto dun;
}
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
INQUIRY_PRODUCT_ID, &pid) != DDI_PROP_SUCCESS) {
SCSI_PROBE_DEBUG1(2, "SCSA post-probe: prop \"%s\" missing\n",
INQUIRY_PRODUCT_ID);
goto dun;
}
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devi,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
INQUIRY_REVISION_ID, &rev) != DDI_PROP_SUCCESS) {
SCSI_PROBE_DEBUG1(2, "SCSA post-probe: prop \"%s\" missing\n",
INQUIRY_REVISION_ID);
goto dun;
}
SCSI_PROBE_DEBUG3(3, "SCSA post-probe: looking for vid/pid/rev = "
"\"%s\"/\"%s\"/\"%s\"\n", vid, pid, rev);
for (i = 0; i < scsi_probe_strict_s2_size; i++) {
p = &scsi_probe_strict_s2_list[i];
if ((strcmp(p->sli_vid, vid) == 0) &&
(strcmp(p->sli_pid, pid) == 0) &&
(strcmp(p->sli_rev, rev) == 0)) {
SCSI_PROBE_DEBUG3(1,
"SCSA post-probe: recording strict SCSI-2 node "
"vid/pid/rev = \"%s\"/\"%s\"/\"%s\"\n",
vid, pid, rev);
bad_target_found = 1;
break;
}
}
scsi_update_parent_ss2_prop(devi, ap->a_target, bad_target_found);
dun:
if (vid != NULL) {
ddi_prop_free(vid);
}
if (pid != NULL) {
ddi_prop_free(pid);
}
if (rev != NULL) {
ddi_prop_free(rev);
}
}
static void
scsi_update_parent_ss2_prop(dev_info_t *devi, int tgt, int add_tgt)
{
dev_info_t *pdevi = (dev_info_t *)DEVI(devi)->devi_parent;
uchar_t *tgt_list;
uint_t nelements;
uint_t new_nelements;
int i;
int update_result;
uchar_t new_tgt_list[NTARGETS_WIDE];
ASSERT(pdevi != NULL);
SCSI_PROBE_DEBUG3(3,
"SCSA post-probe: updating parent=%s property to %s tgt=%d\n",
ddi_node_name(pdevi), add_tgt ? "add" : "remove", tgt);
if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, pdevi,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
SS2_LUN0_TGT_LIST_PROP, &tgt_list, &nelements) ==
DDI_PROP_SUCCESS) {
if (add_tgt) {
for (i = 0; i < nelements; i++) {
if (tgt_list[i] == tgt) {
SCSI_PROBE_DEBUG1(2, "SCSA post-probe:"
" tgt %d already listed\n", tgt);
ddi_prop_free(tgt_list);
return;
}
}
new_nelements = nelements + 1;
if (new_nelements >= NTARGETS_WIDE) {
SCSI_PROBE_DEBUG0(1, "SCSA post-probe: "
"internal error: no room "
"for more targets?\n");
ddi_prop_free(tgt_list);
return;
}
bcopy((void *)tgt_list, (void *)new_tgt_list,
sizeof (uchar_t) * nelements);
new_tgt_list[new_nelements - 1] = (uchar_t)tgt;
} else {
int tgt_removed = 0;
new_nelements = 0;
for (i = 0; i < nelements; i++) {
if (tgt_list[i] != tgt) {
new_tgt_list[new_nelements++] =
tgt_list[i];
} else {
tgt_removed++;
}
}
if (!tgt_removed) {
SCSI_PROBE_DEBUG1(2, "SCSA post-probe:"
" no need to remove tgt %d\n", tgt);
ddi_prop_free(tgt_list);
return;
}
}
update_result = ddi_prop_update_byte_array(DDI_DEV_T_NONE,
pdevi, SS2_LUN0_TGT_LIST_PROP, new_tgt_list,
new_nelements);
ddi_prop_free(tgt_list);
} else {
if (add_tgt) {
new_tgt_list[0] = (uchar_t)tgt;
new_nelements = 1;
update_result = ddi_prop_update_byte_array(
DDI_DEV_T_NONE, pdevi, SS2_LUN0_TGT_LIST_PROP,
new_tgt_list, new_nelements);
} else {
return;
}
}
#ifdef DEBUG
if (update_result != DDI_PROP_SUCCESS) {
SCSI_PROBE_DEBUG2(1, "SCSA post-probe: can't update parent "
"property with tgt=%d (%d)\n", tgt, update_result);
} else {
if (add_tgt) {
SCSI_PROBE_DEBUG3(2,
"SCSA post-probe: added tgt=%d to parent "
"prop=\"%s\" (now %d entries)\n",
tgt, SS2_LUN0_TGT_LIST_PROP, new_nelements);
} else {
SCSI_PROBE_DEBUG3(2,
"SCSA post-probe: removed tgt=%d from parent "
"prop=\"%s\" (now %d entries)\n",
tgt, SS2_LUN0_TGT_LIST_PROP, new_nelements);
}
}
#endif
}
#define VPD_HEAD_OFFSET 3
#define VPD_PAGE_LENGTH 3
#define VPD_MODE_PAGE 1
#define MAX_INQUIRY_SIZE 0xF0
#define MAX_INQUIRY_SIZE_EVPD 0xFF
int
scsi_device_identity(struct scsi_device *sd, int (*callback)())
{
dev_info_t *devi = sd->sd_dev;
uchar_t *inq80 = NULL;
uchar_t *inq83 = NULL;
int rval;
size_t len;
int pg80, pg83;
if (check_vpd_page_support8083(sd, callback, &pg80, &pg83) == -1)
return (-1);
if (pg80) {
inq80 = kmem_zalloc(MAX_INQUIRY_SIZE,
((callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP));
if (inq80 == NULL) {
rval = -1;
goto out;
}
rval = send_scsi_INQUIRY(sd, callback, inq80,
MAX_INQUIRY_SIZE, 0x01, 0x80, &len, STC_IDENTITY_PG80);
if (rval)
goto out;
if (len && (ndi_prop_update_byte_array(DDI_DEV_T_NONE, devi,
"inquiry-page-80", inq80, len) != DDI_PROP_SUCCESS)) {
cmn_err(CE_WARN, "scsi_device_identity: "
"failed to add page80 prop");
rval = -1;
goto out;
}
}
if (pg83) {
inq83 = kmem_zalloc(MAX_INQUIRY_SIZE,
((callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP));
if (inq83 == NULL) {
rval = -1;
goto out;
}
rval = send_scsi_INQUIRY(sd, callback, inq83,
MAX_INQUIRY_SIZE, 0x01, 0x83, &len, STC_IDENTITY_PG83);
if (rval)
goto out;
if (len && (ndi_prop_update_byte_array(DDI_DEV_T_NONE, devi,
"inquiry-page-83", inq83, len) != DDI_PROP_SUCCESS)) {
cmn_err(CE_WARN, "scsi_device_identity: "
"failed to add page83 prop");
rval = -1;
goto out;
}
}
rval = 0;
out: if (inq80 != NULL)
kmem_free(inq80, MAX_INQUIRY_SIZE);
if (inq83 != NULL)
kmem_free(inq83, MAX_INQUIRY_SIZE);
return (rval);
}
static int
check_vpd_page_support8083(struct scsi_device *sd, int (*callback)(),
int *ppg80, int *ppg83)
{
uchar_t *page_list;
int counter;
int rval;
*ppg80 = 0;
*ppg83 = 0;
page_list = kmem_zalloc(MAX_INQUIRY_SIZE_EVPD,
((callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP));
if (page_list == NULL)
return (-1);
rval = send_scsi_INQUIRY(sd, callback,
page_list, MAX_INQUIRY_SIZE_EVPD, 1, 0, NULL, STC_VPD_CHECK);
if ((rval == 0) &&
(page_list[VPD_MODE_PAGE] == 0x00)) {
counter = 4;
while ((page_list[counter] <= 0x83) &&
(counter <= (page_list[VPD_PAGE_LENGTH] +
VPD_HEAD_OFFSET))) {
switch (page_list[counter]) {
case 0x80:
*ppg80 = 1;
break;
case 0x83:
*ppg83 = 1;
break;
}
counter++;
}
}
kmem_free(page_list, MAX_INQUIRY_SIZE_EVPD);
return (0);
}
static int
send_scsi_INQUIRY(struct scsi_device *sd, int (*callback)(),
uchar_t *bufaddr, size_t buflen,
uchar_t evpd, uchar_t page_code, size_t *lenp,
enum scsi_test_ctxt ctxt)
{
int (*cb_flag)();
struct buf *inq_bp;
struct scsi_pkt *inq_pkt = NULL;
int rval = -1;
if (lenp)
*lenp = 0;
if (callback != SLEEP_FUNC && callback != NULL_FUNC)
cb_flag = NULL_FUNC;
else
cb_flag = callback;
inq_bp = scsi_alloc_consistent_buf(ROUTE,
(struct buf *)NULL, buflen, B_READ, cb_flag, NULL);
if (inq_bp == NULL)
goto out;
inq_pkt = scsi_init_pkt(ROUTE, (struct scsi_pkt *)NULL,
inq_bp, CDB_GROUP0, sizeof (struct scsi_arq_status),
0, PKT_CONSISTENT, callback, NULL);
if (inq_pkt == NULL)
goto out;
ASSERT(inq_bp->b_error == 0);
(void) scsi_setup_cdb((union scsi_cdb *)inq_pkt->pkt_cdbp,
SCMD_INQUIRY, 0, buflen, 0);
inq_pkt->pkt_cdbp[1] = evpd;
inq_pkt->pkt_cdbp[2] = page_code;
inq_pkt->pkt_time = SCSI_POLL_TIMEOUT;
inq_pkt->pkt_flags = FLAG_NOINTR|FLAG_NOPARITY;
if (scsi_test(inq_pkt, ctxt) == SCSI_TEST_CMPLT_GOOD) {
ASSERT(inq_pkt->pkt_resid >= 0);
ASSERT(inq_pkt->pkt_resid <= buflen);
bcopy(inq_bp->b_un.b_addr,
bufaddr, buflen - inq_pkt->pkt_resid);
if (lenp)
*lenp = (buflen - inq_pkt->pkt_resid);
rval = 0;
}
out: if (inq_pkt)
scsi_destroy_pkt(inq_pkt);
if (inq_bp)
scsi_free_consistent_buf(inq_bp);
return (rval);
}