diff options
author | mjacob <mjacob@FreeBSD.org> | 2000-01-17 00:36:28 +0000 |
---|---|---|
committer | mjacob <mjacob@FreeBSD.org> | 2000-01-17 00:36:28 +0000 |
commit | 8febf895d87217afc9fe095bd19df9f8ed523720 (patch) | |
tree | b399d5bd3a41ef618ebc9de9a5ba87f4d996f39f /sys/cam | |
parent | b28a2a02c556bdada81d6da3ff7b8a80b8d0942c (diff) | |
download | FreeBSD-src-8febf895d87217afc9fe095bd19df9f8ed523720.zip FreeBSD-src-8febf895d87217afc9fe095bd19df9f8ed523720.tar.gz |
Add in SAF-TE handling code and do some minor consistency cleanups.
Diffstat (limited to 'sys/cam')
-rw-r--r-- | sys/cam/scsi/scsi_ses.c | 1027 |
1 files changed, 1017 insertions, 10 deletions
diff --git a/sys/cam/scsi/scsi_ses.c b/sys/cam/scsi/scsi_ses.c index 8207b13..b7065a4 100644 --- a/sys/cam/scsi/scsi_ses.c +++ b/sys/cam/scsi/scsi_ses.c @@ -81,12 +81,12 @@ typedef struct { #define ENCI_SVALID 0x80 typedef struct { - u_int32_t + uint32_t enctype : 8, /* enclosure type */ subenclosure : 8, /* subenclosure id */ svalid : 1, /* enclosure information valid */ priv : 15; /* private data, per object */ - u_int8_t encstat[4]; /* state && stats */ + uint8_t encstat[4]; /* state && stats */ } encobj; #define SEN_ID "UNISYS SUN_SEN" @@ -104,6 +104,13 @@ static int ses_set_encstat(ses_softc_t *, uint8_t, int); static int ses_get_objstat(ses_softc_t *, ses_objstat *, int); static int ses_set_objstat(ses_softc_t *, ses_objstat *, int); +static int safte_softc_init(ses_softc_t *, int); +static int safte_init_enc(ses_softc_t *); +static int safte_get_encstat(ses_softc_t *, int); +static int safte_set_encstat(ses_softc_t *, uint8_t, int); +static int safte_get_objstat(ses_softc_t *, ses_objstat *, int); +static int safte_set_objstat(ses_softc_t *, ses_objstat *, int); + /* * Platform implementation defines/functions for SES internal kernel stuff */ @@ -285,7 +292,7 @@ sesasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) * PROBLEM: WE NEED TO LOOK AT BYTES 48-53 TO SEE IF THIS IS * PROBLEM: IS A SAF-TE DEVICE. */ - switch (ses_type(&cgd->inq_data, sizeof (cgd->inq_data))) { + switch (ses_type(&cgd->inq_data, cgd->inq_len)) { case SES_SES: case SES_SES_SCSI2: case SES_SES_PASSTHROUGH: @@ -354,8 +361,15 @@ sesregister(struct cam_periph *periph, void *arg) softc->ses_vec.get_objstat = ses_get_objstat; softc->ses_vec.set_objstat = ses_set_objstat; break; - case SES_SEN: case SES_SAFT: + softc->ses_vec.softc_init = safte_softc_init; + softc->ses_vec.init_enc = safte_init_enc; + softc->ses_vec.get_encstat = safte_get_encstat; + softc->ses_vec.set_encstat = safte_set_encstat; + softc->ses_vec.get_objstat = safte_get_objstat; + softc->ses_vec.set_objstat = safte_set_objstat; + break; + case SES_SEN: break; case SES_NONE: default: @@ -395,7 +409,7 @@ sesregister(struct cam_periph *periph, void *arg) tname = "SES Passthrough Device"; break; case SES_SEN: - tname = "Unisys SEN Device"; + tname = "UNISYS SEN Device (NOT HANDLED YET)"; break; case SES_SAFT: tname = "SAF-TE Compliant Device"; @@ -709,7 +723,7 @@ ses_log(struct ses_softc *ssc, const char *fmt, ...) /* * The code after this point runs on many platforms, - * so forgive the slightly awkward and nocorming + * so forgive the slightly awkward and nonconforming * appearance. */ @@ -935,8 +949,8 @@ struct sscfg { static int ses_getconfig(ses_softc_t *); static int ses_getputstat(ses_softc_t *, int, SesComStat *, int, int); static int ses_cfghdr(uint8_t *, int, SesCfgHdr *); -static int ses_enchdr(uint8_t *, int, u_char, SesEncHdr *); -static int ses_encdesc(uint8_t *, int, u_char, SesEncDesc *); +static int ses_enchdr(uint8_t *, int, uint8_t, SesEncHdr *); +static int ses_encdesc(uint8_t *, int, uint8_t, SesEncDesc *); static int ses_getthdr(uint8_t *, int, int, SesThdr *); static int ses_decode(char *, int, uint8_t *, int, int, SesComStat *); static int ses_encode(char *, int, uint8_t *, int, int, SesComStat *); @@ -1296,7 +1310,7 @@ ses_cfghdr(uint8_t *buffer, int buflen, SesCfgHdr *cfp) } static int -ses_enchdr(uint8_t *buffer, int amt, u_char SubEncId, SesEncHdr *chp) +ses_enchdr(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncHdr *chp) { int s, off = 8; for (s = 0; s < SubEncId; s++) { @@ -1314,7 +1328,7 @@ ses_enchdr(uint8_t *buffer, int amt, u_char SubEncId, SesEncHdr *chp) } static int -ses_encdesc(uint8_t *buffer, int amt, u_char SubEncId, SesEncDesc *cdp) +ses_encdesc(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncDesc *cdp) { int s, e, enclen, off = 8; for (s = 0; s < SubEncId; s++) { @@ -1546,3 +1560,996 @@ ses_encode(char *b, int amt, uint8_t *ep, int elt, int elm, SesComStat *sp) return (0); } + +/* + * SAF-TE Type Device Emulation + */ + +static int safte_getconfig(ses_softc_t *); +static int safte_rdstat(ses_softc_t *, int);; +static int set_objstat_sel(ses_softc_t *, ses_objstat *, int); +static int wrbuf16(ses_softc_t *, uint8_t, uint8_t, uint8_t, uint8_t, int); +static void wrslot_stat(ses_softc_t *, int); +static int perf_slotop(ses_softc_t *, uint8_t, uint8_t, int); + +#define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \ + SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) +/* + * SAF-TE specific defines- Mandatory ones only... + */ + +/* + * READ BUFFER ('get' commands) IDs- placed in offset 2 of cdb + */ +#define SAFTE_RD_RDCFG 0x00 /* read enclosure configuration */ +#define SAFTE_RD_RDESTS 0x01 /* read enclosure status */ +#define SAFTE_RD_RDDSTS 0x04 /* read drive slot status */ + +/* + * WRITE BUFFER ('set' commands) IDs- placed in offset 0 of databuf + */ +#define SAFTE_WT_DSTAT 0x10 /* write device slot status */ +#define SAFTE_WT_SLTOP 0x12 /* perform slot operation */ +#define SAFTE_WT_FANSPD 0x13 /* set fan speed */ +#define SAFTE_WT_ACTPWS 0x14 /* turn on/off power supply */ +#define SAFTE_WT_GLOBAL 0x15 /* send global command */ + + +#define SAFT_SCRATCH 64 +#define NPSEUDO_THERM 16 +#define NPSEUDO_ALARM 1 +struct scfg { + /* + * Cached Configuration + */ + uint8_t Nfans; /* Number of Fans */ + uint8_t Npwr; /* Number of Power Supplies */ + uint8_t Nslots; /* Number of Device Slots */ + uint8_t DoorLock; /* Door Lock Installed */ + uint8_t Ntherm; /* Number of Temperature Sensors */ + uint8_t Nspkrs; /* Number of Speakers */ + uint8_t Nalarm; /* Number of Alarms (at least one) */ + /* + * Cached Flag Bytes for Global Status + */ + uint8_t flag1; + uint8_t flag2; + /* + * What object index ID is where various slots start. + */ + uint8_t pwroff; + uint8_t slotoff; +#define SAFT_ALARM_OFFSET(cc) (cc)->slotoff - 1 +}; + +#define SAFT_FLG1_ALARM 0x1 +#define SAFT_FLG1_GLOBFAIL 0x2 +#define SAFT_FLG1_GLOBWARN 0x4 +#define SAFT_FLG1_ENCPWROFF 0x8 +#define SAFT_FLG1_ENCFANFAIL 0x10 +#define SAFT_FLG1_ENCPWRFAIL 0x20 +#define SAFT_FLG1_ENCDRVFAIL 0x40 +#define SAFT_FLG1_ENCDRVWARN 0x80 + +#define SAFT_FLG2_LOCKDOOR 0x4 +#define SAFT_PRIVATE sizeof (struct scfg) + +static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; +#define SAFT_BAIL(r, x, k, l) \ + if (r >= x) { \ + SES_LOG(ssc, safte_2little, x, __LINE__);\ + SES_FREE(k, l); \ + return (EIO); \ + } + + +int +safte_softc_init(ses_softc_t *ssc, int doinit) +{ + int err, i, r; + struct scfg *cc; + + if (doinit == 0) { + if (ssc->ses_nobjects) { + if (ssc->ses_objmap) { + SES_FREE(ssc->ses_objmap, + ssc->ses_nobjects * sizeof (encobj)); + ssc->ses_objmap = NULL; + } + ssc->ses_nobjects = 0; + } + if (ssc->ses_private) { + SES_FREE(ssc->ses_private, SAFT_PRIVATE); + ssc->ses_private = NULL; + } + return (0); + } + + if (ssc->ses_private == NULL) { + ssc->ses_private = SES_MALLOC(SAFT_PRIVATE); + if (ssc->ses_private == NULL) { + return (ENOMEM); + } + MEMZERO(ssc->ses_private, SAFT_PRIVATE); + } + + ssc->ses_nobjects = 0; + ssc->ses_encstat = 0; + + if ((err = safte_getconfig(ssc)) != 0) { + return (err); + } + + /* + * The number of objects here, as well as that reported by the + * READ_BUFFER/GET_CONFIG call, are the over-temperature flags (15) + * that get reported during READ_BUFFER/READ_ENC_STATUS. + */ + cc = ssc->ses_private; + ssc->ses_nobjects = cc->Nfans + cc->Npwr + cc->Nslots + cc->DoorLock + + cc->Ntherm + cc->Nspkrs + NPSEUDO_THERM + NPSEUDO_ALARM; + ssc->ses_objmap = (encobj *) + SES_MALLOC(ssc->ses_nobjects * sizeof (encobj)); + if (ssc->ses_objmap == NULL) { + return (ENOMEM); + } + MEMZERO(ssc->ses_objmap, ssc->ses_nobjects * sizeof (encobj)); + + r = 0; + /* + * Note that this is all arranged for the convenience + * in later fetches of status. + */ + for (i = 0; i < cc->Nfans; i++) + ssc->ses_objmap[r++].enctype = SESTYP_FAN; + cc->pwroff = (uint8_t) r; + for (i = 0; i < cc->Npwr; i++) + ssc->ses_objmap[r++].enctype = SESTYP_POWER; + for (i = 0; i < cc->DoorLock; i++) + ssc->ses_objmap[r++].enctype = SESTYP_DOORLOCK; + for (i = 0; i < cc->Nspkrs; i++) + ssc->ses_objmap[r++].enctype = SESTYP_ALARM; + for (i = 0; i < cc->Ntherm; i++) + ssc->ses_objmap[r++].enctype = SESTYP_THERM; + for (i = 0; i < NPSEUDO_THERM; i++) + ssc->ses_objmap[r++].enctype = SESTYP_THERM; + ssc->ses_objmap[r++].enctype = SESTYP_ALARM; + cc->slotoff = (uint8_t) r; + for (i = 0; i < cc->Nslots; i++) + ssc->ses_objmap[r++].enctype = SESTYP_DEVICE; + return (0); +} + +int +safte_init_enc(ses_softc_t *ssc) +{ + int err, amt; + char *sdata; + static char cdb0[10] = { SEND_DIAGNOSTIC }; + static char cdb[10] = + { WRITE_BUFFER , 1, 0, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; + + sdata = SES_MALLOC(SAFT_SCRATCH); + if (sdata == NULL) + return (ENOMEM); + + err = ses_runcmd(ssc, cdb0, 10, NULL, 0); + if (err) { + SES_FREE(sdata, SAFT_SCRATCH); + return (err); + } + sdata[0] = SAFTE_WT_GLOBAL; + MEMZERO(&sdata[1], SAFT_SCRATCH - 1); + amt = -SAFT_SCRATCH; + err = ses_runcmd(ssc, cdb, 10, sdata, &amt); + SES_FREE(sdata, SAFT_SCRATCH); + return (err); +} + +int +safte_get_encstat(ses_softc_t *ssc, int slpflg) +{ + return (safte_rdstat(ssc, slpflg)); +} + +int +safte_set_encstat(ses_softc_t *ssc, uint8_t encstat, int slpflg) +{ + struct scfg *cc = ssc->ses_private; + if (cc == NULL) + return (0); + /* + * Since SAF-TE devices aren't necessarily sticky in terms + * of state, make our soft copy of enclosure status 'sticky'- + * that is, things set in enclosure status stay set (as implied + * by conditions set in reading object status) until cleared. + */ + ssc->ses_encstat &= ~ALL_ENC_STAT; + ssc->ses_encstat |= (encstat & ALL_ENC_STAT); + ssc->ses_encstat |= ENCI_SVALID; + cc->flag1 &= ~(SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN); + if ((encstat & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV)) != 0) { + cc->flag1 |= SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL; + } else if ((encstat & SES_ENCSTAT_NONCRITICAL) != 0) { + cc->flag1 |= SAFT_FLG1_GLOBWARN; + } + return (wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg)); +} + +int +safte_get_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflg) +{ + int i = (int)obp->obj_id; + + if ((ssc->ses_encstat & ENCI_SVALID) == 0 || + (ssc->ses_objmap[i].svalid) == 0) { + int err = safte_rdstat(ssc, slpflg); + if (err) + return (err); + } + 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 +safte_set_objstat(ses_softc_t *ssc, ses_objstat *obp, int slp) +{ + int idx, err; + encobj *ep; + struct scfg *cc; + + + SES_VLOG(ssc, "safte_set_objstat(%d): %x %x %x %x\n", + (int)obp->obj_id, obp->cstat[0], obp->cstat[1], obp->cstat[2], + obp->cstat[3]); + + /* + * If this is clear, we don't do diddly. + */ + if ((obp->cstat[0] & SESCTL_CSEL) == 0) { + return (0); + } + + err = 0; + /* + * Check to see if the common bits are set and do them first. + */ + if (obp->cstat[0] & ~SESCTL_CSEL) { + err = set_objstat_sel(ssc, obp, slp); + if (err) + return (err); + } + + cc = ssc->ses_private; + if (cc == NULL) + return (0); + + idx = (int)obp->obj_id; + ep = &ssc->ses_objmap[idx]; + + switch (ep->enctype) { + case SESTYP_DEVICE: + { + uint8_t slotop = 0; + /* + * XXX: I should probably cache the previous state + * XXX: of SESCTL_DEVOFF so that when it goes from + * XXX: true to false I can then set PREPARE FOR OPERATION + * XXX: flag in PERFORM SLOT OPERATION write buffer command. + */ + if (obp->cstat[2] & (SESCTL_RQSINS|SESCTL_RQSRMV)) { + slotop |= 0x2; + } + if (obp->cstat[2] & SESCTL_RQSID) { + slotop |= 0x4; + } + err = perf_slotop(ssc, (uint8_t) idx - (uint8_t) cc->slotoff, + slotop, slp); + if (err) + return (err); + if (obp->cstat[3] & SESCTL_RQSFLT) { + ep->priv |= 0x2; + } else { + ep->priv &= ~0x2; + } + if (ep->priv & 0xc6) { + ep->priv &= ~0x1; + } else { + ep->priv |= 0x1; /* no errors */ + } + wrslot_stat(ssc, slp); + break; + } + case SESTYP_POWER: + if (obp->cstat[3] & SESCTL_RQSTFAIL) { + cc->flag1 |= SAFT_FLG1_ENCPWRFAIL; + } else { + cc->flag1 &= ~SAFT_FLG1_ENCPWRFAIL; + } + err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + if (err) + return (err); + if (obp->cstat[3] & SESCTL_RQSTON) { + (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, + idx - cc->pwroff, 0, 0, slp); + } else { + (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, + idx - cc->pwroff, 0, 1, slp); + } + break; + case SESTYP_FAN: + if (obp->cstat[3] & SESCTL_RQSTFAIL) { + cc->flag1 |= SAFT_FLG1_ENCFANFAIL; + } else { + cc->flag1 &= ~SAFT_FLG1_ENCFANFAIL; + } + err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + if (err) + return (err); + if (obp->cstat[3] & SESCTL_RQSTON) { + uint8_t fsp; + if ((obp->cstat[3] & 0x7) == 7) { + fsp = 4; + } else if ((obp->cstat[3] & 0x7) == 6) { + fsp = 3; + } else if ((obp->cstat[3] & 0x7) == 4) { + fsp = 2; + } else { + fsp = 1; + } + (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, fsp, 0, slp); + } else { + (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); + } + break; + case SESTYP_DOORLOCK: + if (obp->cstat[3] & 0x1) { + cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; + } else { + cc->flag2 |= SAFT_FLG2_LOCKDOOR; + } + (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + 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) { + cc->flag2 &= ~SAFT_FLG1_ALARM; + } else if (obp->cstat[3] != 0) { + cc->flag2 |= SAFT_FLG1_ALARM; + } else { + cc->flag2 &= ~SAFT_FLG1_ALARM; + } + ep->priv = obp->cstat[3]; + (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + break; + default: + break; + } + ep->svalid = 0; + return (0); +} + +static int +safte_getconfig(ses_softc_t *ssc) +{ + struct scfg *cfg; + int err, amt; + char *sdata; + static char cdb[10] = + { READ_BUFFER, 1, SAFTE_RD_RDCFG, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; + + cfg = ssc->ses_private; + if (cfg == NULL) + return (ENXIO); + + sdata = SES_MALLOC(SAFT_SCRATCH); + if (sdata == NULL) + return (ENOMEM); + + amt = SAFT_SCRATCH; + err = ses_runcmd(ssc, cdb, 10, sdata, &amt); + if (err) { + SES_FREE(sdata, SAFT_SCRATCH); + return (err); + } + amt = SAFT_SCRATCH - amt; + if (amt < 6) { + SES_LOG(ssc, "too little data (%d) for configuration\n", amt); + SES_FREE(sdata, SAFT_SCRATCH); + return (EIO); + } + SES_VLOG(ssc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d\n", + sdata[0], sdata[1], sdata[2], sdata[3], sdata[4], sdata[5]); + cfg->Nfans = sdata[0]; + cfg->Npwr = sdata[1]; + cfg->Nslots = sdata[2]; + cfg->DoorLock = sdata[3]; + cfg->Ntherm = sdata[4]; + cfg->Nspkrs = sdata[5]; + cfg->Nalarm = NPSEUDO_ALARM; + SES_FREE(sdata, SAFT_SCRATCH); + return (0); +} + +static int +safte_rdstat(ses_softc_t *ssc, int slpflg) +{ + int err, oid, r, i, hiwater, nitems, amt; + uint16_t tempflags; + size_t buflen; + uint8_t status, oencstat; + char *sdata, cdb[10]; + struct scfg *cc = ssc->ses_private; + + + /* + * The number of objects overstates things a bit, + * both for the bogus 'thermometer' entries and + * the drive status (which isn't read at the same + * time as the enclosure status), but that's okay. + */ + buflen = 4 * cc->Nslots; + if (ssc->ses_nobjects > buflen) + buflen = ssc->ses_nobjects; + sdata = SES_MALLOC(buflen); + if (sdata == NULL) + return (ENOMEM); + + cdb[0] = READ_BUFFER; + cdb[1] = 1; + cdb[2] = SAFTE_RD_RDESTS; + cdb[3] = 0; + cdb[4] = 0; + cdb[5] = 0; + cdb[6] = 0; + cdb[7] = (buflen >> 8) & 0xff; + cdb[8] = buflen & 0xff; + cdb[9] = 0; + amt = buflen; + err = ses_runcmd(ssc, cdb, 10, sdata, &amt); + if (err) { + SES_FREE(sdata, buflen); + return (err); + } + hiwater = buflen - amt; + + + /* + * invalidate all status bits. + */ + for (i = 0; i < ssc->ses_nobjects; i++) + ssc->ses_objmap[i].svalid = 0; + oencstat = ssc->ses_encstat & ALL_ENC_STAT; + ssc->ses_encstat = 0; + + + /* + * Now parse returned buffer. + * If we didn't get enough data back, + * that's considered a fatal error. + */ + oid = r = 0; + + for (nitems = i = 0; i < cc->Nfans; i++) { + SAFT_BAIL(r, hiwater, sdata, buflen); + /* + * 0 = Fan Operational + * 1 = Fan is malfunctioning + * 2 = Fan is not present + * 0x80 = Unknown or Not Reportable Status + */ + ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ + ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ + switch ((int)(uint8_t)sdata[r]) { + case 0: + nitems++; + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + /* + * We could get fancier and cache + * fan speeds that we have set, but + * that isn't done now. + */ + ssc->ses_objmap[oid].encstat[3] = 7; + break; + + case 1: + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; + /* + * FAIL and FAN STOPPED synthesized + */ + ssc->ses_objmap[oid].encstat[3] = 0x40; + /* + * Enclosure marked with CRITICAL error + * if only one fan or no thermometers, + * else the NONCRITICAL error is set. + */ + if (cc->Nfans == 1 || cc->Ntherm == 0) + ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; + else + ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; + break; + case 2: + ssc->ses_objmap[oid].encstat[0] = + SES_OBJSTAT_NOTINSTALLED; + ssc->ses_objmap[oid].encstat[3] = 0; + /* + * Enclosure marked with CRITICAL error + * if only one fan or no thermometers, + * else the NONCRITICAL error is set. + */ + if (cc->Nfans == 1) + ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; + else + ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; + break; + case 0x80: + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; + ssc->ses_objmap[oid].encstat[3] = 0; + ssc->ses_encstat |= SES_ENCSTAT_INFO; + break; + default: + ssc->ses_objmap[oid].encstat[0] = + SES_OBJSTAT_UNSUPPORTED; + SES_LOG(ssc, "Unknown fan%d status 0x%x\n", i, + sdata[r] & 0xff); + break; + } + ssc->ses_objmap[oid++].svalid = 1; + r++; + } + + /* + * No matter how you cut it, no cooling elements when there + * should be some there is critical. + */ + if (cc->Nfans && nitems == 0) { + ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; + } + + + for (i = 0; i < cc->Npwr; i++) { + SAFT_BAIL(r, hiwater, sdata, buflen); + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; + ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ + ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ + ssc->ses_objmap[oid].encstat[3] = 0x20; /* requested on */ + switch ((uint8_t)sdata[r]) { + case 0x00: /* pws operational and on */ + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + break; + case 0x01: /* pws operational and off */ + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + ssc->ses_objmap[oid].encstat[3] = 0x10; + ssc->ses_encstat |= SES_ENCSTAT_INFO; + break; + case 0x10: /* pws is malfunctioning and commanded on */ + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; + ssc->ses_objmap[oid].encstat[3] = 0x61; + ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; + break; + + case 0x11: /* pws is malfunctioning and commanded off */ + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NONCRIT; + ssc->ses_objmap[oid].encstat[3] = 0x51; + ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; + break; + case 0x20: /* pws is not present */ + ssc->ses_objmap[oid].encstat[0] = + SES_OBJSTAT_NOTINSTALLED; + ssc->ses_objmap[oid].encstat[3] = 0; + ssc->ses_encstat |= SES_ENCSTAT_INFO; + break; + case 0x21: /* pws is present */ + /* + * This is for enclosures that cannot tell whether the + * device is on or malfunctioning, but know that it is + * present. Just fall through. + */ + /* FALLTHROUGH */ + case 0x80: /* Unknown or Not Reportable Status */ + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; + ssc->ses_objmap[oid].encstat[3] = 0; + ssc->ses_encstat |= SES_ENCSTAT_INFO; + break; + default: + SES_LOG(ssc, "unknown power supply %d status (0x%x)\n", + i, sdata[r] & 0xff); + break; + } + ssc->ses_objmap[oid++].svalid = 1; + r++; + } + + /* + * Skip over Slot SCSI IDs + */ + r += cc->Nslots; + + /* + * We always have doorlock status, no matter what, + * but we only save the status if we have one. + */ + SAFT_BAIL(r, hiwater, sdata, buflen); + if (cc->DoorLock) { + /* + * 0 = Door Locked + * 1 = Door Unlocked, or no Lock Installed + * 0x80 = Unknown or Not Reportable Status + */ + ssc->ses_objmap[oid].encstat[1] = 0; + ssc->ses_objmap[oid].encstat[2] = 0; + switch ((uint8_t)sdata[r]) { + case 0: + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + ssc->ses_objmap[oid].encstat[3] = 0; + break; + case 1: + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + ssc->ses_objmap[oid].encstat[3] = 1; + break; + case 0x80: + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; + ssc->ses_objmap[oid].encstat[3] = 0; + ssc->ses_encstat |= SES_ENCSTAT_INFO; + break; + default: + ssc->ses_objmap[oid].encstat[0] = + SES_OBJSTAT_UNSUPPORTED; + SES_LOG(ssc, "unknown lock status 0x%x\n", + sdata[r] & 0xff); + break; + } + ssc->ses_objmap[oid++].svalid = 1; + } + r++; + + /* + * We always have speaker status, no matter what, + * but we only save the status if we have one. + */ + SAFT_BAIL(r, hiwater, sdata, buflen); + if (cc->Nspkrs) { + ssc->ses_objmap[oid].encstat[1] = 0; + ssc->ses_objmap[oid].encstat[2] = 0; + if (sdata[r] == 1) { + /* + * We need to cache tone urgency indicators. + * Someday. + */ + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NONCRIT; + ssc->ses_objmap[oid].encstat[3] = 0x8; + ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; + } else if (sdata[r] == 0) { + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + ssc->ses_objmap[oid].encstat[3] = 0; + } else { + ssc->ses_objmap[oid].encstat[0] = + SES_OBJSTAT_UNSUPPORTED; + ssc->ses_objmap[oid].encstat[3] = 0; + SES_LOG(ssc, "unknown spkr status 0x%x\n", + sdata[r] & 0xff); + } + ssc->ses_objmap[oid++].svalid = 1; + } + r++; + + for (i = 0; i < cc->Ntherm; i++) { + SAFT_BAIL(r, hiwater, sdata, buflen); + /* + * Status is a range from -10 to 245 deg Celsius, + * which we need to normalize to -20 to -245 according + * to the latest SCSI spec, which makes little + * sense since this would overflow an 8bit value. + * Well, still, the base normalization is -20, + * not -10, so we have to adjust. + * + * So what's over and under temperature? + * Hmm- we'll state that 'normal' operating + * is 10 to 40 deg Celsius. + */ + ssc->ses_objmap[oid].encstat[1] = 0; + ssc->ses_objmap[oid].encstat[2] = + ((unsigned int) sdata[r]) - 10; + if (sdata[r] < 20) { + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; + /* + * Set 'under temperature' failure. + */ + ssc->ses_objmap[oid].encstat[3] = 2; + ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; + } else if (sdata[r] > 30) { + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; + /* + * Set 'over temperature' failure. + */ + ssc->ses_objmap[oid].encstat[3] = 8; + ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; + } else { + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + } + ssc->ses_objmap[oid++].svalid = 1; + r++; + } + + /* + * Now, for "pseudo" thermometers, we have two bytes + * of information in enclosure status- 16 bits. Actually, + * the MSB is a single TEMP ALERT flag indicating whether + * any other bits are set, but, thanks to fuzzy thinking, + * in the SAF-TE spec, this can also be set even if no + * other bits are set, thus making this really another + * binary temperature sensor. + */ + + SAFT_BAIL(r, hiwater, sdata, buflen); + tempflags = sdata[r++]; + SAFT_BAIL(r, hiwater, sdata, buflen); + tempflags |= (tempflags << 8) | sdata[r++]; + + for (i = 0; i < NPSEUDO_THERM; i++) { + ssc->ses_objmap[oid].encstat[1] = 0; + if (tempflags & (1 << (NPSEUDO_THERM - i - 1))) { + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; + ssc->ses_objmap[4].encstat[2] = 0xff; + /* + * Set 'over temperature' failure. + */ + ssc->ses_objmap[oid].encstat[3] = 8; + ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; + } else { + /* + * We used to say 'not available' and synthesize a + * nominal 30 deg (C)- that was wrong. Actually, + * Just say 'OK', and use the reserved value of + * zero. + */ + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + ssc->ses_objmap[oid].encstat[2] = 0; + ssc->ses_objmap[oid].encstat[3] = 0; + } + ssc->ses_objmap[oid++].svalid = 1; + } + + /* + * Get alarm status. + */ + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + ssc->ses_objmap[oid].encstat[3] = ssc->ses_objmap[oid].priv; + ssc->ses_objmap[oid++].svalid = 1; + + /* + * Now get drive slot status + */ + cdb[2] = SAFTE_RD_RDDSTS; + amt = buflen; + err = ses_runcmd(ssc, cdb, 10, sdata, &amt); + if (err) { + SES_FREE(sdata, buflen); + return (err); + } + hiwater = buflen - amt; + for (r = i = 0; i < cc->Nslots; i++, r += 4) { + SAFT_BAIL(r+3, hiwater, sdata, buflen); + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; + ssc->ses_objmap[oid].encstat[1] = (uint8_t) i; + ssc->ses_objmap[oid].encstat[2] = 0; + ssc->ses_objmap[oid].encstat[3] = 0; + status = sdata[r+3]; + if ((status & 0x1) == 0) { /* no device */ + ssc->ses_objmap[oid].encstat[0] = + SES_OBJSTAT_NOTINSTALLED; + } else { + ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; + } + if (status & 0x2) { + ssc->ses_objmap[oid].encstat[2] = 0x8; + } + if ((status & 0x4) == 0) { + ssc->ses_objmap[oid].encstat[3] = 0x10; + } + ssc->ses_objmap[oid++].svalid = 1; + } + /* see comment below about sticky enclosure status */ + ssc->ses_encstat |= ENCI_SVALID | oencstat; + SES_FREE(sdata, buflen); + return (0); +} + +static int +set_objstat_sel(ses_softc_t *ssc, ses_objstat *obp, int slp) +{ + int idx; + encobj *ep; + struct scfg *cc = ssc->ses_private; + + if (cc == NULL) + return (0); + + idx = (int)obp->obj_id; + ep = &ssc->ses_objmap[idx]; + + switch (ep->enctype) { + case SESTYP_DEVICE: + if (obp->cstat[0] & SESCTL_PRDFAIL) { + ep->priv |= 0x40; + } + /* SESCTL_RSTSWAP has no correspondence in SAF-TE */ + if (obp->cstat[0] & SESCTL_DISABLE) { + ep->priv |= 0x80; + /* + * Hmm. Try to set the 'No Drive' flag. + * Maybe that will count as a 'disable'. + */ + } + if (ep->priv & 0xc6) { + ep->priv &= ~0x1; + } else { + ep->priv |= 0x1; /* no errors */ + } + wrslot_stat(ssc, slp); + break; + case SESTYP_POWER: + /* + * Okay- the only one that makes sense here is to + * do the 'disable' for a power supply. + */ + if (obp->cstat[0] & SESCTL_DISABLE) { + (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, + idx - cc->pwroff, 0, 0, slp); + } + break; + case SESTYP_FAN: + /* + * Okay- the only one that makes sense here is to + * set fan speed to zero on disable. + */ + if (obp->cstat[0] & SESCTL_DISABLE) { + /* remember- fans are the first items, so idx works */ + (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); + } + break; + case SESTYP_DOORLOCK: + /* + * Well, we can 'disable' the lock. + */ + if (obp->cstat[0] & SESCTL_DISABLE) { + cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; + (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + } + break; + case SESTYP_ALARM: + /* + * Well, we can 'disable' the alarm. + */ + if (obp->cstat[0] & SESCTL_DISABLE) { + cc->flag2 &= ~SAFT_FLG1_ALARM; + ep->priv |= 0x40; /* Muted */ + (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, + cc->flag2, 0, slp); + } + break; + default: + break; + } + ep->svalid = 0; + return (0); +} + +/* + * This function handles all of the 16 byte WRITE BUFFER commands. + */ +static int +wrbuf16(ses_softc_t *ssc, uint8_t op, uint8_t b1, uint8_t b2, + uint8_t b3, int slp) +{ + int err, amt; + char *sdata; + struct scfg *cc = ssc->ses_private; + static char cdb[10] = { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, 16, 0 }; + + if (cc == NULL) + return (0); + + sdata = SES_MALLOC(16); + if (sdata == NULL) + return (ENOMEM); + + SES_VLOG(ssc, "saf_wrbuf16 %x %x %x %x\n", op, b1, b2, b3); + + sdata[0] = op; + sdata[1] = b1; + sdata[2] = b2; + sdata[3] = b3; + MEMZERO(&sdata[4], 12); + amt = -16; + err = ses_runcmd(ssc, cdb, 10, sdata, &amt); + SES_FREE(sdata, 16); + return (err); +} + +/* + * This function updates the status byte for the device slot described. + * + * Since this is an optional SAF-TE command, there's no point in + * returning an error. + */ +static void +wrslot_stat(ses_softc_t *ssc, int slp) +{ + int i, amt; + encobj *ep; + char cdb[10], *sdata; + struct scfg *cc = ssc->ses_private; + + if (cc == NULL) + return; + + SES_VLOG(ssc, "saf_wrslot\n"); + cdb[0] = WRITE_BUFFER; + cdb[1] = 1; + cdb[2] = 0; + cdb[3] = 0; + cdb[4] = 0; + cdb[5] = 0; + cdb[6] = 0; + cdb[7] = 0; + cdb[8] = cc->Nslots * 3 + 1; + cdb[9] = 0; + + sdata = SES_MALLOC(cc->Nslots * 3 + 1); + if (sdata == NULL) + return; + MEMZERO(sdata, cc->Nslots * 3 + 1); + + sdata[0] = SAFTE_WT_DSTAT; + for (i = 0; i < cc->Nslots; i++) { + ep = &ssc->ses_objmap[cc->slotoff + i]; + SES_VLOG(ssc, "saf_wrslot %d <- %x\n", i, ep->priv & 0xff); + sdata[1 + (3 * i)] = ep->priv & 0xff; + } + amt = -(cc->Nslots * 3 + 1); + (void) ses_runcmd(ssc, cdb, 10, sdata, &amt); + SES_FREE(sdata, cc->Nslots * 3 + 1); +} + +/* + * This function issues the "PERFORM SLOT OPERATION" command. + */ +static int +perf_slotop(ses_softc_t *ssc, uint8_t slot, uint8_t opflag, int slp) +{ + int err, amt; + char *sdata; + struct scfg *cc = ssc->ses_private; + static char cdb[10] = + { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; + + if (cc == NULL) + return (0); + + sdata = SES_MALLOC(SAFT_SCRATCH); + if (sdata == NULL) + return (ENOMEM); + MEMZERO(sdata, SAFT_SCRATCH); + + sdata[0] = SAFTE_WT_SLTOP; + sdata[1] = slot; + sdata[2] = opflag; + SES_VLOG(ssc, "saf_slotop slot %d op %x\n", slot, opflag); + amt = -SAFT_SCRATCH; + err = ses_runcmd(ssc, cdb, 10, sdata, &amt); + SES_FREE(sdata, SAFT_SCRATCH); + return (err); +} |