#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>
#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 };
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);
}
if (sdata[10] & 0x80)
baseid = 8;
else
baseid = 0;
oid = 0;
mutex_enter(&ssc->ses_devp->sd_mutex);
for (i = 0; i < ssc->ses_nobjects; i++)
ssc->ses_objmap[i].svalid = 0;
ssc->ses_encstat = 0;
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) {
ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
} else {
ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTINSTALLED;
ssc->ses_encstat |= ENCSTAT_INFO;
}
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;
}
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) {
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;
}
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) {
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) {
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;
}
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[3] = 0x8;
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;
}
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) {
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 ((obp->cstat[0] & SESCTL_CSEL) == 0) {
return (0);
}
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);
}
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;
runcmd++;
}
break;
case SESTYP_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);
} 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);
}