/* $OpenBSD: safte.c,v 1.56 2019/09/01 15:03:32 krw Exp $ */ /* * Copyright (c) 2005 David Gwynne * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "bio.h" #include #include #include #include #include #include #include #include #include #if NBIO > 0 #include #endif #include #include #include #ifdef SAFTE_DEBUG #define DPRINTF(x) do { if (safte_debug) printf x ; } while (0) int safte_debug = 1; #else #define DPRINTF(x) /* x */ #endif int safte_match(struct device *, void *, void *); void safte_attach(struct device *, struct device *, void *); int safte_detach(struct device *, int); struct safte_sensor { struct ksensor se_sensor; enum { SAFTE_T_FAN, SAFTE_T_PWRSUP, SAFTE_T_DOORLOCK, SAFTE_T_ALARM, SAFTE_T_TEMP } se_type; u_int8_t *se_field; }; struct safte_softc { struct device sc_dev; struct scsi_link *sc_link; struct rwlock sc_lock; u_int sc_encbuflen; u_char *sc_encbuf; int sc_nsensors; struct safte_sensor *sc_sensors; struct ksensordev sc_sensordev; struct sensor_task *sc_sensortask; int sc_celsius; int sc_ntemps; struct safte_sensor *sc_temps; u_int8_t *sc_temperrs; #if NBIO > 0 int sc_nslots; u_int8_t *sc_slots; #endif }; struct cfattach safte_ca = { sizeof(struct safte_softc), safte_match, safte_attach, safte_detach }; struct cfdriver safte_cd = { NULL, "safte", DV_DULL }; #define DEVNAME(s) ((s)->sc_dev.dv_xname) int safte_read_config(struct safte_softc *); void safte_read_encstat(void *); #if NBIO > 0 int safte_ioctl(struct device *, u_long, caddr_t); int safte_bio_blink(struct safte_softc *, struct bioc_blink *); #endif int64_t safte_temp2uK(u_int8_t, int); int safte_match(struct device *parent, void *match, void *aux) { struct scsi_inquiry_data *inqbuf; struct scsi_attach_args *sa = aux; struct scsi_inquiry_data *inq = sa->sa_inqbuf; struct scsi_xfer *xs; struct safte_inq *si; int error, flags = 0, length; if (inq == NULL) return (0); /* match on dell enclosures */ if ((inq->device & SID_TYPE) == T_PROCESSOR && SID_ANSII_REV(inq) == SCSI_REV_SPC) return (2); if ((inq->device & SID_TYPE) != T_PROCESSOR || SID_ANSII_REV(inq) != SCSI_REV_2 || (inq->response_format & SID_ANSII) != 2) return (0); length = inq->additional_length + SAFTE_EXTRA_OFFSET; if (length < SAFTE_INQ_LEN) return (0); if (length > sizeof(*inqbuf)) length = sizeof(*inqbuf); inqbuf = dma_alloc(sizeof(*inqbuf), PR_NOWAIT | PR_ZERO); if (inqbuf == NULL) return (0); memset(inqbuf->extra, ' ', sizeof(inqbuf->extra)); if (cold) flags |= SCSI_AUTOCONF; xs = scsi_xs_get(sa->sa_sc_link, flags | SCSI_DATA_IN); if (xs == NULL) goto fail; xs->retries = 2; xs->timeout = 10000; scsi_init_inquiry(xs, 0, 0, inqbuf, length); error = scsi_xs_sync(xs); scsi_xs_put(xs); if (error) goto fail; si = (struct safte_inq *)&inqbuf->extra; if (memcmp(si->ident, SAFTE_IDENT, sizeof(si->ident)) == 0) { dma_free(inqbuf, sizeof(*inqbuf)); return (2); } fail: dma_free(inqbuf, sizeof(*inqbuf)); return (0); } void safte_attach(struct device *parent, struct device *self, void *aux) { struct safte_softc *sc = (struct safte_softc *)self; struct scsi_attach_args *sa = aux; int i = 0; sc->sc_link = sa->sa_sc_link; sa->sa_sc_link->device_softc = sc; rw_init(&sc->sc_lock, DEVNAME(sc)); printf("\n"); sc->sc_encbuf = NULL; sc->sc_nsensors = 0; #if NBIO > 0 sc->sc_nslots = 0; #endif if (safte_read_config(sc) != 0) { printf("%s: unable to read enclosure configuration\n", DEVNAME(sc)); return; } if (sc->sc_nsensors > 0) { sc->sc_sensortask = sensor_task_register(sc, safte_read_encstat, 10); if (sc->sc_sensortask == NULL) { printf("%s: unable to register update task\n", DEVNAME(sc)); free(sc->sc_sensors, M_DEVBUF, sc->sc_nsensors * sizeof(struct safte_sensor)); sc->sc_nsensors = sc->sc_ntemps = 0; } else { for (i = 0; i < sc->sc_nsensors; i++) sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i].se_sensor); sensordev_install(&sc->sc_sensordev); } } #if NBIO > 0 if (sc->sc_nslots > 0 && bio_register(self, safte_ioctl) != 0) { printf("%s: unable to register ioctl with bio\n", DEVNAME(sc)); sc->sc_nslots = 0; } else i++; #endif if (i) /* if we're doing something, then preinit encbuf and sensors */ safte_read_encstat(sc); else { dma_free(sc->sc_encbuf, sc->sc_encbuflen); sc->sc_encbuf = NULL; } } int safte_detach(struct device *self, int flags) { struct safte_softc *sc = (struct safte_softc *)self; int i; rw_enter_write(&sc->sc_lock); #if NBIO > 0 if (sc->sc_nslots > 0) bio_unregister(self); #endif if (sc->sc_nsensors > 0) { sensordev_deinstall(&sc->sc_sensordev); sensor_task_unregister(sc->sc_sensortask); for (i = 0; i < sc->sc_nsensors; i++) sensor_detach(&sc->sc_sensordev, &sc->sc_sensors[i].se_sensor); free(sc->sc_sensors, M_DEVBUF, sc->sc_nsensors * sizeof(struct safte_sensor)); } if (sc->sc_encbuf != NULL) dma_free(sc->sc_encbuf, sc->sc_encbuflen); rw_exit_write(&sc->sc_lock); return (0); } int safte_read_config(struct safte_softc *sc) { struct safte_config *config = NULL; struct safte_readbuf_cmd *cmd; struct safte_sensor *s; struct scsi_xfer *xs; int error = 0, flags = 0, i, j; config = dma_alloc(sizeof(*config), PR_NOWAIT); if (config == NULL) return (1); if (cold) flags |= SCSI_AUTOCONF; xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT); if (xs == NULL) { error = 1; goto done; } xs->cmdlen = sizeof(*cmd); xs->data = (void *)config; xs->datalen = sizeof(*config); xs->retries = 2; xs->timeout = 30000; cmd = (struct safte_readbuf_cmd *)xs->cmd; cmd->opcode = READ_BUFFER; cmd->flags |= SAFTE_RD_MODE; cmd->bufferid = SAFTE_RD_CONFIG; cmd->length = htobe16(sizeof(*config)); error = scsi_xs_sync(xs); scsi_xs_put(xs); if (error != 0) { error = 1; goto done; } DPRINTF(("%s: nfans: %d npwrsup: %d nslots: %d doorlock: %d ntemps: %d" " alarm: %d celsius: %d ntherm: %d\n", DEVNAME(sc), config->nfans, config->npwrsup, config->nslots, config->doorlock, config->ntemps, config->alarm, SAFTE_CFG_CELSIUS(config->therm), SAFTE_CFG_NTHERM(config->therm))); sc->sc_encbuflen = config->nfans * sizeof(u_int8_t) + /* fan status */ config->npwrsup * sizeof(u_int8_t) + /* power supply status */ config->nslots * sizeof(u_int8_t) + /* device scsi id (lun) */ sizeof(u_int8_t) + /* door lock status */ sizeof(u_int8_t) + /* speaker status */ config->ntemps * sizeof(u_int8_t) + /* temp sensors */ sizeof(u_int16_t); /* temp out of range sensors */ sc->sc_encbuf = dma_alloc(sc->sc_encbuflen, PR_NOWAIT); if (sc->sc_encbuf == NULL) { error = 1; goto done; } sc->sc_nsensors = config->nfans + config->npwrsup + config->ntemps + (config->doorlock ? 1 : 0) + (config->alarm ? 1 : 0); sc->sc_sensors = mallocarray(sc->sc_nsensors, sizeof(struct safte_sensor), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc->sc_sensors == NULL) { dma_free(sc->sc_encbuf, sc->sc_encbuflen); sc->sc_encbuf = NULL; sc->sc_nsensors = 0; error = 1; goto done; } strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), sizeof(sc->sc_sensordev.xname)); s = sc->sc_sensors; for (i = 0; i < config->nfans; i++) { s->se_type = SAFTE_T_FAN; s->se_field = (u_int8_t *)(sc->sc_encbuf + i); s->se_sensor.type = SENSOR_INDICATOR; snprintf(s->se_sensor.desc, sizeof(s->se_sensor.desc), "Fan%d", i); s++; } j = config->nfans; for (i = 0; i < config->npwrsup; i++) { s->se_type = SAFTE_T_PWRSUP; s->se_field = (u_int8_t *)(sc->sc_encbuf + j + i); s->se_sensor.type = SENSOR_INDICATOR; snprintf(s->se_sensor.desc, sizeof(s->se_sensor.desc), "PSU%d", i); s++; } j += config->npwrsup; #if NBIO > 0 sc->sc_nslots = config->nslots; sc->sc_slots = (u_int8_t *)(sc->sc_encbuf + j); #endif j += config->nslots; if (config->doorlock) { s->se_type = SAFTE_T_DOORLOCK; s->se_field = (u_int8_t *)(sc->sc_encbuf + j); s->se_sensor.type = SENSOR_INDICATOR; strlcpy(s->se_sensor.desc, "doorlock", sizeof(s->se_sensor.desc)); s++; } j++; if (config->alarm) { s->se_type = SAFTE_T_ALARM; s->se_field = (u_int8_t *)(sc->sc_encbuf + j); s->se_sensor.type = SENSOR_INDICATOR; strlcpy(s->se_sensor.desc, "alarm", sizeof(s->se_sensor.desc)); s++; } j++; /* * stash the temp info so we can get out of range status. limit the * number so the out of temp checks cant go into memory it doesnt own */ sc->sc_ntemps = (config->ntemps > 15) ? 15 : config->ntemps; sc->sc_temps = s; sc->sc_celsius = SAFTE_CFG_CELSIUS(config->therm); for (i = 0; i < config->ntemps; i++) { s->se_type = SAFTE_T_TEMP; s->se_field = (u_int8_t *)(sc->sc_encbuf + j + i); s->se_sensor.type = SENSOR_TEMP; s++; } j += config->ntemps; sc->sc_temperrs = (u_int8_t *)(sc->sc_encbuf + j); done: dma_free(config, sizeof(*config)); return (error); } void safte_read_encstat(void *arg) { struct safte_readbuf_cmd *cmd; struct safte_sensor *s; struct safte_softc *sc = (struct safte_softc *)arg; struct scsi_xfer *xs; int error, i, flags = 0; u_int16_t oot; rw_enter_write(&sc->sc_lock); if (cold) flags |= SCSI_AUTOCONF; xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT); if (xs == NULL) { rw_exit_write(&sc->sc_lock); return; } xs->cmdlen = sizeof(*cmd); xs->data = sc->sc_encbuf; xs->datalen = sc->sc_encbuflen; xs->retries = 2; xs->timeout = 30000; cmd = (struct safte_readbuf_cmd *)xs->cmd; cmd->opcode = READ_BUFFER; cmd->flags |= SAFTE_RD_MODE; cmd->bufferid = SAFTE_RD_ENCSTAT; cmd->length = htobe16(sc->sc_encbuflen); error = scsi_xs_sync(xs); scsi_xs_put(xs); if (error != 0) { rw_exit_write(&sc->sc_lock); return; } for (i = 0; i < sc->sc_nsensors; i++) { s = &sc->sc_sensors[i]; s->se_sensor.flags &= ~SENSOR_FUNKNOWN; DPRINTF(("%s: %d type: %d field: 0x%02x\n", DEVNAME(sc), i, s->se_type, *s->se_field)); switch (s->se_type) { case SAFTE_T_FAN: switch (*s->se_field) { case SAFTE_FAN_OP: s->se_sensor.value = 1; s->se_sensor.status = SENSOR_S_OK; break; case SAFTE_FAN_MF: s->se_sensor.value = 0; s->se_sensor.status = SENSOR_S_CRIT; break; case SAFTE_FAN_NOTINST: case SAFTE_FAN_UNKNOWN: default: s->se_sensor.value = 0; s->se_sensor.status = SENSOR_S_UNKNOWN; s->se_sensor.flags |= SENSOR_FUNKNOWN; break; } break; case SAFTE_T_PWRSUP: switch (*s->se_field) { case SAFTE_PWR_OP_ON: s->se_sensor.value = 1; s->se_sensor.status = SENSOR_S_OK; break; case SAFTE_PWR_OP_OFF: s->se_sensor.value = 0; s->se_sensor.status = SENSOR_S_OK; break; case SAFTE_PWR_MF_ON: s->se_sensor.value = 1; s->se_sensor.status = SENSOR_S_CRIT; break; case SAFTE_PWR_MF_OFF: s->se_sensor.value = 0; s->se_sensor.status = SENSOR_S_CRIT; break; case SAFTE_PWR_NOTINST: case SAFTE_PWR_PRESENT: case SAFTE_PWR_UNKNOWN: s->se_sensor.value = 0; s->se_sensor.status = SENSOR_S_UNKNOWN; s->se_sensor.flags |= SENSOR_FUNKNOWN; break; } break; case SAFTE_T_DOORLOCK: switch (*s->se_field) { case SAFTE_DOOR_LOCKED: s->se_sensor.value = 1; s->se_sensor.status = SENSOR_S_OK; break; case SAFTE_DOOR_UNLOCKED: s->se_sensor.value = 0; s->se_sensor.status = SENSOR_S_CRIT; break; case SAFTE_DOOR_UNKNOWN: s->se_sensor.value = 0; s->se_sensor.status = SENSOR_S_CRIT; s->se_sensor.flags |= SENSOR_FUNKNOWN; break; } break; case SAFTE_T_ALARM: switch (*s->se_field) { case SAFTE_SPKR_OFF: s->se_sensor.value = 0; s->se_sensor.status = SENSOR_S_OK; break; case SAFTE_SPKR_ON: s->se_sensor.value = 1; s->se_sensor.status = SENSOR_S_CRIT; break; } break; case SAFTE_T_TEMP: s->se_sensor.value = safte_temp2uK(*s->se_field, sc->sc_celsius); break; } } oot = _2btol(sc->sc_temperrs); for (i = 0; i < sc->sc_ntemps; i++) sc->sc_temps[i].se_sensor.status = (oot & (1 << i)) ? SENSOR_S_CRIT : SENSOR_S_OK; rw_exit_write(&sc->sc_lock); } #if NBIO > 0 int safte_ioctl(struct device *dev, u_long cmd, caddr_t addr) { struct safte_softc *sc = (struct safte_softc *)dev; int error = 0; switch (cmd) { case BIOCBLINK: error = safte_bio_blink(sc, (struct bioc_blink *)addr); break; default: error = EINVAL; break; } return (error); } int safte_bio_blink(struct safte_softc *sc, struct bioc_blink *blink) { struct safte_writebuf_cmd *cmd; struct safte_slotop *op; struct scsi_xfer *xs; int error, slot, flags = 0, wantblink; switch (blink->bb_status) { case BIOC_SBBLINK: wantblink = 1; break; case BIOC_SBUNBLINK: wantblink = 0; break; default: return (EINVAL); } rw_enter_read(&sc->sc_lock); for (slot = 0; slot < sc->sc_nslots; slot++) { if (sc->sc_slots[slot] == blink->bb_target) break; } rw_exit_read(&sc->sc_lock); if (slot >= sc->sc_nslots) return (ENODEV); op = dma_alloc(sizeof(*op), PR_WAITOK | PR_ZERO); op->opcode = SAFTE_WRITE_SLOTOP; op->slot = slot; op->flags |= wantblink ? SAFTE_SLOTOP_IDENTIFY : 0; if (cold) flags |= SCSI_AUTOCONF; xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_OUT | SCSI_SILENT); if (xs == NULL) { dma_free(op, sizeof(*op)); return (ENOMEM); } xs->cmdlen = sizeof(*cmd); xs->data = (void *)op; xs->datalen = sizeof(*op); xs->retries = 2; xs->timeout = 30000; cmd = (struct safte_writebuf_cmd *)xs->cmd; cmd->opcode = WRITE_BUFFER; cmd->flags |= SAFTE_WR_MODE; cmd->length = htobe16(sizeof(struct safte_slotop)); error = scsi_xs_sync(xs); scsi_xs_put(xs); if (error != 0) { error = EIO; } dma_free(op, sizeof(*op)); return (error); } #endif /* NBIO > 0 */ int64_t safte_temp2uK(u_int8_t measured, int celsius) { int64_t temp; temp = (int64_t)measured; temp += SAFTE_TEMP_OFFSET; temp *= 1000000; /* convert to micro (mu) degrees */ if (!celsius) temp = ((temp - 32000000) * 5) / 9; /* convert to Celsius */ temp += 273150000; /* convert to kelvin */ return (temp); }