diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2005-08-01 23:14:32 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2005-08-01 23:14:32 +0000 |
commit | 1fbfbe7a6ac1a86fa34ad2082efd5e4172bc1777 (patch) | |
tree | 7e8fc538f90f7812b6c5ec54a94f4d04a2fc08d7 /sys | |
parent | 104867d2bfc44ed6c194d7c59e40eddf83e5481f (diff) |
a new scsi enclosure services driver. this and safte replace the old ses
driver. rather than relying on its own set of tools, ses reports the
enclosure status via the hw.sensors sysctl tree. so far only temperature
is reported
ok marco@ deraadt@ krw@
Diffstat (limited to 'sys')
-rw-r--r-- | sys/scsi/files.scsi | 5 | ||||
-rw-r--r-- | sys/scsi/ses.c | 470 | ||||
-rw-r--r-- | sys/scsi/ses.h | 168 |
3 files changed, 642 insertions, 1 deletions
diff --git a/sys/scsi/files.scsi b/sys/scsi/files.scsi index 941da0be3ba..fa137e4685b 100644 --- a/sys/scsi/files.scsi +++ b/sys/scsi/files.scsi @@ -1,4 +1,4 @@ -# $OpenBSD: files.scsi,v 1.18 2005/07/31 06:22:56 dlg Exp $ +# $OpenBSD: files.scsi,v 1.19 2005/08/01 23:14:31 dlg Exp $ # $NetBSD: files.scsi,v 1.4 1996/05/16 04:01:08 mycroft Exp $ # # Config.new file and device description for machine-independent SCSI code. @@ -42,3 +42,6 @@ device safte: disk attach safte at scsibus file scsi/scsi_safte.c safte needs-flag +device ses: disk +attach ses at scsibus +file scsi/ses.c ses needs-flag diff --git a/sys/scsi/ses.c b/sys/scsi/ses.c new file mode 100644 index 00000000000..f1cb620ef7e --- /dev/null +++ b/sys/scsi/ses.c @@ -0,0 +1,470 @@ +/* $OpenBSD: ses.c,v 1.9 2005/08/01 23:14:31 dlg Exp $ */ + +/* + * Copyright (c) 2005 David Gwynne <dlg@openbsd.org> + * + * 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 <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/scsiio.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/queue.h> +#include <sys/sensors.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +#include <scsi/ses.h> + +#ifdef SES_DEBUG +#define DPRINTF(x...) do { if (sesdebug) printf(x); } while (0) +#define DPRINTFN(n, x...) do { if (sesdebug > (n)) printf(x); } while (0) +int sesdebug = 2; +#else +#define DPRINTF(x...) /* x */ +#define DPRINTFN(n,x...) /* n: x */ +#endif + +int ses_match(struct device *, void *, void *); +void ses_attach(struct device *, struct device *, void *); +int ses_detach(struct device *, int); + +struct ses_sensor { + struct sensor se_sensor; + u_int8_t se_type; + struct ses_status *se_stat; + + TAILQ_ENTRY(ses_sensor) se_entry; +}; + +struct ses_softc { + struct device sc_dev; + struct scsi_link *sc_link; + + enum { + SES_ST_NONE, + SES_ST_OK, + SES_ST_ERR + } sc_state; + + u_char *sc_buf; + ssize_t sc_buflen; + + TAILQ_HEAD(, ses_sensor) sc_sensors; + struct timeout sc_timeout; +}; + +struct cfattach ses_ca = { + sizeof(struct ses_softc), ses_match, ses_attach, ses_detach +}; + +struct cfdriver ses_cd = { + NULL, "ses", DV_DULL +}; + +#define DEVNAME(s) ((s)->sc_dev.dv_xname) + +#define SES_BUFLEN 2048 /* XXX is this enough? */ + +void ses_refresh(void *); + +int ses_read_config(struct ses_softc *); +int ses_read_status(struct ses_softc *, int refresh); +int ses_make_sensors(struct ses_softc *, struct ses_type_desc *, int); +int ses_refresh_sensors(struct ses_softc *); + +int64_t temp2uK(struct ses_status *); + +#ifdef SES_DEBUG +void ses_dump_enc_desc(struct ses_enc_desc *); +char *ses_dump_enc_string(u_char *, ssize_t); +#endif + +int +ses_match(struct device *parent, void *match, void *aux) +{ + struct scsibus_attach_args *sa = aux; + struct scsi_inquiry_data *inq = sa->sa_inqbuf; + + if (inq == NULL) + return (0); + + if ((inq->device & SID_TYPE) == T_ENCLOSURE && + (inq->version & SID_ANSII) >= SID_ANSII_SCSI2) + return (2); + + /* XXX apparently we can match on passthrough devs too? */ + + return (0); +} + +void +ses_attach(struct device *parent, struct device *self, void *aux) +{ + struct ses_softc *sc = (struct ses_softc *)self; + struct scsibus_attach_args *sa = aux; + + sc->sc_link = sa->sa_sc_link; + sc->sc_state = SES_ST_NONE; + + printf("\n"); + printf("%s: SCSI Enclosure Services\n", DEVNAME(sc)); + + if (ses_read_config(sc) != 0) { + printf("%s: unable to read enclosure configuration\n", + DEVNAME(sc)); + return; + } + + sc->sc_state = SES_ST_OK; + + timeout_set(&sc->sc_timeout, ses_refresh, sc); + timeout_add(&sc->sc_timeout, 10 * hz); +} + +int +ses_detach(struct device *self, int flags) +{ + struct ses_softc *sc = (struct ses_softc *)self; + struct ses_sensor *sensor; + + if (sc->sc_state != SES_ST_NONE) { + timeout_del(&sc->sc_timeout); + + /* + * We cant free the sensors once theyre in the systems sensor + * list, so just mark them as invalid. + */ + TAILQ_FOREACH(sensor, &sc->sc_sensors, se_entry) + sensor->se_sensor.flags |= SENSOR_FINVALID; + + free(sc->sc_buf, M_DEVBUF); + } + + return (0); +} + +void +ses_refresh(void *arg) +{ + struct ses_softc *sc = arg; + + if (ses_refresh_sensors(sc) != 0) { + if (sc->sc_state != SES_ST_ERR) + printf("%s: error reading enclosure status\n", + DEVNAME(sc)); + sc->sc_state = SES_ST_ERR; + } else { + if (sc->sc_state != SES_ST_OK) + printf("%s: reading enclosure status\n", DEVNAME(sc)); + sc->sc_state = SES_ST_OK; + } + + timeout_add(&sc->sc_timeout, 10 * hz); +} + +int +ses_read_config(struct ses_softc *sc) +{ + struct ses_scsi_diag cmd; + int flags; + + u_char *buf, *p; + + struct ses_config_hdr *cfg; + struct ses_enc_hdr *enc; +#ifdef SES_DEBUG + struct ses_enc_desc *desc; +#endif + struct ses_type_desc *tdh, *tdlist; + + int i, ntypes = 0, nelems = 0; + + buf = malloc(SES_BUFLEN, M_DEVBUF, M_NOWAIT); + if (buf == NULL) + return (1); + + memset(buf, 0, SES_BUFLEN); + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = RECEIVE_DIAGNOSTIC; + cmd.flags |= SES_DIAG_PCV; + cmd.pgcode = SES_PAGE_CONFIG; + cmd.length = htobe16(SES_BUFLEN); + flags = SCSI_DATA_IN; +#ifndef SCSIDEBUG + flags |= SCSI_SILENT; +#endif + + if (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), buf, SES_BUFLEN, 2, 3000, NULL, flags) != 0) { + free(buf, M_DEVBUF); + return (1); + } + + cfg = (struct ses_config_hdr *)buf; + if (cfg->pgcode != cmd.pgcode || betoh16(cfg->length) > SES_BUFLEN) { + free(buf, M_DEVBUF); + return (1); + } + + DPRINTF("%s: config: n_subenc: %d length: %d\n", DEVNAME(sc), + cfg->n_subenc, betoh16(cfg->length)); + + p = buf + SES_CFG_HDRLEN; + for (i = 0; i <= cfg->n_subenc; i++) { + enc = (struct ses_enc_hdr *)p; +#ifdef SES_DEBUG + DPRINTF("%s: enclosure %d enc_id: 0x%02x n_types: %d\n", + DEVNAME(sc), i, enc->enc_id, enc->n_types); + desc = (struct ses_enc_desc *)(p + SES_ENC_HDRLEN); + ses_dump_enc_desc(desc); +#endif /* SES_DEBUG */ + + ntypes += enc->n_types; + + p += SES_ENC_HDRLEN + enc->vendor_len; + } + + tdlist = (struct ses_type_desc *)p; /* stash this for later */ + + for (i = 0; i < ntypes; i++) { + tdh = (struct ses_type_desc *)p; + DPRINTF("%s: td %d subenc_id: %d type 0x%02x n_elem: %d\n", + DEVNAME(sc), i, tdh->subenc_id, tdh->type, tdh->n_elem); + + nelems += tdh->n_elem; + + p += SES_TYPE_DESCLEN; + } + +#ifdef SES_DEBUG + for (i = 0; i < ntypes; i++) { + DPRINTF("%s: td %d '%s'\n", DEVNAME(sc), i, + ses_dump_enc_string(p, tdlist[i].desc_len)); + + p += tdlist[i].desc_len; + } +#endif /* SES_DEBUG */ + + sc->sc_buflen = SES_STAT_LEN(ntypes, nelems); + sc->sc_buf = malloc(sc->sc_buflen, M_DEVBUF, M_NOWAIT); + if (sc->sc_buf == NULL) { + free(buf, M_DEVBUF); + return (1); + } + + /* get the status page and then use it to generate a list of sensors */ + if (ses_make_sensors(sc, tdlist, ntypes) != 0) { + free(buf, M_DEVBUF); + free(sc->sc_buf, M_DEVBUF); + return (1); + } + + free(buf, M_DEVBUF); + return (0); +} + +int +ses_read_status(struct ses_softc *sc, int refresh) +{ + struct ses_scsi_diag cmd; + int flags; + + memset(&cmd, 0, sizeof(cmd)); + cmd.opcode = RECEIVE_DIAGNOSTIC; + cmd.flags |= SES_DIAG_PCV; + cmd.pgcode = SES_PAGE_STATUS; + cmd.length = htobe16(sc->sc_buflen); + flags = SCSI_DATA_IN; +#ifndef SCSIDEBUG + flags |= SCSI_SILENT; +#endif + if (refresh) + flags |= SCSI_NOSLEEP; + + if (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), sc->sc_buf, sc->sc_buflen, 2, 3000, NULL, flags) != 0) + return (1); + + /* XXX should we check any values in the status header? */ + + return (0); +} + +int +ses_make_sensors(struct ses_softc *sc, struct ses_type_desc *types, int ntypes) +{ + struct ses_status *status; + struct ses_sensor *sensor; + enum sensor_type stype; + char *fmt; + int typecnt[SES_NUM_TYPES]; + int i, j; + + if (ses_read_status(sc, 0) != 0) + return (1); + + memset(typecnt, 0, sizeof(typecnt)); + TAILQ_INIT(&sc->sc_sensors); + + status = (struct ses_status *)(sc->sc_buf + SES_STAT_HDRLEN); + for (i = 0; i < ntypes; i++) { + /* ignore the overall status element for this type */ + DPRINTFN(1, "%s: %3d:- 0x%02x 0x%02x%02x%02x type: 0x%02x\n", + DEVNAME(sc), i, status->com, status->f1, status->f2, + status->f3, types[i].type); + + for (j = 0; j < types[i].n_elem; j++) { + /* move to the current status element */ + status++; + + DPRINTFN(1, "%s: %3d:%-3d 0x%02x 0x%02x%02x%02x\n", + DEVNAME(sc), i, j, status->com, status->f1, + status->f2, status->f3); + + if (SES_STAT_CODE(status->com) == SES_STAT_CODE_NOTINST) + continue; + + switch (types[i].type) { + case SES_T_TEMP: + stype = SENSOR_TEMP; + fmt = "temp%d"; + + break; + + default: + continue; + } + + sensor = malloc(sizeof(struct ses_sensor), M_DEVBUF, + M_NOWAIT); + if (sensor == NULL) + goto error; + + memset(sensor, 0, sizeof(struct ses_sensor)); + sensor->se_type = types[i].type; + sensor->se_stat = status; + sensor->se_sensor.type = stype; + strlcpy(sensor->se_sensor.device, DEVNAME(sc), + sizeof(sensor->se_sensor.device)); + snprintf(sensor->se_sensor.desc, + sizeof(sensor->se_sensor.desc), fmt, + typecnt[types[i].type]++); + + TAILQ_INSERT_TAIL(&sc->sc_sensors, sensor, se_entry); + } + + /* move to the overall status element of the next type */ + status++; + } + + TAILQ_FOREACH(sensor, &sc->sc_sensors, se_entry) + SENSOR_ADD(&sensor->se_sensor); + return (0); + +error: + while (!TAILQ_EMPTY(&sc->sc_sensors)) { + sensor = TAILQ_FIRST(&sc->sc_sensors); + TAILQ_REMOVE(&sc->sc_sensors, sensor, se_entry); + free(sensor, M_DEVBUF); + } + return (1); +} + +int +ses_refresh_sensors(struct ses_softc *sc) +{ + struct ses_sensor *sensor; + int ret = 0; + + if (ses_read_status(sc, 1) != 0) + return (1); + + TAILQ_FOREACH(sensor, &sc->sc_sensors, se_entry) { + DPRINTFN(10, "%s: %s 0x%02x 0x%02x%02x%02x\n", DEVNAME(sc), + sensor->se_sensor.desc, sensor->se_stat->com, + sensor->se_stat->f1, sensor->se_stat->f2, + sensor->se_stat->f3); + + switch (sensor->se_type) { + case SES_T_TEMP: + sensor->se_sensor.value = temp2uK(sensor->se_stat); + break; + + default: + ret = 1; + break; + } + } + + return (ret); +} + +int64_t +temp2uK(struct ses_status *status) +{ + int64_t temp; + + temp = (int64_t)SES_S_TEMP(status); + temp += SES_S_TEMP_OFFSET; + temp *= 1000000; /* convert to micro (mu) degrees */ + temp += 273150000; /* convert to kelvin */ + + return (temp); +} + +#ifdef SES_DEBUG +void +ses_dump_enc_desc(struct ses_enc_desc *desc) +{ + char str[32]; + +#if 0 + /* XXX not a string. wwn? */ + memset(str, 0, sizeof(str)); + memcpy(str, desc->logical_id, sizeof(desc->logical_id)); + DPRINTF("logical_id: %s", str); +#endif + + memset(str, 0, sizeof(str)); + memcpy(str, desc->vendor_id, sizeof(desc->vendor_id)); + DPRINTF(" vendor_id: %s", str); + + memset(str, 0, sizeof(str)); + memcpy(str, desc->prod_id, sizeof(desc->prod_id)); + DPRINTF(" prod_id: %s", str); + + memset(str, 0, sizeof(str)); + memcpy(str, desc->prod_rev, sizeof(desc->prod_rev)); + DPRINTF(" prod_rev: %s\n", str); +} + +char * +ses_dump_enc_string(u_char *buf, ssize_t len) +{ + static char str[256]; + + memset(str, 0, sizeof(str)); + if (len > 0) + memcpy(str, buf, len); + + return (str); +} +#endif /* SES_DEBUG */ diff --git a/sys/scsi/ses.h b/sys/scsi/ses.h new file mode 100644 index 00000000000..12e6e62ed38 --- /dev/null +++ b/sys/scsi/ses.h @@ -0,0 +1,168 @@ +/* $OpenBSD: ses.h,v 1.3 2005/08/01 23:14:31 dlg Exp $ */ +/* + * Copyright (c) 2005 Marco Peereboom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef _SCSI_SES_H_ +#define _SCSI_SES_H_ + +/* the scsi command */ +struct ses_scsi_diag { + u_int8_t opcode; /* RECEIVE_DIAGNOSTIC */ + u_int8_t flags; +#define SES_DIAG_PCV (1<<0) /* page code valid */ + u_int8_t pgcode; +#define SES_PAGE_CONFIG 0x01 /* Configuration */ +#define SES_PAGE_STATUS 0x02 /* Enclosure Status */ +#define SES_PAGE_EDESC 0x07 /* Element Descriptor */ + u_int16_t length; + u_int8_t control; +} __packed; + + +/* all the different sensor types */ +#define SES_T_UNSPEC 0x00 +#define SES_T_DEVICE 0x01 +#define SES_T_POWERSUPPLY 0x02 +#define SES_T_COOLING 0x03 +#define SES_T_TEMP 0x04 +#define SES_T_DOORLOCK 0x05 +#define SES_T_ALARM 0x06 +#define SES_T_ENC_SRV_CTRL 0x07 +#define SES_T_SCC_CTRL 0x08 +#define SES_T_NONVOL_CACHE 0x09 +#define SES_T_INV_OP_REASON 0x0a +#define SES_T_UPS 0x0b +#define SES_T_DISPLAY 0x0c +#define SES_T_KEYPAD 0x0d +#define SES_T_ENCLOSURE 0x0e +#define SES_T_SCSI_PORT_TRANS 0x0f +#define SES_T_LANGUAGE 0x10 +#define SES_T_COMM_PORT 0x11 +#define SES_T_VOLTAGE 0x12 +#define SES_T_CURRENT 0x13 +#define SES_T_SCSI_TARGET_PORT 0x14 +#define SES_T_SCSI_INIT_PORT 0x15 +#define SES_T_SIMP_SUBENC 0x16 +#define SES_T_ARRAY_DEVICE 0x17 + +#define SES_NUM_TYPES 256 + +/* diagnostic page header */ +struct ses_config_hdr { + u_int8_t pgcode; /* SES_PAGE_CONFIG */ + u_int8_t n_subenc; + u_int16_t length; + u_int32_t gencode; +} __packed; +#define SES_CFG_HDRLEN sizeof(struct ses_config_hdr) + +/* enclosure descriptor header */ +struct ses_enc_hdr { + u_int8_t enc_id; + u_int8_t subenc_id; + u_int8_t n_types; + u_int8_t vendor_len; +} __packed; +#define SES_ENC_HDRLEN sizeof(struct ses_enc_hdr) + +/* enclosure descriptor strings */ +struct ses_enc_desc { + u_int8_t logical_id[8]; /* this isnt a string */ + u_int8_t vendor_id[8]; + u_int8_t prod_id[16]; + u_int8_t prod_rev[4]; + u_int8_t vendor[0]; +} __packed; + +/* type descriptor header */ +struct ses_type_desc { + u_int8_t type; + u_int8_t n_elem; + u_int8_t subenc_id; + u_int8_t desc_len; +} __packed; +#define SES_TYPE_DESCLEN sizeof(struct ses_type_desc) + +/* status page header */ +struct ses_status_hdr { + u_int8_t pgcode; /* SES_PAGE_STATUS */ + u_int8_t flags; +#define SES_STAT_UNRECOV (1<<0) /* unrecoverable error */ +#define SES_STAT_CRIT (1<<1) /* critical error */ +#define SES_STAT_NONCRIT (1<<2) /* noncritical error */ +#define SES_STAT_INFO (1<<3) /* info available */ +#define SES_STAT_INVOP (1<<4) /* invalid operation */ + u_int16_t length; + u_int32_t gencode; +} __packed; +#define SES_STAT_HDRLEN sizeof(struct ses_status_hdr) + +struct ses_status { + u_int8_t com; +#define SES_STAT_CODE_MASK 0x0f +#define SES_STAT_CODE(x) ((x) & SES_STAT_CODE_MASK) +#define SES_STAT_CODE_UNSUP 0x00 /* unsupported */ +#define SES_STAT_CODE_OK 0x01 /* installed and ok */ +#define SES_STAT_CODE_CRIT 0x02 /* critical */ +#define SES_STAT_CODE_NONCRIT 0x03 /* warning */ +#define SES_STAT_CODE_UNREC 0x04 /* unrecoverable */ +#define SES_STAT_CODE_NOTINST 0x05 /* not installed */ +#define SES_STAT_CODE_UNKNOWN 0x06 /* unknown */ +#define SES_STAT_CODE_NOTAVAIL 0x07 /* not available */ +#define SES_STAT_SWAP (1<<4) /* element has been swapped */ +#define SES_STAT_DISABLED (1<<5) /* disabled */ +#define SES_STAT_PRDFAIL (1<<6) /* predicted failure */ + + u_int8_t f1; /* use of these flags depends on the SES_T */ + u_int8_t f2; + u_int8_t f3; +} __packed; +#define SES_STAT_ELEMLEN sizeof(struct ses_status) + +/* device */ +#define SES_S_DEV_ADDR(d) ((d)->f1) +#define SES_S_DEV_RDYTOINS(d) ((d)->f2 & (1<<3)) /* ready to insert */ +#define SES_S_DEV_DONOTREM(d) ((d)->f2 & (1<<6)) /* no not remove */ +/* XXX FINISH THIS */ + +/* temperature sensor */ +#define SES_S_TEMP_IDENT(d) ((d)->f1 & (1<<7)) /* identify */ +#define SES_S_TEMP(d) ((d)->f2) +#define SES_S_TEMP_OFFSET (-20) +#define SES_S_TEMP_UTWARN ((d)->f3 & (1<<0)) /* under temp warning */ +#define SES_S_TEMP_UTFAIL ((d)->f3 & (1<<1)) /* under temp failture */ +#define SES_S_TEMP_OTWARN ((d)->f3 & (1<<2)) /* over temp warning */ +#define SES_S_TEMP_OTFAIL ((d)->f3 & (1<<3)) /* over temp failure */ + +/* + * the length of the status page is the header and a status element for + * each type plus the number of elements for each type + */ +#define SES_STAT_LEN(t, e) \ + (SES_STAT_HDRLEN + SES_STAT_ELEMLEN * ((t)+(e))) + +#endif /* _SCSI_SES_H_ */ |