/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
*/
/*
* Enclosure Services Devices, SEN Enclosure Routines
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/modctl.h>
#include <sys/file.h>
#include <sys/scsi/scsi.h>
#include <sys/stat.h>
#include <sys/scsi/targets/sesio.h>
#include <sys/scsi/targets/ses.h>
/*
* The SEN unit is wired to support 7 disk units,
* two power supplies, one fan module, one overtemp sensor,
* and one alarm.
*/
#define NOBJECTS (7+2+1+1+1)
#define DRVOFF 0
#define SDRVOFF 20
#define NDRV 7
#define PWROFF NDRV
#define SPWROFF 28
#define NPWR 2
#define FANOFF (PWROFF + NPWR)
#define SFANOFF 30
#define NFAN 1
#define THMOFF (FANOFF + NFAN)
#define STHMOFF 31
#define NTHM 1
#define ALRMOFF (THMOFF + NTHM)
#define NALRM 1
#define SALRMOFF 8
#define SENPGINSIZE 32
#define SENPGOUTSIZE 22
int
sen_softc_init(ses_softc_t *ssc, int doinit)
{
int i;
if (doinit == 0) {
mutex_enter(&ssc->ses_devp->sd_mutex);
if (ssc->ses_nobjects) {
kmem_free(ssc->ses_objmap,
ssc->ses_nobjects * sizeof (encobj));
ssc->ses_objmap = NULL;
ssc->ses_nobjects = 0;
}
mutex_exit(&ssc->ses_devp->sd_mutex);
return (0);
}
mutex_enter(&ssc->ses_devp->sd_mutex);
ssc->ses_nobjects = 0;
ssc->ses_encstat = 0;
ssc->ses_objmap = (encobj *)
kmem_zalloc(NOBJECTS * sizeof (encobj), KM_SLEEP);
if (ssc->ses_objmap == NULL) {
mutex_exit(&ssc->ses_devp->sd_mutex);
return (ENOMEM);
}
for (i = DRVOFF; i < DRVOFF + NDRV; i++) {
ssc->ses_objmap[i].enctype = SESTYP_DEVICE;
}
for (i = PWROFF; i < PWROFF + NPWR; i++) {
ssc->ses_objmap[i].enctype = SESTYP_POWER;
}
for (i = FANOFF; i < FANOFF + NFAN; i++) {
ssc->ses_objmap[i].enctype = SESTYP_FAN;
}
for (i = THMOFF; i < THMOFF + NTHM; i++) {
ssc->ses_objmap[i].enctype = SESTYP_THERM;
}
for (i = ALRMOFF; i < ALRMOFF + NALRM; i++) {
ssc->ses_objmap[i].enctype = SESTYP_ALARM;
}
ssc->ses_nobjects = NOBJECTS;
mutex_exit(&ssc->ses_devp->sd_mutex);
return (0);
}
int
sen_init_enc(ses_softc_t *ssc)
{
UNUSED_PARAMETER(ssc);
return (0);
}
static int
sen_rdstat(ses_softc_t *ssc, int slpflag)
{
int err, i, oid, baseid, tmp;
Uscmd local, *lp = &local;
char rqbuf[SENSE_LENGTH], *sdata;
static char cdb[CDB_GROUP0] =
{ SCMD_GDIAG, 0x10, 0x4, 0, SENPGINSIZE, 0 };
/*
* Fetch current data
*/
sdata = kmem_alloc(SENPGINSIZE, slpflag);
if (sdata == NULL)
return (ENOMEM);
lp->uscsi_flags = USCSI_READ|USCSI_RQENABLE;
lp->uscsi_timeout = ses_io_time;
lp->uscsi_cdb = cdb;
lp->uscsi_bufaddr = sdata;
lp->uscsi_buflen = SENPGINSIZE;
lp->uscsi_cdblen = sizeof (cdb);
lp->uscsi_rqbuf = rqbuf;
lp->uscsi_rqlen = sizeof (rqbuf);
err = ses_runcmd(ssc, lp);
if (err) {
kmem_free(sdata, SENPGINSIZE);
return (err);
}
if ((lp->uscsi_buflen - lp->uscsi_resid) < SENPGINSIZE) {
SES_LOG(ssc, CE_NOTE, "sen_rdstat: too little data (%ld)",
lp->uscsi_buflen - lp->uscsi_resid);
kmem_free(sdata, SENPGINSIZE);
return (EIO);
}
/*
* Set base SCSI id for drives...
*/
if (sdata[10] & 0x80)
baseid = 8;
else
baseid = 0;
oid = 0;
mutex_enter(&ssc->ses_devp->sd_mutex);
/*
* Invalidate all status bits.
*/
for (i = 0; i < ssc->ses_nobjects; i++)
ssc->ses_objmap[i].svalid = 0;
ssc->ses_encstat = 0;
/*
* Do Drives...
*/
for (i = SDRVOFF; i < SDRVOFF + NDRV; i++) {
ssc->ses_objmap[oid].encstat[1] = baseid + i - SDRVOFF;
ssc->ses_objmap[oid].encstat[2] = 0;
if (sdata[i] & 0x80) {
/*
* Drive is present
*/
ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
} else {
ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTINSTALLED;
ssc->ses_encstat |= ENCSTAT_INFO;
}
/*
* Is the fault LED lit?
*/
if (sdata[i] & 0x40) {
ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
ssc->ses_objmap[oid].encstat[3] = 0x40;
ssc->ses_encstat |= ENCSTAT_CRITICAL;
} else {
ssc->ses_objmap[oid].encstat[3] = 0x0;
}
ssc->ses_objmap[oid++].svalid = 1;
}
/*
* Do Power Supplies...
*
* Power supply bad, or not installed cannot be distinguished.
* Which one to pick? Let's say 'bad' and make it NONCRITICAL
* if only one is bad but CRITICAL if both are bad.
*/
for (tmp = 0, i = SPWROFF; i < SPWROFF + NPWR; i++) {
ssc->ses_objmap[oid].encstat[1] = 0;
ssc->ses_objmap[oid].encstat[2] = 0;
if ((sdata[i] & 0x80) == 0) {
/*
* Power supply 'ok'...
*/
ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
tmp++;
} else {
ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
ssc->ses_encstat |= ENCSTAT_NONCRITICAL;
}
ssc->ses_objmap[oid++].svalid = 1;
}
if (tmp == 0) {
ssc->ses_encstat |= ENCSTAT_CRITICAL;
}
/*
* Do the Fan(s)
*/
for (i = SFANOFF; i < SFANOFF + NFAN; i++) {
ssc->ses_objmap[oid].encstat[1] = 0;
ssc->ses_objmap[oid].encstat[2] = 0;
if (sdata[i] & 0x20) { /* both fans have failed */
ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
ssc->ses_objmap[oid].encstat[3] = 0x40;
ssc->ses_encstat |= ENCSTAT_CRITICAL;
} else if (sdata[i] & 0x80) { /* one fan has failed */
ssc->ses_objmap[oid].encstat[0] = SESSTAT_NONCRIT;
ssc->ses_objmap[oid].encstat[3] = 0x41;
ssc->ses_encstat |= ENCSTAT_NONCRITICAL;
} else {
ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
ssc->ses_objmap[oid].encstat[3] = 0x6;
}
ssc->ses_objmap[oid++].svalid = 1;
}
/*
* Do the temperature sensor...
*/
for (i = STHMOFF; i < STHMOFF + NTHM; i++) {
ssc->ses_objmap[oid].encstat[1] = 0;
if (sdata[i] & 0x80) {
ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
/* ssc->ses_objmap[oid].encstat[2] = 0; */
ssc->ses_objmap[oid].encstat[3] = 0x8;
ssc->ses_encstat |= ENCSTAT_CRITICAL;
} else {
ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
/* ssc->ses_objmap[oid].encstat[2] = 0; */
ssc->ses_objmap[oid].encstat[3] = 0;
}
ssc->ses_objmap[oid++].svalid = 1;
}
/*
* and last, but not least, check the state of the alarm.
*/
for (i = SALRMOFF; i < SALRMOFF + NALRM; i++) {
ssc->ses_objmap[oid].encstat[1] = 0;
ssc->ses_objmap[oid].encstat[2] = 0;
if (sdata[i] & 0x80) { /* Alarm is or was sounding */
ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
ssc->ses_objmap[oid].encstat[3] = 0x2;
if ((sdata[i] & 0xf))
ssc->ses_objmap[oid].encstat[3] |= 0x40;
ssc->ses_encstat |= ENCSTAT_CRITICAL;
} else {
ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
ssc->ses_objmap[oid].encstat[3] = 0;
}
ssc->ses_objmap[oid++].svalid = 1;
}
ssc->ses_encstat |= ENCI_SVALID;
mutex_exit(&ssc->ses_devp->sd_mutex);
kmem_free(sdata, SENPGINSIZE);
return (0);
}
int
sen_get_encstat(ses_softc_t *ssc, int slpflag)
{
return (sen_rdstat(ssc, slpflag));
}
int
sen_set_encstat(ses_softc_t *ssc, uchar_t encstat, int slpflag)
{
UNUSED_PARAMETER(ssc);
UNUSED_PARAMETER(encstat);
UNUSED_PARAMETER(slpflag);
return (0);
}
int
sen_get_objstat(ses_softc_t *ssc, ses_objarg *obp, int slpflag)
{
int i = (int)obp->obj_id;
if ((ssc->ses_encstat & ENCI_SVALID) == 0 ||
(ssc->ses_objmap[i].svalid) == 0) {
int r = sen_rdstat(ssc, slpflag);
if (r)
return (r);
}
obp->cstat[0] = ssc->ses_objmap[i].encstat[0];
obp->cstat[1] = ssc->ses_objmap[i].encstat[1];
obp->cstat[2] = ssc->ses_objmap[i].encstat[2];
obp->cstat[3] = ssc->ses_objmap[i].encstat[3];
return (0);
}
int
sen_set_objstat(ses_softc_t *ssc, ses_objarg *obp, int slpflag)
{
encobj *ep;
int err, runcmd, idx;
Uscmd local, *lp = &local;
char rqbuf[SENSE_LENGTH], *sdata;
static char cdb[CDB_GROUP0] =
{ SCMD_GDIAG, 0x10, 0x4, 0, SENPGINSIZE, 0 };
static char cdb1[CDB_GROUP0] =
{ SCMD_SDIAG, 0x10, 0, 0, SENPGOUTSIZE, 0 };
/*
* If this is clear, we don't do diddly.
*/
if ((obp->cstat[0] & SESCTL_CSEL) == 0) {
return (0);
}
/*
* Fetch current data
*/
sdata = kmem_alloc(SENPGINSIZE, slpflag);
if (sdata == NULL)
return (ENOMEM);
lp->uscsi_flags = USCSI_READ|USCSI_RQENABLE;
lp->uscsi_timeout = ses_io_time;
lp->uscsi_cdb = cdb;
lp->uscsi_bufaddr = sdata;
lp->uscsi_buflen = SENPGINSIZE;
lp->uscsi_cdblen = sizeof (cdb);
lp->uscsi_rqbuf = rqbuf;
lp->uscsi_rqlen = sizeof (rqbuf);
err = ses_runcmd(ssc, lp);
if (err) {
kmem_free(sdata, SENPGINSIZE);
return (err);
}
if ((lp->uscsi_buflen - lp->uscsi_resid) < SENPGINSIZE) {
SES_LOG(ssc, CE_NOTE, "Too Little Data Returned (%ld)",
lp->uscsi_buflen - lp->uscsi_resid);
kmem_free(sdata, SENPGINSIZE);
return (EIO);
}
/*
* Okay, now convert the input page to the output page.
*/
sdata[1] = 0;
sdata[3] = 0x12;
sdata[6] = 1;
sdata[8] &= ~0x80;
sdata[10] = 0;
sdata[14] = sdata[20] & ~0x80;
sdata[15] = sdata[21] & ~0x80;
sdata[16] = sdata[22] & ~0x80;
sdata[17] = sdata[23] & ~0x80;
sdata[18] = sdata[24] & ~0x80;
sdata[19] = sdata[25] & ~0x80;
sdata[20] = sdata[26] & ~0x80;
sdata[21] = 0;
runcmd = 0;
idx = (int)obp->obj_id;
ep = &ssc->ses_objmap[idx];
switch (ep->enctype) {
case SESTYP_DEVICE:
if (idx < 0 || idx >= NDRV) {
err = EINVAL;
} else if ((obp->cstat[3] & SESCTL_RQSFLT) != 0) {
SES_LOG(ssc, SES_CE_DEBUG1, "faulted %d", idx);
sdata[14 + idx] |= 0x40;
runcmd++;
} else {
SES_LOG(ssc, SES_CE_DEBUG1, "clrd fault on %d", idx);
sdata[14 + idx] &= ~0x40;
runcmd++;
}
break;
case SESTYP_POWER:
if ((obp->cstat[3] & SESCTL_RQSTFAIL) ||
(obp->cstat[0] & SESCTL_DISABLE)) {
SES_LOG(ssc, CE_WARN, "Commanding Off Power Supply!");
sdata[10] |= 0x40; /* Seppuku!!!! */
runcmd++;
}
break;
case SESTYP_ALARM:
/*
* On all nonzero but the 'muted' bit,
* we turn on the alarm,
*/
obp->cstat[3] &= ~0xa;
if ((obp->cstat[3] & 0x40) ||
(obp->cstat[0] & SESCTL_DISABLE)) {
sdata[8] = 0;
} else if (obp->cstat[3] != 0) {
sdata[8] = 0x40;
} else {
sdata[8] = 0;
}
runcmd++;
SES_LOG(ssc, SES_CE_DEBUG1, "%sabling alarm",
(sdata[8] & 0x40)? "en" : "dis");
break;
default:
break;
}
if (runcmd) {
lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE;
lp->uscsi_timeout = ses_io_time;
lp->uscsi_cdb = cdb1;
lp->uscsi_bufaddr = sdata;
lp->uscsi_buflen = SENPGOUTSIZE;
lp->uscsi_cdblen = sizeof (cdb);
lp->uscsi_rqbuf = rqbuf;
lp->uscsi_rqlen = sizeof (rqbuf);
err = ses_runcmd(ssc, lp);
/* preserve error across the rest of the action */
} else {
err = 0;
}
mutex_enter(&ssc->ses_devp->sd_mutex);
ep->svalid = 0;
mutex_exit(&ssc->ses_devp->sd_mutex);
kmem_free(sdata, SENPGINSIZE);
return (err);
}
/*
* 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:
*/