ses.c revision 275c9da86e89f8abf71135cf63d9fc23671b2e60
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Enclosure Services Device target driver
*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Power management defines (should be in a common include file?)
*/
#define PM_HARDWARE_STATE_PROP "pm-hardware-state"
#define PM_NEEDS_SUSPEND_RESUME "needs-suspend-resume"
/*
* Global Driver Data
*/
int ses_io_time = SES_IO_TIME;
#ifdef DEBUG
int ses_debug = 0;
#else /* DEBUG */
#define ses_debug 0
#endif /* DEBUG */
/*
* External Enclosure Functions
*/
extern int ses_softc_init(ses_softc_t *, int);
extern int ses_init_enc(ses_softc_t *);
extern int ses_get_encstat(ses_softc_t *, int);
extern int safte_softc_init(ses_softc_t *, int);
extern int safte_init_enc(ses_softc_t *);
extern int safte_get_encstat(ses_softc_t *, int);
extern int sen_softc_init(ses_softc_t *, int);
extern int sen_init_enc(ses_softc_t *);
extern int sen_get_encstat(ses_softc_t *, int);
/*
* Local Function prototypes
*/
static int ses_probe(dev_info_t *);
{
},
{
},
{
}
};
/*
* Local Functions
*/
static void ses_restart(void *arg);
/*
* Local Static Data
*/
#ifndef D_HOTPLUG
#define D_HOTPLUG 0
#endif /* D_HOTPLUG */
static struct cb_ops ses_cb_ops = {
ses_open, /* open */
ses_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
ses_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
#if !defined(CB_REV)
#else /* !defined(CB_REV) */
CB_REV, /* cb_ops version number */
nodev, /* aread */
nodev /* awrite */
#endif /* !defined(CB_REV) */
};
static struct dev_ops ses_dev_ops = {
DEVO_REV, /* devo_rev, */
0, /* refcnt */
ses_info, /* info */
nulldev, /* identify */
ses_probe, /* probe */
ses_attach, /* attach */
ses_detach, /* detach */
nodev, /* reset */
&ses_cb_ops, /* driver operations */
NULL /* power */
};
static const char *Snm = "ses";
static const char *Str = "%s\n";
static const char *fail_msg = "%stransport failed: reason '%s': %s";
/*
* autoconfiguration routines.
*/
char _depends_on[] = "misc/scsi";
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
int status;
if (status == 0) {
}
}
return (status);
}
int
_fini(void)
{
int status;
return (status);
}
return (status);
}
int
{
}
static int
{
int err;
struct scsi_device *devp;
/*
* I finally figured out why we return success
* on every probe. The devices that we attach to
* don't all report as being the same "device type"
*
* 1) A5x00 -- report as Enclosure Services (0xD) SES
* 2) A1000 -- report as Direct Access (0x0) SES
* uses the same target as raid controler.
* 3) D1000 -- report as processor (0x3) SAFTE
* 3) D240 -- report as processor (0x3) SAFTE
*
* We also reportedly attach to SEN devices which I
* believe reside in a Tobasco tray. I have never
* been able to get one to attach.
*
*/
return (DDI_PROBE_FAILURE);
/*
* XXX: Breakage from the x86 folks.
*/
return (DDI_PROBE_FAILURE);
}
/* SES_LOG(NULL, SES_CE_DEBUG1, "ses_probe: OK"); */
return (DDI_PROBE_DONTCARE);
}
case SCSIPROBE_EXISTS:
break;
}
/* FALLTHROUGH */
case SCSIPROBE_NORESP:
return (DDI_PROBE_FAILURE);
default:
"ses_probe: probe error %d", err);
return (DDI_PROBE_FAILURE);
}
return (DDI_PROBE_SUCCESS);
}
static int
{
switch (cmd) {
case DDI_ATTACH:
inst);
if (err == DDI_FAILURE) {
return (DDI_FAILURE);
}
"ses_attach: DDI_ATTACH OK ses%d", inst);
break;
case DDI_RESUME:
return (DDI_FAILURE);
}
inst);
ssc->ses_suspended = 0;
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
{
} else {
}
return (1);
}
/*
* PassThrough Device.
*/
return (1);
}
if (iqlen < 47) {
"INQUIRY data too short to determine SAF-TE");
return (0);
}
return (1);
}
return (0);
}
/*
* Attach ses device.
*
* XXX: Power management is NOT supported. A token framework
* is provided that will need to be extended assuming we have
* ses devices we can power down. Currently, we don't have any.
*/
static int
{
/*
* Workaround for bug #4154979- for some reason we can
* be called with identical instance numbers but for
* different dev_info_t-s- all but one are bogus.
*
* Bad Dog! No Biscuit!
*
* A quick workaround might be to call ddi_soft_state_zalloc
* unconditionally, as the implementation fails these calls
* if there's an item already allocated. A more reasonable
* and longer term change is to move the allocation past
* the probe for the device's existence as most of these
* 'bogus' calls are for nonexistent devices.
*/
/*
* Determine whether the { i, t, l } we're called
* to start is an enclosure services device.
*/
/*
* Call the scsi_probe routine to see whether
* we actually have an Enclosure Services device at
* this address.
*/
if (err != SCSIPROBE_EXISTS) {
"ses_doattach: probe error %d", err);
return (DDI_FAILURE);
}
/* Call is_enc_dev() to get the etyp */
"ses_doattach: ses%d: is_enc_dev failure", inst);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
if (err == DDI_FAILURE) {
return (DDI_FAILURE);
}
/* Call SoftC Init Routine A bit later... */
SLEEP_FUNC, NULL);
}
}
return (DDI_FAILURE);
}
case 1:
/* if already set, don't reset it */
break;
case 0:
/* try and set it */
"auto-rqsense", 1, 1) == 1) ? 1 : 0);
break;
default:
/* probably undefined, so zero it out */
break;
}
/*
* If the HBA supports wide, tell it to use wide.
*/
? 1 : 0;
}
/*
* Now do ssc init of enclosure specifics.
* At the same time, check to make sure getrbuf
* actually succeeded.
*/
}
return (DDI_FAILURE);
}
/*
* create this property so that PM code knows we want
* to be suspended at PM time
*/
/* announce the existence of this device */
return (DDI_SUCCESS);
}
/*
* Detach ses device.
*
* XXX: Power management is NOT supported. A token framework
* is provided that will need to be extended assuming we have
* ses devices we can power down. Currently, we don't have any.
*/
static int
{
int inst;
switch (cmd) {
case DDI_DETACH:
"ses%d: DDI_DETACH, no softstate found", inst);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
#if !defined(lint)
/* LINTED */
#endif /* !defined(lint) */
#if !defined(lint)
#endif /* !defined(lint) */
break;
case DDI_SUSPEND:
"ses%d: DDI_SUSPEND, no softstate found", inst);
return (DDI_FAILURE);
}
/*
* If driver idle, accept suspend request.
* If it's busy, reject it. This keeps things simple!
*/
if (ssc->ses_sbufbsy) {
return (DDI_FAILURE);
}
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
}
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*
* Unix Entry Points
*/
/* ARGSUSED */
static int
{
return (ENXIO);
}
/*
* If the device is powered down, request it's activation.
* If it can't be activated, fail open.
*/
if (ssc->ses_suspended &&
return (EIO);
}
ssc->ses_lyropen++;
else
return (EOK);
}
/*ARGSUSED*/
static int
{
return (ENXIO);
}
if (ssc->ses_suspended) {
}
else
return (0);
}
/*ARGSUSED3*/
static int
{
ses_object k, *up;
ses_objarg x;
uchar_t t;
uchar_t i;
int rv = 0;
return (ENXIO);
}
switch (cmd) {
case SESIOC_GETNOBJ:
sizeof (int), flg)) {
break;
}
break;
case SESIOC_GETOBJMAP:
for (i = 0; i != ssc->ses_nobjects; i++) {
k.obj_id = i;
break;
}
up++;
}
break;
case SESIOC_INIT:
break;
case SESIOC_GETENCSTAT:
if (rv) {
break;
}
}
/*
* And always invalidate enclosure status on the way out.
*/
break;
case SESIOC_SETENCSTAT:
else
break;
case SESIOC_GETOBJSTAT:
break;
}
break;
}
break;
else {
/*
* Now that we no longer poll, svalid never stays true.
*/
}
break;
case SESIOC_SETOBJSTAT:
break;
}
break;
}
if (rv == 0) {
}
break;
case USCSICMD:
break;
default:
break;
}
return (rv);
}
/*
* Loop on running a kernel based command
*
* FIXME: This routine is not really needed.
*/
int
{
int e;
lp->uscsi_status = 0;
#ifdef not
/*
* Debug: Nice cross-check code for verifying consistent status.
*/
if (lp->uscsi_status) {
} else {
lp->uscsi_status);
}
}
#endif /* not */
return (e);
}
/*
* Run a scsi command.
*/
int
{
int err;
/*
* Grab local 'special' buffer
*/
while (ssc->ses_sbufbsy) {
}
/*
* If the device is powered down, request it's activation.
* This check must be done after setting ses_sbufbsy!
*/
if (ssc->ses_suspended &&
ssc->ses_sbufbsy = 0;
return (EIO);
}
if (err != 0) {
"scsi_uscsi_alloc_and_copyin failed\n");
ssc->ses_sbufbsy = 0;
return (err);
}
/*
* Copy the uscsi command related infos to ssc for use in ses_start()
* and ses_callback().
*/
}
"scsi_cmd: %x %x %x %x %x %x",
} else {
"scsi cmd: %x %x %x %x %x %x %x %x %x %x",
}
}
/*
* ses_callback() may set values for ssc->ses_uscsicmd or
* ssc->ses_srqsbuf, so copy them back to uscmd.
*/
}
ssc->ses_sbufbsy = 0;
return (err);
}
/*
* Command start and done functions.
*/
static int
{
/*
* Allocate a packet.
*/
int err;
return (err);
}
}
/*
* Initialize the transfer residue, error code, and retry count.
*/
SET_BP_ERROR(bp, 0);
#if !defined(lint)
#endif /* !defined(lint) */
#if !defined(lint)
/* LINTED */
#endif /* !defined(lint) */
case TRAN_ACCEPT:
return (0);
/* break; */
case TRAN_BUSY:
"ses_start: TRANSPORT BUSY");
return (0);
/* break; */
default:
return (EIO);
/* break; */
}
}
static void
{
int stat_size;
stat_size = sizeof (struct scsi_arq_status);
} else {
stat_size = 1;
}
} else {
}
return;
}
/*
* Restart ses command.
*/
static void
ses_restart(void *arg)
{
switch (scsi_transport(pkt)) {
case TRAN_ACCEPT:
return;
/* break; */
case TRAN_BUSY:
return;
}
break;
default:
break;
}
}
/*
* Command completion processing
*/
static void
{
int err;
char action;
/* SES_LOG(ssc, SES_CE_DEBUG9, "ses_callback"); */
/*
* Optimization: Normal completion.
*/
return;
}
/*
* Abnormal completion.
*
* Assume most common error initially.
*/
}
/* Process transport errors. */
switch (pkt->pkt_reason) {
case CMD_TIMEOUT:
/*
* If the transport layer didn't clear the problem,
* reset the target.
*/
}
break;
case CMD_INCOMPLETE:
case CMD_UNX_BUS_FREE:
/*
* No response? If probing, give up.
* Otherwise, keep trying until retries exhausted.
* Then lockdown the driver as the device is
* unplugged.
*/
}
}
/* SES_CMD_RETRY4(ssc->ses_retries); */
break;
case CMD_DATA_OVR:
/*
* XXX: Some HBA's (e.g. Adaptec 1740 and
* earlier ISP revs) report a DATA OVERRUN
* error instead of a transfer residue. So,
* we convert the error and restart.
*/
"ignoring overrun");
goto CHECK_PKT;
}
/* err = EIO; */
break;
case CMD_DMA_DERR:
break;
default:
/* err = EIO; */
break;
}
"Request Sense ",
(ssc->ses_retries > 0)?
"retrying": "giving up");
} else {
(ssc->ses_retries > 0)?
"retrying": "giving up");
}
/* Device exists, allow full error recovery. */
}
/*
* Process status and sense data errors.
*/
} else {
}
/*
* Initiate error recovery action, as needed.
*/
switch (action) {
case QUE_COMMAND_NOW:
/* SES_LOG(ssc, SES_CE_DEBUG1, "retrying cmd now"); */
scmd->uscsi_status = 0;
sizeof (struct scsi_arq_status));
!= TRAN_ACCEPT) {
}
return;
}
break;
case QUE_COMMAND:
ssc->ses_retries -=
scmd->uscsi_status = 0;
sizeof (struct scsi_arq_status));
return;
}
break;
case QUE_SENSE:
scmd->uscsi_status = 0;
sizeof (struct scsi_extended_sense));
}
return;
}
break;
case COMMAND_DONE:
"transfer residue %ld(%ld)",
}
break;
}
if (err) {
}
}
/*
* Check status and sense data and determine recovery.
*/
static int
{
struct scsi_extended_sense *sense =
char sense_flag = 0;
char *err_action;
char action;
/*
* Process manual request sense.
* Copy manual request sense to sense buffer.
*
* This is done if auto request sense is not enabled.
* Or the auto request sense failed and the request
* sense needs to be retried.
*/
sense_flag = 1;
/*
* Process auto request sense.
* Copy auto request sense to sense buffer.
*
* If auto request sense failed due to transport error,
* retry the command. Otherwise process the status and
* sense data.
*/
struct scsi_arq_status *arq =
return (QUE_COMMAND);
}
sense_flag = 1;
}
/*
* Check status of REQUEST SENSE or command.
*
* If it's not successful, try retrying the original command
* and hope that it goes away. If not, we'll eventually run
* out of retries and die.
*/
switch (status) {
case STATUS_GOOD:
case STATUS_INTERMEDIATE:
case STATUS_MET:
/*
* If the command status is ok, we're done.
* Otherwise, examine the request sense data.
*/
if (! sense_flag) {
return (COMMAND_DONE);
}
break;
case STATUS_CHECK:
return (QUE_SENSE);
/* break; */
case STATUS_BUSY:
/* SES_CMD_RETRY2(ssc->ses_retries); */
return (QUE_COMMAND);
/* break; */
return (COMMAND_DONE_ERROR);
/* break; */
case STATUS_TERMINATED:
return (COMMAND_DONE_ERROR);
/* break; */
default:
return (QUE_COMMAND);
/* break; */
}
/*
* Check REQUEST SENSE error code.
*
* Either there's no error, a retryable error,
* or it's dead. SES devices aren't very complex.
*/
err_action = "retrying";
case KEY_RECOVERABLE_ERROR:
err_action = "recovered";
break;
case KEY_UNIT_ATTENTION:
/*
* This is common for RAID!
*/
/* *err = EIO; */
break;
case KEY_NOT_READY:
case KEY_NO_SENSE:
/* *err = EIO; */
break;
default:
/* *err = EIO; */
err_action = "fatal";
break;
}
#ifdef not
/*
* Dump cdb and sense data stat's for manufacturing.
*/
auto buf[128];
if ((j = scsi_cdb_size[CDB_GROUPID(*p)]) == 0)
j = CDB_SIZE;
/* Print cdb */
for (i = 0; i < j; i++) {
}
/* Suppress trailing zero's in sense data */
if (amt > 3) {
for (j = amt; j > 3; j--) {
if (*(--p)) break;
}
} else {
j = amt;
}
/* Print sense data. */
for (i = 0; i < j; i++) {
}
}
#endif /* not */
return (action);
}
/*PRINTFLIKE3*/
void
{
char buf[256];
switch (level) {
case SES_CE_DEBUG1:
if (ses_debug > 1)
break;
case SES_CE_DEBUG2:
if (ses_debug > 2)
break;
case SES_CE_DEBUG3:
if (ses_debug > 3)
break;
case SES_CE_DEBUG4:
if (ses_debug > 4)
break;
case SES_CE_DEBUG5:
if (ses_debug > 5)
break;
case SES_CE_DEBUG6:
if (ses_debug > 6)
break;
case SES_CE_DEBUG7:
if (ses_debug > 7)
break;
case SES_CE_DEBUG8:
if (ses_debug > 8)
break;
case SES_CE_DEBUG9:
if (ses_debug > 9)
break;
case CE_NOTE:
case CE_WARN:
case CE_PANIC:
break;
case SES_CE_DEBUG:
default:
break;
}
return;
}
switch (level) {
case CE_CONT:
case CE_NOTE:
case CE_WARN:
case CE_PANIC:
break;
case SES_CE_DEBUG1:
if (ses_debug > 1)
break;
case SES_CE_DEBUG2:
if (ses_debug > 2)
break;
case SES_CE_DEBUG3:
if (ses_debug > 3)
break;
case SES_CE_DEBUG4:
if (ses_debug > 4)
break;
case SES_CE_DEBUG5:
if (ses_debug > 5)
break;
case SES_CE_DEBUG6:
if (ses_debug > 6)
break;
case SES_CE_DEBUG7:
if (ses_debug > 7)
break;
case SES_CE_DEBUG8:
if (ses_debug > 8)
break;
case SES_CE_DEBUG9:
if (ses_debug > 9)
break;
case SES_CE_DEBUG:
default:
break;
}
}
/*
* mode: c
* Local variables:
* c-indent-level: 8
* c-brace-imaginary-offset: 0
* c-brace-offset: -8
* c-argdecl-indent: 8
* c-label-offset: -8
* c-continued-statement-offset: 8
* c-continued-brace-offset: 0
* End:
*/