/* * 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 #include #include #include #include #include /* * 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: */