summaryrefslogtreecommitdiffstats
path: root/sys/cam
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2012-05-24 14:07:44 +0000
committermav <mav@FreeBSD.org>2012-05-24 14:07:44 +0000
commit96f3e42ce2c9de14b3e9a1de338ed639c1c8b596 (patch)
tree40272ce66d9fbbbec27ad859c9e9a595d6716bd3 /sys/cam
parent5b4a3e5f4a4045127c253ffac2a618946f83912b (diff)
downloadFreeBSD-src-96f3e42ce2c9de14b3e9a1de338ed639c1c8b596.zip
FreeBSD-src-96f3e42ce2c9de14b3e9a1de338ed639c1c8b596.tar.gz
MFprojects/zfsd:
Revamp the CAM enclosure services driver. This updated driver uses an in-kernel daemon to track state changes and publishes physical path location information\for disk elements into the CAM device database. Sponsored by: Spectra Logic Corporation Sponsored by: iXsystems, Inc. Submitted by: gibbs, will, mav
Diffstat (limited to 'sys/cam')
-rw-r--r--sys/cam/scsi/scsi_enc.c1011
-rw-r--r--sys/cam/scsi/scsi_enc.h219
-rw-r--r--sys/cam/scsi/scsi_enc_internal.h230
-rw-r--r--sys/cam/scsi/scsi_enc_safte.c1137
-rw-r--r--sys/cam/scsi/scsi_enc_ses.c2816
-rw-r--r--sys/cam/scsi/scsi_ses.c2533
-rw-r--r--sys/cam/scsi/scsi_ses.h2448
7 files changed, 7766 insertions, 2628 deletions
diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c
new file mode 100644
index 0000000..03e43bc
--- /dev/null
+++ b/sys/cam/scsi/scsi_enc.c
@@ -0,0 +1,1011 @@
+/*-
+ * Copyright (c) 2000 Matthew Jacob
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/sx.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <machine/stdarg.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_periph.h>
+#include <cam/cam_xpt_periph.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+#include <cam/scsi/scsi_enc.h>
+#include <cam/scsi/scsi_enc_internal.h>
+
+#include <opt_enc.h>
+
+MALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers");
+
+/* Enclosure type independent driver */
+
+#define SEN_ID "UNISYS SUN_SEN"
+#define SEN_ID_LEN 24
+
+static d_open_t enc_open;
+static d_close_t enc_close;
+static d_ioctl_t enc_ioctl;
+static periph_init_t enc_init;
+static periph_ctor_t enc_ctor;
+static periph_oninv_t enc_oninvalidate;
+static periph_dtor_t enc_dtor;
+static periph_start_t enc_start;
+
+static void enc_async(void *, uint32_t, struct cam_path *, void *);
+static enctyp enc_type(struct ccb_getdev *);
+
+SYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0,
+ "CAM Enclosure Services driver");
+
+static struct periph_driver encdriver = {
+ enc_init, "ses",
+ TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0
+};
+
+PERIPHDRIVER_DECLARE(enc, encdriver);
+
+static struct cdevsw enc_cdevsw = {
+ .d_version = D_VERSION,
+ .d_open = enc_open,
+ .d_close = enc_close,
+ .d_ioctl = enc_ioctl,
+ .d_name = "ses",
+ .d_flags = 0,
+};
+
+static void
+enc_init(void)
+{
+ cam_status status;
+
+ /*
+ * Install a global async callback. This callback will
+ * receive async callbacks like "new device found".
+ */
+ status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL);
+
+ if (status != CAM_REQ_CMP) {
+ printf("enc: Failed to attach master async callback "
+ "due to status 0x%x!\n", status);
+ }
+}
+
+static void
+enc_oninvalidate(struct cam_periph *periph)
+{
+ struct enc_softc *enc;
+
+ enc = periph->softc;
+
+ enc->enc_flags |= ENC_FLAG_INVALID;
+
+ /* If the sub-driver has an invalidate routine, call it */
+ if (enc->enc_vec.softc_invalidate != NULL)
+ enc->enc_vec.softc_invalidate(enc);
+
+ /*
+ * Unregister any async callbacks.
+ */
+ xpt_register_async(0, enc_async, periph, periph->path);
+
+ /*
+ * Shutdown our daemon.
+ */
+ enc->enc_flags |= ENC_FLAG_SHUTDOWN;
+ if (enc->enc_daemon != NULL) {
+ /* Signal and wait for the ses daemon to terminate. */
+ wakeup(enc->enc_daemon);
+ /*
+ * We're called with the SIM mutex held, but we're dropping
+ * the update mutex here on sleep. So we have to manually
+ * drop the SIM mutex.
+ */
+ cam_periph_sleep(enc->periph, enc->enc_daemon,
+ PUSER, "thtrm", 0);
+ }
+ callout_drain(&enc->status_updater);
+
+ xpt_print(periph->path, "lost device\n");
+}
+
+static void
+enc_dtor(struct cam_periph *periph)
+{
+ struct enc_softc *enc;
+
+ enc = periph->softc;
+
+ xpt_print(periph->path, "removing device entry\n");
+ cam_periph_unlock(periph);
+ destroy_dev(enc->enc_dev);
+ cam_periph_lock(periph);
+
+ /* If the sub-driver has a cleanup routine, call it */
+ if (enc->enc_vec.softc_cleanup != NULL)
+ enc->enc_vec.softc_cleanup(enc);
+
+ if (enc->enc_boot_hold_ch.ich_func != NULL) {
+ config_intrhook_disestablish(&enc->enc_boot_hold_ch);
+ enc->enc_boot_hold_ch.ich_func = NULL;
+ }
+
+ ENC_FREE(enc);
+}
+
+static void
+enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
+{
+ struct cam_periph *periph;
+
+ periph = (struct cam_periph *)callback_arg;
+
+ switch(code) {
+ case AC_FOUND_DEVICE:
+ {
+ struct ccb_getdev *cgd;
+ cam_status status;
+ path_id_t path_id;
+
+ cgd = (struct ccb_getdev *)arg;
+ if (arg == NULL) {
+ break;
+ }
+
+ if (enc_type(cgd) == ENC_NONE) {
+ /*
+ * Schedule announcement of the ENC bindings for
+ * this device if it is managed by a SEP.
+ */
+ path_id = xpt_path_path_id(path);
+ xpt_lock_buses();
+ TAILQ_FOREACH(periph, &encdriver.units, unit_links) {
+ struct enc_softc *softc;
+
+ softc = (struct enc_softc *)periph->softc;
+ if (xpt_path_path_id(periph->path) != path_id
+ || softc == NULL
+ || (softc->enc_flags & ENC_FLAG_INITIALIZED)
+ == 0
+ || softc->enc_vec.device_found == NULL)
+ continue;
+
+ softc->enc_vec.device_found(softc);
+ }
+ xpt_unlock_buses();
+ return;
+ }
+
+ status = cam_periph_alloc(enc_ctor, enc_oninvalidate,
+ enc_dtor, enc_start, "ses", CAM_PERIPH_BIO,
+ cgd->ccb_h.path, enc_async, AC_FOUND_DEVICE, cgd);
+
+ if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) {
+ printf("enc_async: Unable to probe new device due to "
+ "status 0x%x\n", status);
+ }
+ break;
+ }
+ default:
+ cam_periph_async(periph, code, path, arg);
+ break;
+ }
+}
+
+static int
+enc_open(struct cdev *dev, int flags, int fmt, struct thread *td)
+{
+ struct cam_periph *periph;
+ struct enc_softc *softc;
+ int error = 0;
+
+ periph = (struct cam_periph *)dev->si_drv1;
+ if (periph == NULL) {
+ return (ENXIO);
+ }
+
+ if (cam_periph_acquire(periph) != CAM_REQ_CMP)
+ return (ENXIO);
+
+ cam_periph_lock(periph);
+
+ softc = (struct enc_softc *)periph->softc;
+
+ if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
+ error = ENXIO;
+ goto out;
+ }
+ if (softc->enc_flags & ENC_FLAG_INVALID) {
+ error = ENXIO;
+ goto out;
+ }
+
+out:
+ cam_periph_unlock(periph);
+ if (error) {
+ cam_periph_release(periph);
+ }
+ return (error);
+}
+
+static int
+enc_close(struct cdev *dev, int flag, int fmt, struct thread *td)
+{
+ struct cam_periph *periph;
+ struct enc_softc *softc;
+
+ periph = (struct cam_periph *)dev->si_drv1;
+ if (periph == NULL)
+ return (ENXIO);
+
+ cam_periph_lock(periph);
+
+ softc = (struct enc_softc *)periph->softc;
+
+ cam_periph_unlock(periph);
+ cam_periph_release(periph);
+
+ return (0);
+}
+
+static void
+enc_start(struct cam_periph *p, union ccb *sccb)
+{
+ struct enc_softc *enc;
+
+ enc = p->softc;
+ ENC_DLOG(enc, "%s enter imm=%d prio=%d\n",
+ __func__, p->immediate_priority, p->pinfo.priority);
+ if (p->immediate_priority <= p->pinfo.priority) {
+ SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle);
+ p->immediate_priority = CAM_PRIORITY_NONE;
+ wakeup(&p->ccb_list);
+ } else
+ xpt_release_ccb(sccb);
+ ENC_DLOG(enc, "%s exit\n", __func__);
+}
+
+void
+enc_done(struct cam_periph *periph, union ccb *dccb)
+{
+ wakeup(&dccb->ccb_h.cbfcnp);
+}
+
+int
+enc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags)
+{
+ struct enc_softc *softc;
+ struct cam_periph *periph;
+
+ periph = xpt_path_periph(ccb->ccb_h.path);
+ softc = (struct enc_softc *)periph->softc;
+
+ return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb));
+}
+
+static int
+enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag,
+ struct thread *td)
+{
+ struct cam_periph *periph;
+ encioc_enc_status_t tmp;
+ encioc_string_t sstr;
+ encioc_elm_status_t elms;
+ encioc_elm_desc_t elmd;
+ encioc_elm_devnames_t elmdn;
+ encioc_element_t *uelm;
+ enc_softc_t *enc;
+ enc_cache_t *cache;
+ void *addr;
+ int error, i;
+
+
+ if (arg_addr)
+ addr = *((caddr_t *) arg_addr);
+ else
+ addr = NULL;
+
+ periph = (struct cam_periph *)dev->si_drv1;
+ if (periph == NULL)
+ return (ENXIO);
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n"));
+
+ cam_periph_lock(periph);
+ enc = (struct enc_softc *)periph->softc;
+ cache = &enc->enc_cache;
+
+ /*
+ * Now check to see whether we're initialized or not.
+ * This actually should never fail as we're not supposed
+ * to get past enc_open w/o successfully initializing
+ * things.
+ */
+ if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
+ cam_periph_unlock(periph);
+ return (ENXIO);
+ }
+ cam_periph_unlock(periph);
+
+ error = 0;
+
+ CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
+ ("trying to do ioctl %#lx\n", cmd));
+
+ /*
+ * If this command can change the device's state,
+ * we must have the device open for writing.
+ *
+ * For commands that get information about the
+ * device- we don't need to lock the peripheral
+ * if we aren't running a command. The periph
+ * also can't go away while a user process has
+ * it open.
+ */
+ switch (cmd) {
+ case ENCIOC_GETNELM:
+ case ENCIOC_GETELMMAP:
+ case ENCIOC_GETENCSTAT:
+ case ENCIOC_GETELMSTAT:
+ case ENCIOC_GETELMDESC:
+ case ENCIOC_GETELMDEVNAMES:
+ break;
+ default:
+ if ((flag & FWRITE) == 0) {
+ return (EBADF);
+ }
+ }
+
+ /*
+ * XXX The values read here are only valid for the current
+ * configuration generation. We need these ioctls
+ * to also pass in/out a generation number.
+ */
+ sx_slock(&enc->enc_cache_lock);
+ switch (cmd) {
+ case ENCIOC_GETNELM:
+ error = copyout(&cache->nelms, addr, sizeof (cache->nelms));
+ break;
+
+ case ENCIOC_GETELMMAP:
+ for (uelm = addr, i = 0; i != cache->nelms; i++) {
+ encioc_element_t kelm;
+ kelm.elm_idx = i;
+ kelm.elm_subenc_id = cache->elm_map[i].subenclosure;
+ kelm.elm_type = cache->elm_map[i].enctype;
+ error = copyout(&kelm, &uelm[i], sizeof(kelm));
+ if (error)
+ break;
+ }
+ break;
+
+ case ENCIOC_GETENCSTAT:
+ cam_periph_lock(periph);
+ error = enc->enc_vec.get_enc_status(enc, 1);
+ if (error) {
+ cam_periph_unlock(periph);
+ break;
+ }
+ tmp = cache->enc_status;
+ cam_periph_unlock(periph);
+ error = copyout(&tmp, addr, sizeof(tmp));
+ cache->enc_status = tmp;
+ break;
+
+ case ENCIOC_SETENCSTAT:
+ error = copyin(addr, &tmp, sizeof(tmp));
+ if (error)
+ break;
+ cam_periph_lock(periph);
+ error = enc->enc_vec.set_enc_status(enc, tmp, 1);
+ cam_periph_unlock(periph);
+ break;
+
+ case ENCIOC_GETSTRING:
+ case ENCIOC_SETSTRING:
+ if (enc->enc_vec.handle_string == NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = copyin(addr, &sstr, sizeof(sstr));
+ if (error)
+ break;
+ cam_periph_lock(periph);
+ error = enc->enc_vec.handle_string(enc, &sstr, cmd);
+ cam_periph_unlock(periph);
+ break;
+
+ case ENCIOC_GETELMSTAT:
+ error = copyin(addr, &elms, sizeof(elms));
+ if (error)
+ break;
+ if (elms.elm_idx >= cache->nelms) {
+ error = EINVAL;
+ break;
+ }
+ cam_periph_lock(periph);
+ error = enc->enc_vec.get_elm_status(enc, &elms, 1);
+ cam_periph_unlock(periph);
+ if (error)
+ break;
+ error = copyout(&elms, addr, sizeof(elms));
+ break;
+
+ case ENCIOC_GETELMDESC:
+ error = copyin(addr, &elmd, sizeof(elmd));
+ if (error)
+ break;
+ if (elmd.elm_idx >= cache->nelms) {
+ error = EINVAL;
+ break;
+ }
+ if (enc->enc_vec.get_elm_desc != NULL) {
+ error = enc->enc_vec.get_elm_desc(enc, &elmd);
+ if (error)
+ break;
+ } else
+ elmd.elm_desc_len = 0;
+ error = copyout(&elmd, addr, sizeof(elmd));
+ break;
+
+ case ENCIOC_GETELMDEVNAMES:
+ if (enc->enc_vec.get_elm_devnames == NULL) {
+ error = EINVAL;
+ break;
+ }
+ error = copyin(addr, &elmdn, sizeof(elmdn));
+ if (error)
+ break;
+ if (elmdn.elm_idx >= cache->nelms) {
+ error = EINVAL;
+ break;
+ }
+ cam_periph_lock(periph);
+ error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn);
+ cam_periph_unlock(periph);
+ if (error)
+ break;
+ error = copyout(&elmdn, addr, sizeof(elmdn));
+ break;
+
+ case ENCIOC_SETELMSTAT:
+ error = copyin(addr, &elms, sizeof(elms));
+ if (error)
+ break;
+
+ if (elms.elm_idx >= cache->nelms) {
+ error = EINVAL;
+ break;
+ }
+ cam_periph_lock(periph);
+ error = enc->enc_vec.set_elm_status(enc, &elms, 1);
+ cam_periph_unlock(periph);
+
+ break;
+
+ case ENCIOC_INIT:
+
+ cam_periph_lock(periph);
+ error = enc->enc_vec.init_enc(enc);
+ cam_periph_unlock(periph);
+ break;
+
+ default:
+ cam_periph_lock(periph);
+ error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error);
+ cam_periph_unlock(periph);
+ break;
+ }
+ sx_sunlock(&enc->enc_cache_lock);
+ return (error);
+}
+
+int
+enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp)
+{
+ int error, dlen, tdlen;
+ ccb_flags ddf;
+ union ccb *ccb;
+
+ CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE,
+ ("entering enc_runcmd\n"));
+ if (dptr) {
+ if ((dlen = *dlenp) < 0) {
+ dlen = -dlen;
+ ddf = CAM_DIR_OUT;
+ } else {
+ ddf = CAM_DIR_IN;
+ }
+ } else {
+ dlen = 0;
+ ddf = CAM_DIR_NONE;
+ }
+
+ if (cdbl > IOCDBLEN) {
+ cdbl = IOCDBLEN;
+ }
+
+ ccb = cam_periph_getccb(enc->periph, 1);
+ if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) {
+ tdlen = min(dlen, 1020);
+ tdlen = (tdlen + 3) & ~3;
+ cam_fill_ataio(&ccb->ataio, 0, enc_done, ddf, 0, dptr, tdlen,
+ 30 * 1000);
+ if (cdb[0] == RECEIVE_DIAGNOSTIC)
+ ata_28bit_cmd(&ccb->ataio,
+ ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4);
+ else if (cdb[0] == SEND_DIAGNOSTIC)
+ ata_28bit_cmd(&ccb->ataio,
+ ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
+ 0x82, tdlen / 4);
+ else if (cdb[0] == READ_BUFFER)
+ ata_28bit_cmd(&ccb->ataio,
+ ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4);
+ else
+ ata_28bit_cmd(&ccb->ataio,
+ ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
+ 0x80, tdlen / 4);
+ } else {
+ tdlen = dlen;
+ cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG,
+ dptr, dlen, sizeof (struct scsi_sense_data), cdbl,
+ 60 * 1000);
+ bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl);
+ }
+
+ error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL);
+ if (error) {
+ if (dptr) {
+ *dlenp = dlen;
+ }
+ } else {
+ if (dptr) {
+ if (ccb->ccb_h.func_code == XPT_ATA_IO)
+ *dlenp = ccb->ataio.resid;
+ else
+ *dlenp = ccb->csio.resid;
+ *dlenp += tdlen - dlen;
+ }
+ }
+ xpt_release_ccb(ccb);
+ CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
+ ("exiting enc_runcmd: *dlenp = %d\n", *dlenp));
+ return (error);
+}
+
+void
+enc_log(struct enc_softc *enc, const char *fmt, ...)
+{
+ va_list ap;
+
+ printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * The code after this point runs on many platforms,
+ * so forgive the slightly awkward and nonconforming
+ * appearance.
+ */
+
+/*
+ * Is this a device that supports enclosure services?
+ *
+ * It's a a pretty simple ruleset- if it is device type 0x0D (13), it's
+ * an ENC device. If it happens to be an old UNISYS SEN device, we can
+ * handle that too.
+ */
+
+#define SAFTE_START 44
+#define SAFTE_END 50
+#define SAFTE_LEN SAFTE_END-SAFTE_START
+
+static enctyp
+enc_type(struct ccb_getdev *cgd)
+{
+ int buflen;
+ unsigned char *iqd;
+
+ if (cgd->protocol == PROTO_SEMB) {
+ iqd = (unsigned char *)&cgd->ident_data;
+ if (STRNCMP(iqd + 43, "S-E-S", 5) == 0)
+ return (ENC_SEMB_SES);
+ else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0)
+ return (ENC_SEMB_SAFT);
+ return (ENC_NONE);
+
+ } else if (cgd->protocol != PROTO_SCSI)
+ return (ENC_NONE);
+
+ iqd = (unsigned char *)&cgd->inq_data;
+ buflen = min(sizeof(cgd->inq_data),
+ SID_ADDITIONAL_LENGTH(&cgd->inq_data));
+ if (buflen < 8+SEN_ID_LEN)
+ return (ENC_NONE);
+
+ if ((iqd[0] & 0x1f) == T_ENCLOSURE) {
+ if (STRNCMP(&iqd[8], SEN_ID, SEN_ID_LEN) == 0) {
+ return (ENC_SEN);
+ } else if ((iqd[2] & 0x7) > 2) {
+ return (ENC_SES);
+ } else {
+ return (ENC_SES_SCSI2);
+ }
+ return (ENC_NONE);
+ }
+
+#ifdef ENC_ENABLE_PASSTHROUGH
+ if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) {
+ /*
+ * PassThrough Device.
+ */
+ return (ENC_ENC_PASSTHROUGH);
+ }
+#endif
+
+ /*
+ * The comparison is short for a reason-
+ * some vendors were chopping it short.
+ */
+
+ if (buflen < SAFTE_END - 2) {
+ return (ENC_NONE);
+ }
+
+ if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) {
+ return (ENC_SAFT);
+ }
+ return (ENC_NONE);
+}
+
+/*================== Enclosure Monitoring/Processing Daemon ==================*/
+/**
+ * \brief Queue an update request for a given action, if needed.
+ *
+ * \param enc SES softc to queue the request for.
+ * \param action Action requested.
+ */
+void
+enc_update_request(enc_softc_t *enc, uint32_t action)
+{
+ if ((enc->pending_actions & (0x1 << action)) == 0) {
+ enc->pending_actions |= (0x1 << action);
+ ENC_DLOG(enc, "%s: queing requested action %d\n",
+ __func__, action);
+ if (enc->current_action == ENC_UPDATE_NONE)
+ wakeup(enc->enc_daemon);
+ } else {
+ ENC_DLOG(enc, "%s: ignoring requested action %d - "
+ "Already queued\n", __func__, action);
+ }
+}
+
+/**
+ * \brief Invoke the handler of the highest priority pending
+ * state in the SES state machine.
+ *
+ * \param enc The SES instance invoking the state machine.
+ */
+static void
+enc_fsm_step(enc_softc_t *enc)
+{
+ union ccb *ccb;
+ uint8_t *buf;
+ struct enc_fsm_state *cur_state;
+ int error;
+ uint32_t xfer_len;
+
+ ENC_DLOG(enc, "%s enter %p\n", __func__, enc);
+
+ enc->current_action = ffs(enc->pending_actions) - 1;
+ enc->pending_actions &= ~(0x1 << enc->current_action);
+
+ cur_state = &enc->enc_fsm_states[enc->current_action];
+
+ buf = NULL;
+ if (cur_state->buf_size != 0) {
+ cam_periph_unlock(enc->periph);
+ buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO);
+ cam_periph_lock(enc->periph);
+ }
+
+ error = 0;
+ ccb = NULL;
+ if (cur_state->fill != NULL) {
+ ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL);
+
+ error = cur_state->fill(enc, cur_state, ccb, buf);
+ if (error != 0)
+ goto done;
+
+ error = cam_periph_runccb(ccb, cur_state->error,
+ ENC_CFLAGS,
+ ENC_FLAGS|SF_QUIET_IR, NULL);
+ }
+
+ if (ccb != NULL) {
+ if (ccb->ccb_h.func_code == XPT_ATA_IO)
+ xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid;
+ else
+ xfer_len = ccb->csio.dxfer_len - ccb->csio.resid;
+ } else
+ xfer_len = 0;
+
+ cam_periph_unlock(enc->periph);
+ cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len);
+ cam_periph_lock(enc->periph);
+
+done:
+ ENC_DLOG(enc, "%s exit - result %d\n", __func__, error);
+ ENC_FREE_AND_NULL(buf);
+ if (ccb != NULL)
+ xpt_release_ccb(ccb);
+}
+
+/**
+ * \invariant Called with cam_periph mutex held.
+ */
+static void
+enc_status_updater(void *arg)
+{
+ enc_softc_t *enc;
+
+ enc = arg;
+ if (enc->enc_vec.poll_status != NULL)
+ enc->enc_vec.poll_status(enc);
+}
+
+static void
+enc_daemon(void *arg)
+{
+ enc_softc_t *enc;
+
+ enc = arg;
+
+ cam_periph_lock(enc->periph);
+ while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) {
+ if (enc->pending_actions == 0) {
+ struct intr_config_hook *hook;
+
+ /*
+ * Reset callout and msleep, or
+ * issue timed task completion
+ * status command.
+ */
+ enc->current_action = ENC_UPDATE_NONE;
+
+ /*
+ * We've been through our state machine at least
+ * once. Allow the transition to userland.
+ */
+ hook = &enc->enc_boot_hold_ch;
+ if (hook->ich_func != NULL) {
+ config_intrhook_disestablish(hook);
+ hook->ich_func = NULL;
+ }
+
+ callout_reset(&enc->status_updater, 60*hz,
+ enc_status_updater, enc);
+
+ cam_periph_sleep(enc->periph, enc->enc_daemon,
+ PUSER, "idle", 0);
+ } else {
+ enc_fsm_step(enc);
+ }
+ }
+ enc->enc_daemon = NULL;
+ cam_periph_unlock(enc->periph);
+ kproc_exit(0);
+}
+
+static int
+enc_kproc_init(enc_softc_t *enc)
+{
+ int result;
+
+ callout_init_mtx(&enc->status_updater, enc->periph->sim->mtx, 0);
+
+ result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0,
+ /*stackpgs*/0, "enc_daemon%d",
+ enc->periph->unit_number);
+ if (result == 0) {
+ /* Do an initial load of all page data. */
+ cam_periph_lock(enc->periph);
+ enc->enc_vec.poll_status(enc);
+ cam_periph_unlock(enc->periph);
+ }
+ return (result);
+}
+
+/**
+ * \brief Interrupt configuration hook callback associated with
+ * enc_boot_hold_ch.
+ *
+ * Since interrupts are always functional at the time of enclosure
+ * configuration, there is nothing to be done when the callback occurs.
+ * This hook is only registered to hold up boot processing while initial
+ * eclosure processing occurs.
+ *
+ * \param arg The enclosure softc, but currently unused in this callback.
+ */
+static void
+enc_nop_confighook_cb(void *arg __unused)
+{
+}
+
+static cam_status
+enc_ctor(struct cam_periph *periph, void *arg)
+{
+ cam_status status = CAM_REQ_CMP_ERR;
+ int err;
+ enc_softc_t *enc;
+ struct ccb_getdev *cgd;
+ char *tname;
+
+ cgd = (struct ccb_getdev *)arg;
+ if (periph == NULL) {
+ printf("enc_ctor: periph was NULL!!\n");
+ goto out;
+ }
+
+ if (cgd == NULL) {
+ printf("enc_ctor: no getdev CCB, can't register device\n");
+ goto out;
+ }
+
+ enc = ENC_MALLOCZ(sizeof(*enc));
+ if (enc == NULL) {
+ printf("enc_ctor: Unable to probe new device. "
+ "Unable to allocate enc\n");
+ goto out;
+ }
+ enc->periph = periph;
+ enc->current_action = ENC_UPDATE_INVALID;
+
+ enc->enc_type = enc_type(cgd);
+ sx_init(&enc->enc_cache_lock, "enccache");
+
+ switch (enc->enc_type) {
+ case ENC_SES:
+ case ENC_SES_SCSI2:
+ case ENC_SES_PASSTHROUGH:
+ case ENC_SEMB_SES:
+ err = ses_softc_init(enc);
+ break;
+ case ENC_SAFT:
+ case ENC_SEMB_SAFT:
+ err = safte_softc_init(enc);
+ break;
+ case ENC_SEN:
+ case ENC_NONE:
+ default:
+ ENC_FREE(enc);
+ return (CAM_REQ_CMP_ERR);
+ }
+
+ if (err) {
+ xpt_print(periph->path, "error %d initializing\n", err);
+ goto out;
+ }
+
+ /*
+ * Hold off userland until we have made at least one pass
+ * through our state machine so that physical path data is
+ * present.
+ */
+ if (enc->enc_vec.poll_status != NULL) {
+ enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb;
+ enc->enc_boot_hold_ch.ich_arg = enc;
+ config_intrhook_establish(&enc->enc_boot_hold_ch);
+ }
+
+ /*
+ * The softc field is set only once the enc is fully initialized
+ * so that we can rely on this field to detect partially
+ * initialized periph objects in the AC_FOUND_DEVICE handler.
+ */
+ periph->softc = enc;
+
+ cam_periph_unlock(periph);
+ if (enc->enc_vec.poll_status != NULL) {
+ err = enc_kproc_init(enc);
+ if (err) {
+ xpt_print(periph->path,
+ "error %d string enc_daemon\n", err);
+ goto out;
+ }
+ }
+ enc->enc_dev = make_dev(&enc_cdevsw, periph->unit_number,
+ UID_ROOT, GID_OPERATOR, 0600, "%s%d",
+ periph->periph_name, periph->unit_number);
+ cam_periph_lock(periph);
+ enc->enc_dev->si_drv1 = periph;
+
+ enc->enc_flags |= ENC_FLAG_INITIALIZED;
+
+ /*
+ * Add an async callback so that we get notified if this
+ * device goes away.
+ */
+ xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path);
+
+ switch (enc->enc_type) {
+ default:
+ case ENC_NONE:
+ tname = "No ENC device";
+ break;
+ case ENC_SES_SCSI2:
+ tname = "SCSI-2 ENC Device";
+ break;
+ case ENC_SES:
+ tname = "SCSI-3 ENC Device";
+ break;
+ case ENC_SES_PASSTHROUGH:
+ tname = "ENC Passthrough Device";
+ break;
+ case ENC_SEN:
+ tname = "UNISYS SEN Device (NOT HANDLED YET)";
+ break;
+ case ENC_SAFT:
+ tname = "SAF-TE Compliant Device";
+ break;
+ case ENC_SEMB_SES:
+ tname = "SEMB SES Device";
+ break;
+ case ENC_SEMB_SAFT:
+ tname = "SEMB SAF-TE Device";
+ break;
+ }
+ xpt_announce_periph(periph, tname);
+ status = CAM_REQ_CMP;
+
+out:
+ if (status != CAM_REQ_CMP)
+ enc_dtor(periph);
+ return (status);
+}
+
diff --git a/sys/cam/scsi/scsi_enc.h b/sys/cam/scsi/scsi_enc.h
new file mode 100644
index 0000000..a5345f2
--- /dev/null
+++ b/sys/cam/scsi/scsi_enc.h
@@ -0,0 +1,219 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2000 by Matthew Jacob
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * the GNU Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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_ENC_H_
+#define _SCSI_ENC_H_
+
+#include <cam/scsi/scsi_ses.h>
+
+#define ENCIOC ('s' - 040)
+#define ENCIOC_GETNELM _IO(ENCIOC, 1)
+#define ENCIOC_GETELMMAP _IO(ENCIOC, 2)
+#define ENCIOC_GETENCSTAT _IO(ENCIOC, 3)
+#define ENCIOC_SETENCSTAT _IO(ENCIOC, 4)
+#define ENCIOC_GETELMSTAT _IO(ENCIOC, 5)
+#define ENCIOC_SETELMSTAT _IO(ENCIOC, 6)
+#define ENCIOC_GETTEXT _IO(ENCIOC, 7)
+#define ENCIOC_INIT _IO(ENCIOC, 8)
+#define ENCIOC_GETELMDESC _IO(ENCIOC, 9)
+#define ENCIOC_GETELMDEVNAMES _IO(ENCIOC, 10)
+#define ENCIOC_GETSTRING _IO(ENCIOC, 11)
+#define ENCIOC_SETSTRING _IO(ENCIOC, 12)
+
+/*
+ * Platform Independent Definitions for enclosure devices.
+ */
+/*
+ * SCSI Based Environmental Services Application Defines
+ *
+ * Based almost entirely on SCSI-3 ENC Revision 8A specification,
+ * but slightly abstracted as the underlying device may in fact
+ * be a SAF-TE or vendor unique device.
+ */
+/*
+ * ENC Driver Operations:
+ * (The defines themselves are platform and access method specific)
+ *
+ * ENCIOC_GETNELM
+ * ENCIOC_GETELMMAP
+ * ENCIOC_GETENCSTAT
+ * ENCIOC_SETENCSTAT
+ * ENCIOC_GETELMSTAT
+ * ENCIOC_SETELMSTAT
+ * ENCIOC_INIT
+ *
+ *
+ * An application finds out how many elements an enclosure instance
+ * is managing by performing a ENCIOC_GETNELM operation. It then
+ * performs a ENCIOC_GETELMMAP to get the map that contains the
+ * elment identifiers for all elements (see encioc_element_t below).
+ * This information is static.
+ *
+ * The application may perform ENCIOC_GETELMSTAT operations to retrieve
+ * status on an element (see the enc_elm_status_t structure below),
+ * ENCIOC_SETELMSTAT operations to set status for an element.
+ *
+ * Similarly, overall enclosure status me be fetched or set via
+ * ENCIOC_GETENCSTAT or ENCIOC_SETENCSTAT operations (see encioc_enc_status_t
+ * below).
+ *
+ * Readers should note that there is nothing that requires either a set
+ * or a clear operation to actually latch and do anything in the target.
+ *
+ * A ENCIOC_INIT operation causes the enclosure to be initialized.
+ */
+
+/* Element Types */
+typedef enum {
+ ELMTYP_UNSPECIFIED = 0x00,
+ ELMTYP_DEVICE = 0x01,
+ ELMTYP_POWER = 0x02,
+ ELMTYP_FAN = 0x03,
+ ELMTYP_THERM = 0x04,
+ ELMTYP_DOORLOCK = 0x05,
+ ELMTYP_ALARM = 0x06,
+ ELMTYP_ESCC = 0x07, /* Enclosure SCC */
+ ELMTYP_SCC = 0x08, /* SCC */
+ ELMTYP_NVRAM = 0x09,
+ ELMTYP_INV_OP_REASON = 0x0a,
+ ELMTYP_UPS = 0x0b,
+ ELMTYP_DISPLAY = 0x0c,
+ ELMTYP_KEYPAD = 0x0d,
+ ELMTYP_ENCLOSURE = 0x0e,
+ ELMTYP_SCSIXVR = 0x0f,
+ ELMTYP_LANGUAGE = 0x10,
+ ELMTYP_COMPORT = 0x11,
+ ELMTYP_VOM = 0x12,
+ ELMTYP_AMMETER = 0x13,
+ ELMTYP_SCSI_TGT = 0x14,
+ ELMTYP_SCSI_INI = 0x15,
+ ELMTYP_SUBENC = 0x16,
+ ELMTYP_ARRAY_DEV = 0x17,
+ ELMTYP_SAS_EXP = 0x18, /* SAS expander */
+ ELMTYP_SAS_CONN = 0x19 /* SAS connector */
+} elm_type_t;
+
+typedef struct encioc_element {
+ /* Element Index */
+ unsigned int elm_idx;
+
+ /* ID of SubEnclosure containing Element*/
+ unsigned int elm_subenc_id;
+
+ /* Element Type */
+ elm_type_t elm_type;
+} encioc_element_t;
+
+/*
+ * Overall Enclosure Status
+ */
+typedef unsigned char encioc_enc_status_t;
+
+/*
+ * Element Status
+ */
+typedef struct encioc_elm_status {
+ unsigned int elm_idx;
+ unsigned char cstat[4];
+} encioc_elm_status_t;
+
+/*
+ * ENC String structure, for StringIn and StringOut commands; use this with
+ * the ENCIOC_GETSTRING and ENCIOC_SETSTRING ioctls.
+ */
+typedef struct encioc_string {
+ size_t bufsiz; /* IN/OUT: length of string provided/returned */
+#define ENC_STRING_MAX 0xffff
+ uint8_t *buf; /* IN/OUT: string */
+} encioc_string_t;
+
+/*============================================================================*/
+
+/*
+ * SES v2 r20 6.1.10 (pg 39) - Element Descriptor diagnostic page
+ * Tables 21, 22, and 23
+ */
+typedef struct encioc_elm_desc {
+ unsigned int elm_idx; /* IN: elment requested */
+ uint16_t elm_desc_len; /* IN: buffer size; OUT: bytes written */
+ char *elm_desc_str; /* IN/OUT: buffer for descriptor data */
+} encioc_elm_desc_t;
+
+/*
+ * ENCIOC_GETELMDEVNAMES:
+ * ioctl structure to get an element's device names, if available
+ */
+typedef struct encioc_elm_devnames {
+ unsigned int elm_idx; /* IN: element index */
+ size_t elm_names_size;/* IN: size of elm_devnames */
+ size_t elm_names_len; /* OUT: actual size returned */
+ /*
+ * IN/OUT: comma separated list of peripheral driver
+ * instances servicing this element.
+ */
+ char *elm_devnames;
+} encioc_elm_devnames_t;
+
+/* ioctl structure for requesting FC info for a port */
+typedef struct encioc_elm_fc_port {
+ unsigned int elm_idx;
+ unsigned int port_idx;
+ struct ses_elm_fc_port port_data;
+} encioc_elm_fc_port_t;
+
+/* ioctl structure for requesting SAS info for element phys */
+typedef struct encioc_elm_sas_device_phy {
+ unsigned int elm_idx;
+ unsigned int phy_idx;
+ struct ses_elm_sas_device_phy phy_data;
+} enioc_elm_sas_phy_t;
+
+/* ioctl structure for requesting SAS info for an expander phy */
+typedef struct encioc_elm_sas_expander_phy {
+ unsigned int elm_idx;
+ unsigned int phy_idx;
+ struct ses_elm_sas_expander_phy phy_data;
+} encioc_elm_sas_expander_phy_t;
+
+/* ioctl structure for requesting SAS info for a port phy */
+typedef struct encioc_elm_sas_port_phy {
+ unsigned int elm_idx;
+ unsigned int phy_idx;
+ struct ses_elm_sas_port_phy phy_data;
+} enioc_elm_sas_port_phy_t;
+
+/* ioctl structure for requesting additional status for an element */
+typedef struct encioc_addl_status {
+ unsigned int elm_idx;
+ union ses_elm_addlstatus_descr_hdr addl_hdr;
+ union ses_elm_addlstatus_proto_hdr proto_hdr;
+} enioc_addl_status_t;
+
+#endif /* _SCSI_ENC_H_ */
diff --git a/sys/cam/scsi/scsi_enc_internal.h b/sys/cam/scsi/scsi_enc_internal.h
new file mode 100644
index 0000000..865b8a1
--- /dev/null
+++ b/sys/cam/scsi/scsi_enc_internal.h
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 2000 Matthew Jacob
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * This file contains definitions only intended for use within
+ * sys/cam/scsi/scsi_enc*.c, and not in other kernel components.
+ */
+
+#ifndef __SCSI_ENC_INTERNAL_H__
+#define __SCSI_ENC_INTERNAL_H__
+
+typedef struct enc_element {
+ uint32_t
+ enctype : 8, /* enclosure type */
+ subenclosure : 8, /* subenclosure id */
+ svalid : 1, /* enclosure information valid */
+ overall_status_elem: 1,/*
+ * This object represents generic
+ * status about all objects of this
+ * type.
+ */
+ priv : 14; /* private data, per object */
+ uint8_t encstat[4]; /* state && stats */
+ uint8_t *physical_path; /* Device physical path data. */
+ u_int physical_path_len; /* Length of device path data. */
+ void *elm_private; /* per-type object data */
+} enc_element_t;
+
+typedef enum {
+ ENC_NONE,
+ ENC_SES_SCSI2,
+ ENC_SES,
+ ENC_SES_PASSTHROUGH,
+ ENC_SEN,
+ ENC_SAFT,
+ ENC_SEMB_SES,
+ ENC_SEMB_SAFT
+} enctyp;
+
+/* Platform Independent Driver Internal Definitions for enclosure devices. */
+typedef struct enc_softc enc_softc_t;
+
+struct enc_fsm_state;
+typedef int fsm_fill_handler_t(enc_softc_t *ssc,
+ struct enc_fsm_state *state,
+ union ccb *ccb,
+ uint8_t *buf);
+typedef int fsm_error_handler_t(union ccb *ccb, uint32_t cflags,
+ uint32_t sflags);
+typedef int fsm_done_handler_t(enc_softc_t *ssc,
+ struct enc_fsm_state *state, union ccb *ccb,
+ uint8_t **bufp, int error, int xfer_len);
+
+struct enc_fsm_state {
+ const char *name;
+ int page_code;
+ size_t buf_size;
+ uint32_t timeout;
+ fsm_fill_handler_t *fill;
+ fsm_done_handler_t *done;
+ fsm_error_handler_t *error;
+};
+
+typedef int (enc_softc_init_t)(enc_softc_t *);
+typedef void (enc_softc_invalidate_t)(enc_softc_t *);
+typedef void (enc_softc_cleanup_t)(enc_softc_t *);
+typedef int (enc_init_enc_t)(enc_softc_t *);
+typedef int (enc_get_enc_status_t)(enc_softc_t *, int);
+typedef int (enc_set_enc_status_t)(enc_softc_t *, encioc_enc_status_t, int);
+typedef int (enc_get_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int);
+typedef int (enc_set_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int);
+typedef int (enc_get_elm_desc_t)(enc_softc_t *, encioc_elm_desc_t *);
+typedef int (enc_get_elm_devnames_t)(enc_softc_t *, encioc_elm_devnames_t *);
+typedef int (enc_handle_string_t)(enc_softc_t *, encioc_string_t *, int);
+typedef void (enc_device_found_t)(enc_softc_t *);
+typedef void (enc_poll_status_t)(enc_softc_t *);
+
+struct enc_vec {
+ enc_softc_invalidate_t *softc_invalidate;
+ enc_softc_cleanup_t *softc_cleanup;
+ enc_init_enc_t *init_enc;
+ enc_get_enc_status_t *get_enc_status;
+ enc_set_enc_status_t *set_enc_status;
+ enc_get_elm_status_t *get_elm_status;
+ enc_set_elm_status_t *set_elm_status;
+ enc_get_elm_desc_t *get_elm_desc;
+ enc_get_elm_devnames_t *get_elm_devnames;
+ enc_handle_string_t *handle_string;
+ enc_device_found_t *device_found;
+ enc_poll_status_t *poll_status;
+};
+
+typedef struct enc_cache {
+ enc_element_t *elm_map; /* objects */
+ int nelms; /* number of objects */
+ encioc_enc_status_t enc_status; /* overall status */
+ void *private; /* per-type private data */
+} enc_cache_t;
+
+/* Enclosure instance toplevel structure */
+struct enc_softc {
+ enctyp enc_type; /* type of enclosure */
+ struct enc_vec enc_vec; /* vector to handlers */
+ void *enc_private; /* per-type private data */
+
+ /**
+ * "Published" configuration and state data available to
+ * external consumers.
+ */
+ enc_cache_t enc_cache;
+
+ /**
+ * Configuration and state data being actively updated
+ * by the enclosure daemon.
+ */
+ enc_cache_t enc_daemon_cache;
+
+ struct sx enc_cache_lock;
+ uint8_t enc_flags;
+#define ENC_FLAG_INVALID 0x01
+#define ENC_FLAG_INITIALIZED 0x02
+#define ENC_FLAG_SHUTDOWN 0x04
+ union ccb saved_ccb;
+ struct cdev *enc_dev;
+ struct cam_periph *periph;
+
+ /* Bitmap of pending operations. */
+ uint32_t pending_actions;
+
+ /* The action on which the state machine is currently working. */
+ uint32_t current_action;
+#define ENC_UPDATE_NONE 0x00
+#define ENC_UPDATE_INVALID 0xff
+
+ /* Callout for auto-updating enclosure status */
+ struct callout status_updater;
+
+ struct proc *enc_daemon;
+
+ struct enc_fsm_state *enc_fsm_states;
+
+ struct intr_config_hook enc_boot_hold_ch;
+};
+
+static inline enc_cache_t *
+enc_other_cache(enc_softc_t *enc, enc_cache_t *primary)
+{
+ return (primary == &enc->enc_cache
+ ? &enc->enc_daemon_cache : &enc->enc_cache);
+}
+
+/* SES Management mode page - SES2r20 Table 59 */
+struct ses_mgmt_mode_page {
+ struct scsi_mode_header_6 header;
+ struct scsi_mode_blk_desc blk_desc;
+ uint8_t byte0; /* ps : 1, spf : 1, page_code : 6 */
+#define SES_MGMT_MODE_PAGE_CODE 0x14
+ uint8_t length;
+#define SES_MGMT_MODE_PAGE_LEN 6
+ uint8_t reserved[3];
+ uint8_t byte5; /* reserved : 7, enbltc : 1 */
+#define SES_MGMT_TIMED_COMP_EN 0x1
+ uint8_t max_comp_time[2];
+};
+
+/* Enclosure core interface for sub-drivers */
+int enc_runcmd(struct enc_softc *, char *, int, char *, int *);
+void enc_log(struct enc_softc *, const char *, ...);
+void enc_done(struct cam_periph *, union ccb *);
+int enc_error(union ccb *, uint32_t, uint32_t);
+void enc_update_request(enc_softc_t *, uint32_t);
+
+/* SES Native interface */
+enc_softc_init_t ses_softc_init;
+
+/* SAF-TE interface */
+enc_softc_init_t safte_softc_init;
+
+/* Helper macros */
+MALLOC_DECLARE(M_SCSIENC);
+#define ENC_CFLAGS CAM_RETRY_SELTO
+#define ENC_FLAGS SF_NO_PRINT | SF_RETRY_UA
+#define STRNCMP strncmp
+#define PRINTF printf
+#define ENC_LOG enc_log
+#if defined(DEBUG) || defined(ENC_DEBUG)
+#define ENC_DLOG enc_log
+#else
+#define ENC_DLOG if (0) enc_log
+#endif
+#define ENC_VLOG if (bootverbose) enc_log
+#define ENC_MALLOC(amt) malloc(amt, M_SCSIENC, M_NOWAIT)
+#define ENC_MALLOCZ(amt) malloc(amt, M_SCSIENC, M_ZERO|M_NOWAIT)
+/* Cast away const avoiding GCC warnings. */
+#define ENC_FREE(ptr) free((void *)((uintptr_t)ptr), M_SCSIENC)
+#define ENC_FREE_AND_NULL(ptr) do { \
+ if (ptr != NULL) { \
+ ENC_FREE(ptr); \
+ ptr = NULL; \
+ } \
+} while(0)
+#define MEMZERO bzero
+#define MEMCPY(dest, src, amt) bcopy(src, dest, amt)
+
+#endif /* __SCSI_ENC_INTERNAL_H__ */
diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c
new file mode 100644
index 0000000..f661e51
--- /dev/null
+++ b/sys/cam/scsi/scsi_enc_safte.c
@@ -0,0 +1,1137 @@
+/*-
+ * Copyright (c) 2000 Matthew Jacob
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/sx.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_periph.h>
+
+#include <cam/scsi/scsi_enc.h>
+#include <cam/scsi/scsi_enc_internal.h>
+#include <cam/scsi/scsi_message.h>
+
+#include <opt_enc.h>
+
+/*
+ * SAF-TE Type Device Emulation
+ */
+
+static int safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag);
+
+#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 */
+#define SAFTE_RD_RDGFLG 0x05 /* read global flags */
+
+/*
+ * 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 SCSZ 0x8000
+
+typedef enum {
+ SAFTE_UPDATE_NONE,
+ SAFTE_UPDATE_READCONFIG,
+ SAFTE_UPDATE_READGFLAGS,
+ SAFTE_UPDATE_READENCSTATUS,
+ SAFTE_UPDATE_READSLOTSTATUS,
+ SAFTE_PROCESS_CONTROL_REQS,
+ SAFTE_NUM_UPDATE_STATES
+} safte_update_action;
+
+static fsm_fill_handler_t safte_fill_read_buf_io;
+static fsm_fill_handler_t safte_fill_control_request;
+static fsm_done_handler_t safte_process_config;
+static fsm_done_handler_t safte_process_gflags;
+static fsm_done_handler_t safte_process_status;
+static fsm_done_handler_t safte_process_slotstatus;
+static fsm_done_handler_t safte_process_control_request;
+
+static struct enc_fsm_state enc_fsm_states[SAFTE_NUM_UPDATE_STATES] =
+{
+ { "SAFTE_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL },
+ {
+ "SAFTE_UPDATE_READCONFIG",
+ SAFTE_RD_RDCFG,
+ SAFT_SCRATCH,
+ 60 * 1000,
+ safte_fill_read_buf_io,
+ safte_process_config,
+ enc_error
+ },
+ {
+ "SAFTE_UPDATE_READGFLAGS",
+ SAFTE_RD_RDGFLG,
+ 16,
+ 60 * 1000,
+ safte_fill_read_buf_io,
+ safte_process_gflags,
+ enc_error
+ },
+ {
+ "SAFTE_UPDATE_READENCSTATUS",
+ SAFTE_RD_RDESTS,
+ SCSZ,
+ 60 * 1000,
+ safte_fill_read_buf_io,
+ safte_process_status,
+ enc_error
+ },
+ {
+ "SAFTE_UPDATE_READSLOTSTATUS",
+ SAFTE_RD_RDDSTS,
+ SCSZ,
+ 60 * 1000,
+ safte_fill_read_buf_io,
+ safte_process_slotstatus,
+ enc_error
+ },
+ {
+ "SAFTE_PROCESS_CONTROL_REQS",
+ 0,
+ SCSZ,
+ 60 * 1000,
+ safte_fill_control_request,
+ safte_process_control_request,
+ enc_error
+ }
+};
+
+typedef struct safte_control_request {
+ int elm_idx;
+ uint8_t elm_stat[4];
+ int result;
+ TAILQ_ENTRY(safte_control_request) links;
+} safte_control_request_t;
+TAILQ_HEAD(safte_control_reqlist, safte_control_request);
+typedef struct safte_control_reqlist safte_control_reqlist_t;
+enum {
+ SES_SETSTATUS_ENC_IDX = -1
+};
+
+static void
+safte_terminate_control_requests(safte_control_reqlist_t *reqlist, int result)
+{
+ safte_control_request_t *req;
+
+ while ((req = TAILQ_FIRST(reqlist)) != NULL) {
+ TAILQ_REMOVE(reqlist, req, links);
+ req->result = result;
+ wakeup(req);
+ }
+}
+
+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 Ntstats; /* Number of Thermostats */
+ /*
+ * 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
+
+ encioc_enc_status_t adm_status;
+ encioc_enc_status_t enc_status;
+ encioc_enc_status_t slot_status;
+
+ safte_control_reqlist_t requests;
+ safte_control_request_t *current_request;
+ int current_request_stage;
+ int current_request_stages;
+};
+
+#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) \
+ if ((r) >= (x)) { \
+ ENC_LOG(enc, safte_2little, x, __LINE__);\
+ return (EIO); \
+ }
+
+int emulate_array_devices = 1;
+SYSCTL_DECL(_kern_cam_enc);
+SYSCTL_INT(_kern_cam_enc, OID_AUTO, emulate_array_devices, CTLFLAG_RW,
+ &emulate_array_devices, 0, "Emulate Array Devices for SAF-TE");
+TUNABLE_INT("kern.cam.enc.emulate_array_devices", &emulate_array_devices);
+
+static int
+safte_fill_read_buf_io(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t *buf)
+{
+
+ if (state->page_code != SAFTE_RD_RDCFG &&
+ enc->enc_cache.nelms == 0) {
+ enc_update_request(enc, SAFTE_UPDATE_READCONFIG);
+ return (-1);
+ }
+
+ if (enc->enc_type == ENC_SEMB_SAFT) {
+ semb_read_buffer(&ccb->ataio, /*retries*/5,
+ enc_done, MSG_SIMPLE_Q_TAG,
+ state->page_code, buf, state->buf_size,
+ state->timeout);
+ } else {
+ scsi_read_buffer(&ccb->csio, /*retries*/5,
+ enc_done, MSG_SIMPLE_Q_TAG, 1,
+ state->page_code, 0, buf, state->buf_size,
+ SSD_FULL_SIZE, state->timeout);
+ }
+ return (0);
+}
+
+static int
+safte_process_config(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ struct scfg *cfg;
+ uint8_t *buf = *bufp;
+ int i, r;
+
+ cfg = enc->enc_private;
+ if (cfg == NULL)
+ return (ENXIO);
+ if (error != 0)
+ return (error);
+ if (xfer_len < 6) {
+ ENC_LOG(enc, "too little data (%d) for configuration\n",
+ xfer_len);
+ return (EIO);
+ }
+ cfg->Nfans = buf[0];
+ cfg->Npwr = buf[1];
+ cfg->Nslots = buf[2];
+ cfg->DoorLock = buf[3];
+ cfg->Ntherm = buf[4];
+ cfg->Nspkrs = buf[5];
+ if (xfer_len >= 7)
+ cfg->Ntstats = buf[6] & 0x0f;
+ else
+ cfg->Ntstats = 0;
+ ENC_VLOG(enc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d "
+ "Ntstats %d\n",
+ cfg->Nfans, cfg->Npwr, cfg->Nslots, cfg->DoorLock, cfg->Ntherm,
+ cfg->Nspkrs, cfg->Ntstats);
+
+ enc->enc_cache.nelms = cfg->Nfans + cfg->Npwr + cfg->Nslots +
+ cfg->DoorLock + cfg->Ntherm + cfg->Nspkrs + cfg->Ntstats + 1;
+ ENC_FREE_AND_NULL(enc->enc_cache.elm_map);
+ enc->enc_cache.elm_map =
+ ENC_MALLOCZ(enc->enc_cache.nelms * sizeof(enc_element_t));
+ if (enc->enc_cache.elm_map == NULL) {
+ enc->enc_cache.nelms = 0;
+ return (ENOMEM);
+ }
+
+ r = 0;
+ /*
+ * Note that this is all arranged for the convenience
+ * in later fetches of status.
+ */
+ for (i = 0; i < cfg->Nfans; i++)
+ enc->enc_cache.elm_map[r++].enctype = ELMTYP_FAN;
+ cfg->pwroff = (uint8_t) r;
+ for (i = 0; i < cfg->Npwr; i++)
+ enc->enc_cache.elm_map[r++].enctype = ELMTYP_POWER;
+ for (i = 0; i < cfg->DoorLock; i++)
+ enc->enc_cache.elm_map[r++].enctype = ELMTYP_DOORLOCK;
+ if (cfg->Nspkrs > 0)
+ enc->enc_cache.elm_map[r++].enctype = ELMTYP_ALARM;
+ for (i = 0; i < cfg->Ntherm; i++)
+ enc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM;
+ for (i = 0; i <= cfg->Ntstats; i++)
+ enc->enc_cache.elm_map[r++].enctype = ELMTYP_THERM;
+ cfg->slotoff = (uint8_t) r;
+ for (i = 0; i < cfg->Nslots; i++)
+ enc->enc_cache.elm_map[r++].enctype =
+ emulate_array_devices ? ELMTYP_ARRAY_DEV :
+ ELMTYP_DEVICE;
+
+ enc_update_request(enc, SAFTE_UPDATE_READGFLAGS);
+ enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS);
+ enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS);
+
+ return (0);
+}
+
+static int
+safte_process_gflags(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ struct scfg *cfg;
+ uint8_t *buf = *bufp;
+
+ cfg = enc->enc_private;
+ if (cfg == NULL)
+ return (ENXIO);
+ if (error != 0)
+ return (error);
+ SAFT_BAIL(3, xfer_len);
+ cfg->flag1 = buf[1];
+ cfg->flag2 = buf[2];
+
+ cfg->adm_status = 0;
+ if (cfg->flag1 & SAFT_FLG1_GLOBFAIL)
+ cfg->adm_status |= SES_ENCSTAT_CRITICAL;
+ else if (cfg->flag1 & SAFT_FLG1_GLOBWARN)
+ cfg->adm_status |= SES_ENCSTAT_NONCRITICAL;
+
+ return (0);
+}
+
+static int
+safte_process_status(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ struct scfg *cfg;
+ uint8_t *buf = *bufp;
+ int oid, r, i, nitems;
+ uint16_t tempflags;
+ enc_cache_t *cache = &enc->enc_cache;
+
+ cfg = enc->enc_private;
+ if (cfg == NULL)
+ return (ENXIO);
+ if (error != 0)
+ return (error);
+
+ oid = r = 0;
+ cfg->enc_status = 0;
+
+ for (nitems = i = 0; i < cfg->Nfans; i++) {
+ SAFT_BAIL(r, xfer_len);
+ /*
+ * 0 = Fan Operational
+ * 1 = Fan is malfunctioning
+ * 2 = Fan is not present
+ * 0x80 = Unknown or Not Reportable Status
+ */
+ cache->elm_map[oid].encstat[1] = 0; /* resvd */
+ cache->elm_map[oid].encstat[2] = 0; /* resvd */
+ if (cfg->flag1 & SAFT_FLG1_ENCFANFAIL)
+ cache->elm_map[oid].encstat[3] |= 0x40;
+ else
+ cache->elm_map[oid].encstat[3] &= ~0x40;
+ switch ((int)buf[r]) {
+ case 0:
+ nitems++;
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK;
+ if ((cache->elm_map[oid].encstat[3] & 0x37) == 0)
+ cache->elm_map[oid].encstat[3] |= 0x27;
+ break;
+
+ case 1:
+ cache->elm_map[oid].encstat[0] =
+ SES_OBJSTAT_CRIT;
+ /*
+ * FAIL and FAN STOPPED synthesized
+ */
+ cache->elm_map[oid].encstat[3] |= 0x10;
+ cache->elm_map[oid].encstat[3] &= ~0x07;
+ /*
+ * Enclosure marked with CRITICAL error
+ * if only one fan or no thermometers,
+ * else the NONCRITICAL error is set.
+ */
+ if (cfg->Nfans == 1 || (cfg->Ntherm + cfg->Ntstats) == 0)
+ cfg->enc_status |= SES_ENCSTAT_CRITICAL;
+ else
+ cfg->enc_status |= SES_ENCSTAT_NONCRITICAL;
+ break;
+ case 2:
+ cache->elm_map[oid].encstat[0] =
+ SES_OBJSTAT_NOTINSTALLED;
+ cache->elm_map[oid].encstat[3] |= 0x10;
+ cache->elm_map[oid].encstat[3] &= ~0x07;
+ /*
+ * Enclosure marked with CRITICAL error
+ * if only one fan or no thermometers,
+ * else the NONCRITICAL error is set.
+ */
+ if (cfg->Nfans == 1)
+ cfg->enc_status |= SES_ENCSTAT_CRITICAL;
+ else
+ cfg->enc_status |= SES_ENCSTAT_NONCRITICAL;
+ break;
+ case 0x80:
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN;
+ cache->elm_map[oid].encstat[3] = 0;
+ cfg->enc_status |= SES_ENCSTAT_INFO;
+ break;
+ default:
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED;
+ ENC_LOG(enc, "Unknown fan%d status 0x%x\n", i,
+ buf[r] & 0xff);
+ break;
+ }
+ cache->elm_map[oid++].svalid = 1;
+ r++;
+ }
+
+ /*
+ * No matter how you cut it, no cooling elements when there
+ * should be some there is critical.
+ */
+ if (cfg->Nfans && nitems == 0)
+ cfg->enc_status |= SES_ENCSTAT_CRITICAL;
+
+ for (i = 0; i < cfg->Npwr; i++) {
+ SAFT_BAIL(r, xfer_len);
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN;
+ cache->elm_map[oid].encstat[1] = 0; /* resvd */
+ cache->elm_map[oid].encstat[2] = 0; /* resvd */
+ cache->elm_map[oid].encstat[3] = 0x20; /* requested on */
+ switch (buf[r]) {
+ case 0x00: /* pws operational and on */
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK;
+ break;
+ case 0x01: /* pws operational and off */
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK;
+ cache->elm_map[oid].encstat[3] = 0x10;
+ cfg->enc_status |= SES_ENCSTAT_INFO;
+ break;
+ case 0x10: /* pws is malfunctioning and commanded on */
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT;
+ cache->elm_map[oid].encstat[3] = 0x61;
+ cfg->enc_status |= SES_ENCSTAT_NONCRITICAL;
+ break;
+
+ case 0x11: /* pws is malfunctioning and commanded off */
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT;
+ cache->elm_map[oid].encstat[3] = 0x51;
+ cfg->enc_status |= SES_ENCSTAT_NONCRITICAL;
+ break;
+ case 0x20: /* pws is not present */
+ cache->elm_map[oid].encstat[0] =
+ SES_OBJSTAT_NOTINSTALLED;
+ cache->elm_map[oid].encstat[3] = 0;
+ cfg->enc_status |= 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 */
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN;
+ cache->elm_map[oid].encstat[3] = 0;
+ cfg->enc_status |= SES_ENCSTAT_INFO;
+ break;
+ default:
+ ENC_LOG(enc, "unknown power supply %d status (0x%x)\n",
+ i, buf[r] & 0xff);
+ break;
+ }
+ enc->enc_cache.elm_map[oid++].svalid = 1;
+ r++;
+ }
+
+ /*
+ * Copy Slot SCSI IDs
+ */
+ for (i = 0; i < cfg->Nslots; i++) {
+ SAFT_BAIL(r, xfer_len);
+ if (cache->elm_map[cfg->slotoff + i].enctype == ELMTYP_DEVICE)
+ cache->elm_map[cfg->slotoff + i].encstat[1] = buf[r];
+ r++;
+ }
+
+ /*
+ * We always have doorlock status, no matter what,
+ * but we only save the status if we have one.
+ */
+ SAFT_BAIL(r, xfer_len);
+ if (cfg->DoorLock) {
+ /*
+ * 0 = Door Locked
+ * 1 = Door Unlocked, or no Lock Installed
+ * 0x80 = Unknown or Not Reportable Status
+ */
+ cache->elm_map[oid].encstat[1] = 0;
+ cache->elm_map[oid].encstat[2] = 0;
+ switch (buf[r]) {
+ case 0:
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK;
+ cache->elm_map[oid].encstat[3] = 0;
+ break;
+ case 1:
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK;
+ cache->elm_map[oid].encstat[3] = 1;
+ break;
+ case 0x80:
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_UNKNOWN;
+ cache->elm_map[oid].encstat[3] = 0;
+ cfg->enc_status |= SES_ENCSTAT_INFO;
+ break;
+ default:
+ cache->elm_map[oid].encstat[0] =
+ SES_OBJSTAT_UNSUPPORTED;
+ ENC_LOG(enc, "unknown lock status 0x%x\n",
+ buf[r] & 0xff);
+ break;
+ }
+ cache->elm_map[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, xfer_len);
+ if (cfg->Nspkrs) {
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK;
+ cache->elm_map[oid].encstat[1] = 0;
+ cache->elm_map[oid].encstat[2] = 0;
+ if (buf[r] == 0) {
+ cache->elm_map[oid].encstat[0] |= SESCTL_DISABLE;
+ cache->elm_map[oid].encstat[3] |= 0x40;
+ }
+ cache->elm_map[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 + cfg->Ntherm, xfer_len);
+ tempflags = buf[r + cfg->Ntherm];
+ SAFT_BAIL(r + cfg->Ntherm + 1, xfer_len);
+ tempflags |= (tempflags << 8) | buf[r + cfg->Ntherm + 1];
+
+ for (i = 0; i < cfg->Ntherm; i++) {
+ SAFT_BAIL(r, xfer_len);
+ /*
+ * 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.
+ */
+
+ /*
+ * Actually.... All of the units that people out in the world
+ * seem to have do not come even close to setting a value that
+ * complies with this spec.
+ *
+ * The closest explanation I could find was in an
+ * LSI-Logic manual, which seemed to indicate that
+ * this value would be set by whatever the I2C code
+ * would interpolate from the output of an LM75
+ * temperature sensor.
+ *
+ * This means that it is impossible to use the actual
+ * numeric value to predict anything. But we don't want
+ * to lose the value. So, we'll propagate the *uncorrected*
+ * value and set SES_OBJSTAT_NOTAVAIL. We'll depend on the
+ * temperature flags for warnings.
+ */
+ if (tempflags & (1 << i)) {
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT;
+ cfg->enc_status |= SES_ENCSTAT_CRITICAL;
+ } else
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK;
+ cache->elm_map[oid].encstat[1] = 0;
+ cache->elm_map[oid].encstat[2] = buf[r];
+ cache->elm_map[oid].encstat[3] = 0;
+ cache->elm_map[oid++].svalid = 1;
+ r++;
+ }
+
+ for (i = 0; i <= cfg->Ntstats; i++) {
+ cache->elm_map[oid].encstat[1] = 0;
+ if (tempflags & (1 <<
+ ((i == cfg->Ntstats) ? 15 : (cfg->Ntherm + i)))) {
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT;
+ cache->elm_map[4].encstat[2] = 0xff;
+ /*
+ * Set 'over temperature' failure.
+ */
+ cache->elm_map[oid].encstat[3] = 8;
+ cfg->enc_status |= 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.
+ */
+ if ((cfg->Ntherm + cfg->Ntstats) == 0)
+ cache->elm_map[oid].encstat[0] =
+ SES_OBJSTAT_NOTAVAIL;
+ else
+ cache->elm_map[oid].encstat[0] =
+ SES_OBJSTAT_OK;
+ cache->elm_map[oid].encstat[2] = 0;
+ cache->elm_map[oid].encstat[3] = 0;
+ }
+ cache->elm_map[oid++].svalid = 1;
+ }
+ r += 2;
+
+ cache->enc_status =
+ cfg->enc_status | cfg->slot_status | cfg->adm_status;
+ return (0);
+}
+
+static int
+safte_process_slotstatus(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ struct scfg *cfg;
+ uint8_t *buf = *bufp;
+ enc_cache_t *cache = &enc->enc_cache;
+ int oid, r, i;
+
+ cfg = enc->enc_private;
+ if (cfg == NULL)
+ return (ENXIO);
+ if (error != 0)
+ return (error);
+ cfg->slot_status = 0;
+ oid = cfg->slotoff;
+ for (r = i = 0; i < cfg->Nslots; i++, r += 4) {
+ SAFT_BAIL(r+3, xfer_len);
+ if (cache->elm_map[oid].enctype == ELMTYP_ARRAY_DEV)
+ cache->elm_map[oid].encstat[1] = 0;
+ cache->elm_map[oid].encstat[2] &= SESCTL_RQSID;
+ cache->elm_map[oid].encstat[3] = 0;
+ if ((buf[r+3] & 0x01) == 0) { /* no device */
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NOTINSTALLED;
+ } else if (buf[r+0] & 0x02) {
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_CRIT;
+ cfg->slot_status |= SES_ENCSTAT_CRITICAL;
+ } else if (buf[r+0] & 0x40) {
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_NONCRIT;
+ cfg->slot_status |= SES_ENCSTAT_NONCRITICAL;
+ } else {
+ cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK;
+ }
+ if (buf[r+3] & 0x2) {
+ if (buf[r+3] & 0x01)
+ cache->elm_map[oid].encstat[2] |= SESCTL_RQSRMV;
+ else
+ cache->elm_map[oid].encstat[2] |= SESCTL_RQSINS;
+ }
+ if ((buf[r+3] & 0x04) == 0)
+ cache->elm_map[oid].encstat[3] |= SESCTL_DEVOFF;
+ if (buf[r+0] & 0x02)
+ cache->elm_map[oid].encstat[3] |= SESCTL_RQSFLT;
+ if (buf[r+0] & 0x40)
+ cache->elm_map[oid].encstat[0] |= SESCTL_PRDFAIL;
+ if (cache->elm_map[oid].enctype == ELMTYP_ARRAY_DEV) {
+ if (buf[r+0] & 0x01)
+ cache->elm_map[oid].encstat[1] |= 0x80;
+ if (buf[r+0] & 0x04)
+ cache->elm_map[oid].encstat[1] |= 0x02;
+ if (buf[r+0] & 0x08)
+ cache->elm_map[oid].encstat[1] |= 0x04;
+ if (buf[r+0] & 0x10)
+ cache->elm_map[oid].encstat[1] |= 0x08;
+ if (buf[r+0] & 0x20)
+ cache->elm_map[oid].encstat[1] |= 0x10;
+ if (buf[r+1] & 0x01)
+ cache->elm_map[oid].encstat[1] |= 0x20;
+ if (buf[r+1] & 0x02)
+ cache->elm_map[oid].encstat[1] |= 0x01;
+ }
+ cache->elm_map[oid++].svalid = 1;
+ }
+
+ cache->enc_status =
+ cfg->enc_status | cfg->slot_status | cfg->adm_status;
+ return (0);
+}
+
+static int
+safte_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t *buf)
+{
+ struct scfg *cfg;
+ enc_element_t *ep, *ep1;
+ safte_control_request_t *req;
+ int i, idx, xfer_len;
+
+ cfg = enc->enc_private;
+ if (cfg == NULL)
+ return (ENXIO);
+
+ if (enc->enc_cache.nelms == 0) {
+ enc_update_request(enc, SAFTE_UPDATE_READCONFIG);
+ return (-1);
+ }
+
+ if (cfg->current_request == NULL) {
+ cfg->current_request = TAILQ_FIRST(&cfg->requests);
+ TAILQ_REMOVE(&cfg->requests, cfg->current_request, links);
+ cfg->current_request_stage = 0;
+ cfg->current_request_stages = 1;
+ }
+ req = cfg->current_request;
+
+ idx = (int)req->elm_idx;
+ if (req->elm_idx == SES_SETSTATUS_ENC_IDX) {
+ cfg->adm_status = req->elm_stat[0] & ALL_ENC_STAT;
+ cfg->flag1 &= ~(SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN);
+ if (req->elm_stat[0] & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV))
+ cfg->flag1 |= SAFT_FLG1_GLOBFAIL;
+ else if (req->elm_stat[0] & SES_ENCSTAT_NONCRITICAL)
+ cfg->flag1 |= SAFT_FLG1_GLOBWARN;
+ buf[0] = SAFTE_WT_GLOBAL;
+ buf[1] = cfg->flag1;
+ buf[2] = cfg->flag2;
+ buf[3] = 0;
+ xfer_len = 16;
+ } else {
+ ep = &enc->enc_cache.elm_map[idx];
+
+ switch (ep->enctype) {
+ case ELMTYP_DEVICE:
+ case ELMTYP_ARRAY_DEV:
+ switch (cfg->current_request_stage) {
+ case 0:
+ ep->priv = 0;
+ if (req->elm_stat[0] & SESCTL_PRDFAIL)
+ ep->priv |= 0x40;
+ if (req->elm_stat[3] & SESCTL_RQSFLT)
+ ep->priv |= 0x02;
+ if (ep->enctype == ELMTYP_ARRAY_DEV) {
+ if (req->elm_stat[1] & 0x01)
+ ep->priv |= 0x200;
+ if (req->elm_stat[1] & 0x02)
+ ep->priv |= 0x04;
+ if (req->elm_stat[1] & 0x04)
+ ep->priv |= 0x08;
+ if (req->elm_stat[1] & 0x08)
+ ep->priv |= 0x10;
+ if (req->elm_stat[1] & 0x10)
+ ep->priv |= 0x20;
+ if (req->elm_stat[1] & 0x20)
+ ep->priv |= 0x100;
+ if (req->elm_stat[1] & 0x80)
+ ep->priv |= 0x01;
+ }
+ if (ep->priv == 0)
+ ep->priv |= 0x01; /* no errors */
+
+ buf[0] = SAFTE_WT_DSTAT;
+ for (i = 0; i < cfg->Nslots; i++) {
+ ep1 = &enc->enc_cache.elm_map[cfg->slotoff + i];
+ buf[1 + (3 * i)] = ep1->priv;
+ buf[2 + (3 * i)] = ep1->priv >> 8;
+ }
+ xfer_len = cfg->Nslots * 3 + 1;
+#define DEVON(x) (!(((x)[2] & SESCTL_RQSINS) | \
+ ((x)[2] & SESCTL_RQSRMV) | \
+ ((x)[3] & SESCTL_DEVOFF)))
+ if (DEVON(req->elm_stat) != DEVON(ep->encstat))
+ cfg->current_request_stages++;
+#define IDON(x) (!!((x)[2] & SESCTL_RQSID))
+ if (IDON(req->elm_stat) != IDON(ep->encstat))
+ cfg->current_request_stages++;
+ break;
+ case 1:
+ case 2:
+ buf[0] = SAFTE_WT_SLTOP;
+ buf[1] = idx - cfg->slotoff;
+ if (cfg->current_request_stage == 1 &&
+ DEVON(req->elm_stat) != DEVON(ep->encstat)) {
+ if (DEVON(req->elm_stat))
+ buf[2] = 0x01;
+ else
+ buf[2] = 0x02;
+ } else {
+ if (IDON(req->elm_stat))
+ buf[2] = 0x04;
+ else
+ buf[2] = 0x00;
+ ep->encstat[2] &= ~SESCTL_RQSID;
+ ep->encstat[2] |= req->elm_stat[2] &
+ SESCTL_RQSID;
+ }
+ xfer_len = 64;
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+ case ELMTYP_POWER:
+ cfg->current_request_stages = 2;
+ switch (cfg->current_request_stage) {
+ case 0:
+ if (req->elm_stat[3] & SESCTL_RQSTFAIL) {
+ cfg->flag1 |= SAFT_FLG1_ENCPWRFAIL;
+ } else {
+ cfg->flag1 &= ~SAFT_FLG1_ENCPWRFAIL;
+ }
+ buf[0] = SAFTE_WT_GLOBAL;
+ buf[1] = cfg->flag1;
+ buf[2] = cfg->flag2;
+ buf[3] = 0;
+ xfer_len = 16;
+ break;
+ case 1:
+ buf[0] = SAFTE_WT_ACTPWS;
+ buf[1] = idx - cfg->pwroff;
+ if (req->elm_stat[3] & SESCTL_RQSTON)
+ buf[2] = 0x01;
+ else
+ buf[2] = 0x00;
+ buf[3] = 0;
+ xfer_len = 16;
+ default:
+ return (EINVAL);
+ }
+ break;
+ case ELMTYP_FAN:
+ if ((req->elm_stat[3] & 0x7) != 0)
+ cfg->current_request_stages = 2;
+ switch (cfg->current_request_stage) {
+ case 0:
+ if (req->elm_stat[3] & SESCTL_RQSTFAIL)
+ cfg->flag1 |= SAFT_FLG1_ENCFANFAIL;
+ else
+ cfg->flag1 &= ~SAFT_FLG1_ENCFANFAIL;
+ buf[0] = SAFTE_WT_GLOBAL;
+ buf[1] = cfg->flag1;
+ buf[2] = cfg->flag2;
+ buf[3] = 0;
+ xfer_len = 16;
+ break;
+ case 1:
+ buf[0] = SAFTE_WT_FANSPD;
+ buf[1] = idx;
+ if (req->elm_stat[3] & SESCTL_RQSTON) {
+ if ((req->elm_stat[3] & 0x7) == 7)
+ buf[2] = 4;
+ else if ((req->elm_stat[3] & 0x7) >= 5)
+ buf[2] = 3;
+ else if ((req->elm_stat[3] & 0x7) >= 3)
+ buf[2] = 2;
+ else
+ buf[2] = 1;
+ } else
+ buf[2] = 0;
+ buf[3] = 0;
+ xfer_len = 16;
+ ep->encstat[3] = req->elm_stat[3] & 0x67;
+ default:
+ return (EINVAL);
+ }
+ break;
+ case ELMTYP_DOORLOCK:
+ if (req->elm_stat[3] & 0x1)
+ cfg->flag2 &= ~SAFT_FLG2_LOCKDOOR;
+ else
+ cfg->flag2 |= SAFT_FLG2_LOCKDOOR;
+ buf[0] = SAFTE_WT_GLOBAL;
+ buf[1] = cfg->flag1;
+ buf[2] = cfg->flag2;
+ buf[3] = 0;
+ xfer_len = 16;
+ break;
+ case ELMTYP_ALARM:
+ if ((req->elm_stat[0] & SESCTL_DISABLE) ||
+ (req->elm_stat[3] & 0x40)) {
+ cfg->flag2 &= ~SAFT_FLG1_ALARM;
+ } else if ((req->elm_stat[3] & 0x0f) != 0) {
+ cfg->flag2 |= SAFT_FLG1_ALARM;
+ } else {
+ cfg->flag2 &= ~SAFT_FLG1_ALARM;
+ }
+ buf[0] = SAFTE_WT_GLOBAL;
+ buf[1] = cfg->flag1;
+ buf[2] = cfg->flag2;
+ buf[3] = 0;
+ xfer_len = 16;
+ ep->encstat[3] = req->elm_stat[3];
+ break;
+ default:
+ return (EINVAL);
+ }
+ }
+
+ if (enc->enc_type == ENC_SEMB_SAFT) {
+ semb_write_buffer(&ccb->ataio, /*retries*/5,
+ enc_done, MSG_SIMPLE_Q_TAG,
+ buf, xfer_len, state->timeout);
+ } else {
+ scsi_write_buffer(&ccb->csio, /*retries*/5,
+ enc_done, MSG_SIMPLE_Q_TAG, 1,
+ 0, 0, buf, xfer_len,
+ SSD_FULL_SIZE, state->timeout);
+ }
+ return (0);
+}
+
+static int
+safte_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ struct scfg *cfg;
+ safte_control_request_t *req;
+ int idx, type;
+
+ cfg = enc->enc_private;
+ if (cfg == NULL)
+ return (ENXIO);
+
+ req = cfg->current_request;
+ if (req->result == 0)
+ req->result = error;
+ if (++cfg->current_request_stage >= cfg->current_request_stages) {
+ idx = req->elm_idx;
+ if (idx == SES_SETSTATUS_ENC_IDX)
+ type = -1;
+ else
+ type = enc->enc_cache.elm_map[idx].enctype;
+ if (type == ELMTYP_DEVICE || type == ELMTYP_ARRAY_DEV)
+ enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS);
+ else
+ enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS);
+ cfg->current_request = NULL;
+ wakeup(req);
+ } else {
+ enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS);
+ }
+ return (0);
+}
+
+static void
+safte_softc_invalidate(enc_softc_t *enc)
+{
+ struct scfg *cfg;
+
+ cfg = enc->enc_private;
+ safte_terminate_control_requests(&cfg->requests, ENXIO);
+}
+
+static void
+safte_softc_cleanup(enc_softc_t *enc)
+{
+
+ ENC_FREE_AND_NULL(enc->enc_cache.elm_map);
+ ENC_FREE_AND_NULL(enc->enc_private);
+ enc->enc_cache.nelms = 0;
+}
+
+static int
+safte_init_enc(enc_softc_t *enc)
+{
+ struct scfg *cfg;
+ int err;
+ static char cdb0[6] = { SEND_DIAGNOSTIC };
+
+ cfg = enc->enc_private;
+ if (cfg == NULL)
+ return (ENXIO);
+
+ err = enc_runcmd(enc, cdb0, 6, NULL, 0);
+ if (err) {
+ return (err);
+ }
+ DELAY(5000);
+ cfg->flag1 = 0;
+ cfg->flag2 = 0;
+ err = safte_set_enc_status(enc, 0, 1);
+ return (err);
+}
+
+static int
+safte_get_enc_status(enc_softc_t *enc, int slpflg)
+{
+
+ return (0);
+}
+
+static int
+safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag)
+{
+ struct scfg *cfg;
+ safte_control_request_t req;
+
+ cfg = enc->enc_private;
+ if (cfg == NULL)
+ return (ENXIO);
+
+ req.elm_idx = SES_SETSTATUS_ENC_IDX;
+ req.elm_stat[0] = encstat & 0xf;
+ req.result = 0;
+
+ TAILQ_INSERT_TAIL(&cfg->requests, &req, links);
+ enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS);
+ cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0);
+
+ return (req.result);
+}
+
+static int
+safte_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflg)
+{
+ int i = (int)elms->elm_idx;
+
+ elms->cstat[0] = enc->enc_cache.elm_map[i].encstat[0];
+ elms->cstat[1] = enc->enc_cache.elm_map[i].encstat[1];
+ elms->cstat[2] = enc->enc_cache.elm_map[i].encstat[2];
+ elms->cstat[3] = enc->enc_cache.elm_map[i].encstat[3];
+ return (0);
+}
+
+static int
+safte_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag)
+{
+ struct scfg *cfg;
+ safte_control_request_t req;
+
+ cfg = enc->enc_private;
+ if (cfg == NULL)
+ return (ENXIO);
+
+ /* If this is clear, we don't do diddly. */
+ if ((elms->cstat[0] & SESCTL_CSEL) == 0)
+ return (0);
+
+ req.elm_idx = elms->elm_idx;
+ memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat));
+ req.result = 0;
+
+ TAILQ_INSERT_TAIL(&cfg->requests, &req, links);
+ enc_update_request(enc, SAFTE_PROCESS_CONTROL_REQS);
+ cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0);
+
+ return (req.result);
+}
+
+static void
+safte_poll_status(enc_softc_t *enc)
+{
+
+ enc_update_request(enc, SAFTE_UPDATE_READENCSTATUS);
+ enc_update_request(enc, SAFTE_UPDATE_READSLOTSTATUS);
+}
+
+static struct enc_vec safte_enc_vec =
+{
+ .softc_invalidate = safte_softc_invalidate,
+ .softc_cleanup = safte_softc_cleanup,
+ .init_enc = safte_init_enc,
+ .get_enc_status = safte_get_enc_status,
+ .set_enc_status = safte_set_enc_status,
+ .get_elm_status = safte_get_elm_status,
+ .set_elm_status = safte_set_elm_status,
+ .poll_status = safte_poll_status
+};
+
+int
+safte_softc_init(enc_softc_t *enc)
+{
+ struct scfg *cfg;
+
+ enc->enc_vec = safte_enc_vec;
+ enc->enc_fsm_states = enc_fsm_states;
+
+ if (enc->enc_private == NULL) {
+ enc->enc_private = ENC_MALLOCZ(SAFT_PRIVATE);
+ if (enc->enc_private == NULL)
+ return (ENOMEM);
+ }
+ cfg = enc->enc_private;
+
+ enc->enc_cache.nelms = 0;
+ enc->enc_cache.enc_status = 0;
+
+ TAILQ_INIT(&cfg->requests);
+ return (0);
+}
+
diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c
new file mode 100644
index 0000000..9b6fba3
--- /dev/null
+++ b/sys/cam/scsi/scsi_enc_ses.c
@@ -0,0 +1,2816 @@
+/*-
+ * Copyright (c) 2000 Matthew Jacob
+ * Copyright (c) 2010 Spectra Logic Corporation
+ * 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,
+ * without modification, immediately at the beginning of the file.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+/**
+ * \file scsi_enc_ses.c
+ *
+ * Structures and routines specific && private to SES only
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/sx.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_xpt_periph.h>
+#include <cam/cam_periph.h>
+
+#include <cam/scsi/scsi_message.h>
+#include <cam/scsi/scsi_enc.h>
+#include <cam/scsi/scsi_enc_internal.h>
+
+#include <opt_enc.h>
+
+/* SES Native Type Device Support */
+
+/* SES Diagnostic Page Codes */
+typedef enum {
+ SesSupportedPages = 0x0,
+ SesConfigPage = 0x1,
+ SesControlPage = 0x2,
+ SesStatusPage = SesControlPage,
+ SesHelpTxt = 0x3,
+ SesStringOut = 0x4,
+ SesStringIn = SesStringOut,
+ SesThresholdOut = 0x5,
+ SesThresholdIn = SesThresholdOut,
+ SesArrayControl = 0x6, /* Obsolete in SES v2 */
+ SesArrayStatus = SesArrayControl,
+ SesElementDescriptor = 0x7,
+ SesShortStatus = 0x8,
+ SesEnclosureBusy = 0x9,
+ SesAddlElementStatus = 0xa
+} SesDiagPageCodes;
+
+typedef struct ses_type {
+ const struct ses_elm_type_desc *hdr;
+ const char *text;
+} ses_type_t;
+
+typedef struct ses_comstat {
+ uint8_t comstatus;
+ uint8_t comstat[3];
+} ses_comstat_t;
+
+typedef union ses_addl_data {
+ struct ses_elm_sas_device_phy *sasdev_phys;
+ struct ses_elm_sas_expander_phy *sasexp_phys;
+ struct ses_elm_sas_port_phy *sasport_phys;
+ struct ses_fcobj_port *fc_ports;
+} ses_add_data_t;
+
+typedef struct ses_addl_status {
+ struct ses_elm_addlstatus_base_hdr *hdr;
+ union {
+ union ses_fcobj_hdr *fc;
+ union ses_elm_sas_hdr *sas;
+ } proto_hdr;
+ union ses_addl_data proto_data; /* array sizes stored in header */
+} ses_add_status_t;
+
+typedef struct ses_element {
+ uint8_t eip; /* eip bit is set */
+ uint16_t descr_len; /* length of the descriptor */
+ char *descr; /* descriptor for this object */
+ struct ses_addl_status addl; /* additional status info */
+} ses_element_t;
+
+typedef struct ses_control_request {
+ int elm_idx;
+ ses_comstat_t elm_stat;
+ int result;
+ TAILQ_ENTRY(ses_control_request) links;
+} ses_control_request_t;
+TAILQ_HEAD(ses_control_reqlist, ses_control_request);
+typedef struct ses_control_reqlist ses_control_reqlist_t;
+enum {
+ SES_SETSTATUS_ENC_IDX = -1
+};
+
+static void
+ses_terminate_control_requests(ses_control_reqlist_t *reqlist, int result)
+{
+ ses_control_request_t *req;
+
+ while ((req = TAILQ_FIRST(reqlist)) != NULL) {
+ TAILQ_REMOVE(reqlist, req, links);
+ req->result = result;
+ wakeup(req);
+ }
+}
+
+enum ses_iter_index_values {
+ /**
+ * \brief Value of an initialized but invalid index
+ * in a ses_iterator object.
+ *
+ * This value is used for the individual_element_index of
+ * overal status elements and for all index types when
+ * an iterator is first initialized.
+ */
+ ITERATOR_INDEX_INVALID = -1,
+
+ /**
+ * \brief Value of an index in a ses_iterator object
+ * when the iterator has traversed past the last
+ * valid element..
+ */
+ ITERATOR_INDEX_END = INT_MAX
+};
+
+/**
+ * \brief Structure encapsulating all data necessary to traverse the
+ * elements of a SES configuration.
+ *
+ * The ses_iterator object simplifies the task of iterating through all
+ * elements detected via the SES configuration page by tracking the numerous
+ * element indexes that, instead of memoizing in the softc, we calculate
+ * on the fly during the traversal of the element objects. The various
+ * indexes are necessary due to the varying needs of matching objects in
+ * the different SES pages. Some pages (e.g. Status/Control) contain all
+ * elements, while others (e.g. Additional Element Status) only contain
+ * individual elements (no overal status elements) of particular types.
+ *
+ * To use an iterator, initialize it with ses_iter_init(), and then
+ * use ses_iter_next() to traverse the elements (including the first) in
+ * the configuration. Once an iterator is initiailized with ses_iter_init(),
+ * you may also seek to any particular element by either it's global or
+ * individual element index via the ses_iter_seek_to() function. You may
+ * also return an iterator to the position just before the first element
+ * (i.e. the same state as after an ses_iter_init()), with ses_iter_reset().
+ */
+struct ses_iterator {
+ /**
+ * \brief Backlink to the overal software configuration structure.
+ *
+ * This is included for convenience so the iteration functions
+ * need only take a single, struct ses_iterator *, argument.
+ */
+ enc_softc_t *enc;
+
+ enc_cache_t *cache;
+
+ /**
+ * \brief Index of the type of the current element within the
+ * ses_cache's ses_types array.
+ */
+ int type_index;
+
+ /**
+ * \brief The position (0 based) of this element relative to all other
+ * elements of this type.
+ *
+ * This index resets to zero every time the iterator transitions
+ * to elements of a new type in the configuration.
+ */
+ int type_element_index;
+
+ /**
+ * \brief The position (0 based) of this element relative to all
+ * other individual status elements in the configuration.
+ *
+ * This index ranges from 0 through the number of individual
+ * elements in the configuration. When the iterator returns
+ * an overall status element, individual_element_index is
+ * set to ITERATOR_INDEX_INVALID, to indicate that it does
+ * not apply to the current element.
+ */
+ int individual_element_index;
+
+ /**
+ * \brief The position (0 based) of this element relative to
+ * all elements in the configration.
+ *
+ * This index is appropriate for indexing into enc->ses_elm_map.
+ */
+ int global_element_index;
+
+ /**
+ * \brief The last valid individual element index of this
+ * iterator.
+ *
+ * When an iterator traverses an overal status element, the
+ * individual element index is reset to ITERATOR_INDEX_INVALID
+ * to prevent unintential use of the individual_element_index
+ * field. The saved_individual_element_index allows the iterator
+ * to restore it's position in the individual elements upon
+ * reaching the next individual element.
+ */
+ int saved_individual_element_index;
+};
+
+typedef enum {
+ SES_UPDATE_NONE,
+ SES_UPDATE_PAGES,
+ SES_UPDATE_GETCONFIG,
+ SES_UPDATE_GETSTATUS,
+ SES_UPDATE_GETELMDESCS,
+ SES_UPDATE_GETELMADDLSTATUS,
+ SES_PROCESS_CONTROL_REQS,
+ SES_PUBLISH_PHYSPATHS,
+ SES_PUBLISH_CACHE,
+ SES_NUM_UPDATE_STATES
+} ses_update_action;
+
+static enc_softc_cleanup_t ses_softc_cleanup;
+
+#define SCSZ 0x8000
+
+static fsm_fill_handler_t ses_fill_rcv_diag_io;
+static fsm_fill_handler_t ses_fill_control_request;
+static fsm_done_handler_t ses_process_pages;
+static fsm_done_handler_t ses_process_config;
+static fsm_done_handler_t ses_process_status;
+static fsm_done_handler_t ses_process_elm_descs;
+static fsm_done_handler_t ses_process_elm_addlstatus;
+static fsm_done_handler_t ses_process_control_request;
+static fsm_done_handler_t ses_publish_physpaths;
+static fsm_done_handler_t ses_publish_cache;
+
+static struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] =
+{
+ { "SES_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL },
+ {
+ "SES_UPDATE_PAGES",
+ SesSupportedPages,
+ SCSZ,
+ 60 * 1000,
+ ses_fill_rcv_diag_io,
+ ses_process_pages,
+ enc_error
+ },
+ {
+ "SES_UPDATE_GETCONFIG",
+ SesConfigPage,
+ SCSZ,
+ 60 * 1000,
+ ses_fill_rcv_diag_io,
+ ses_process_config,
+ enc_error
+ },
+ {
+ "SES_UPDATE_GETSTATUS",
+ SesStatusPage,
+ SCSZ,
+ 60 * 1000,
+ ses_fill_rcv_diag_io,
+ ses_process_status,
+ enc_error
+ },
+ {
+ "SES_UPDATE_GETELMDESCS",
+ SesElementDescriptor,
+ SCSZ,
+ 60 * 1000,
+ ses_fill_rcv_diag_io,
+ ses_process_elm_descs,
+ enc_error
+ },
+ {
+ "SES_UPDATE_GETELMADDLSTATUS",
+ SesAddlElementStatus,
+ SCSZ,
+ 60 * 1000,
+ ses_fill_rcv_diag_io,
+ ses_process_elm_addlstatus,
+ enc_error
+ },
+ {
+ "SES_PROCESS_CONTROL_REQS",
+ SesControlPage,
+ SCSZ,
+ 60 * 1000,
+ ses_fill_control_request,
+ ses_process_control_request,
+ enc_error
+ },
+ {
+ "SES_PUBLISH_PHYSPATHS",
+ 0,
+ 0,
+ 0,
+ NULL,
+ ses_publish_physpaths,
+ NULL
+ },
+ {
+ "SES_PUBLISH_CACHE",
+ 0,
+ 0,
+ 0,
+ NULL,
+ ses_publish_cache,
+ NULL
+ }
+};
+
+typedef struct ses_cache {
+ /* Source for all the configuration data pointers */
+ const struct ses_cfg_page *cfg_page;
+
+ /* References into the config page. */
+ const struct ses_enc_desc * const *subencs;
+ uint8_t ses_ntypes;
+ const ses_type_t *ses_types;
+
+ /* Source for all the status pointers */
+ const struct ses_status_page *status_page;
+
+ /* Source for all the object descriptor pointers */
+ const struct ses_elem_descr_page *elm_descs_page;
+
+ /* Source for all the additional object status pointers */
+ const struct ses_addl_elem_status_page *elm_addlstatus_page;
+
+} ses_cache_t;
+
+typedef struct ses_softc {
+ uint32_t ses_flags;
+#define SES_FLAG_TIMEDCOMP 0x01
+#define SES_FLAG_ADDLSTATUS 0x02
+
+ ses_control_reqlist_t ses_requests;
+ ses_control_reqlist_t ses_pending_requests;
+} ses_softc_t;
+
+/**
+ * \brief Reset a SES iterator to just before the first element
+ * in the configuration.
+ *
+ * \param iter The iterator object to reset.
+ *
+ * The indexes within a reset iterator are invalid and will only
+ * become valid upon completion of a ses_iter_seek_to() or a
+ * ses_iter_next().
+ */
+static void
+ses_iter_reset(struct ses_iterator *iter)
+{
+ /*
+ * Set our indexes to just before the first valid element
+ * of the first type (ITERATOR_INDEX_INVALID == -1). This
+ * simplifies the implementation of ses_iter_next().
+ */
+ iter->type_index = 0;
+ iter->type_element_index = ITERATOR_INDEX_INVALID;
+ iter->global_element_index = ITERATOR_INDEX_INVALID;
+ iter->individual_element_index = ITERATOR_INDEX_INVALID;
+ iter->saved_individual_element_index = ITERATOR_INDEX_INVALID;
+}
+
+/**
+ * \brief Initialize the storage of a SES iterator and reset it to
+ * the position just before the first element of the
+ * configuration.
+ *
+ * \param enc The SES softc for the SES instance whose configuration
+ * will be enumerated by this iterator.
+ * \param iter The iterator object to initialize.
+ */
+static void
+ses_iter_init(enc_softc_t *enc, enc_cache_t *cache, struct ses_iterator *iter)
+{
+ iter->enc = enc;
+ iter->cache = cache;
+ ses_iter_reset(iter);
+}
+
+/**
+ * \brief Traverse the provided SES iterator to the next element
+ * within the configuraiton.
+ *
+ * \param iter The iterator to move.
+ *
+ * \return If a valid next element exists, a pointer to it's enc_element_t.
+ * Otherwise NULL.
+ */
+static enc_element_t *
+ses_iter_next(struct ses_iterator *iter)
+{
+ ses_cache_t *ses_cache;
+ const ses_type_t *element_type;
+
+ ses_cache = iter->cache->private;
+
+ /*
+ * Note: Treat nelms as signed, so we will hit this case
+ * and immediately terminate the iteration if the
+ * configuration has 0 objects.
+ */
+ if (iter->global_element_index >= (int)iter->cache->nelms - 1) {
+
+ /* Elements exhausted. */
+ iter->type_index = ITERATOR_INDEX_END;
+ iter->type_element_index = ITERATOR_INDEX_END;
+ iter->global_element_index = ITERATOR_INDEX_END;
+ iter->individual_element_index = ITERATOR_INDEX_END;
+ return (NULL);
+ }
+
+ KASSERT((iter->type_index < ses_cache->ses_ntypes),
+ ("Corrupted element iterator. %d not less than %d",
+ iter->type_index, ses_cache->ses_ntypes));
+
+ element_type = &ses_cache->ses_types[iter->type_index];
+ iter->global_element_index++;
+ iter->type_element_index++;
+
+ /*
+ * There is an object for overal type status in addition
+ * to one for each allowed element, but only if the element
+ * count is non-zero.
+ */
+ if (iter->type_element_index > element_type->hdr->etype_maxelt) {
+
+ /*
+ * We've exhausted the elements of this type.
+ * This next element belongs to the next type.
+ */
+ iter->type_index++;
+ iter->type_element_index = 0;
+ iter->saved_individual_element_index
+ = iter->individual_element_index;
+ iter->individual_element_index = ITERATOR_INDEX_INVALID;
+ }
+
+ if (iter->type_element_index > 0) {
+ if (iter->type_element_index == 1) {
+ iter->individual_element_index
+ = iter->saved_individual_element_index;
+ }
+ iter->individual_element_index++;
+ }
+
+ return (&iter->cache->elm_map[iter->global_element_index]);
+}
+
+/**
+ * Element index types tracked by a SES iterator.
+ */
+typedef enum {
+ /**
+ * Index relative to all elements (overall and individual)
+ * in the system.
+ */
+ SES_ELEM_INDEX_GLOBAL,
+
+ /**
+ * \brief Index relative to all individual elements in the system.
+ *
+ * This index counts only individual elements, skipping overall
+ * status elements. This is the index space of the additional
+ * element status page (page 0xa).
+ */
+ SES_ELEM_INDEX_INDIVIDUAL
+} ses_elem_index_type_t;
+
+/**
+ * \brief Move the provided iterator forwards or backwards to the object
+ * having the give index.
+ *
+ * \param iter The iterator on which to perform the seek.
+ * \param element_index The index of the element to find.
+ * \param index_type The type (global or individual) of element_index.
+ *
+ * \return If the element is found, a pointer to it's enc_element_t.
+ * Otherwise NULL.
+ */
+static enc_element_t *
+ses_iter_seek_to(struct ses_iterator *iter, int element_index,
+ ses_elem_index_type_t index_type)
+{
+ enc_element_t *element;
+ int *cur_index;
+
+ if (index_type == SES_ELEM_INDEX_GLOBAL)
+ cur_index = &iter->global_element_index;
+ else
+ cur_index = &iter->individual_element_index;
+
+ if (*cur_index == element_index) {
+ /* Already there. */
+ return (&iter->cache->elm_map[iter->global_element_index]);
+ }
+
+ ses_iter_reset(iter);
+ while ((element = ses_iter_next(iter)) != NULL
+ && *cur_index != element_index)
+ ;
+
+ if (*cur_index != element_index)
+ return (NULL);
+
+ return (element);
+}
+
+#if 0
+static int ses_encode(enc_softc_t *, uint8_t *, int, int,
+ struct ses_comstat *);
+#endif
+static int ses_set_timed_completion(enc_softc_t *, uint8_t);
+#if 0
+static int ses_putstatus(enc_softc_t *, int, struct ses_comstat *);
+#endif
+
+static void ses_print_addl_data(enc_softc_t *, enc_element_t *);
+
+/*=========================== SES cleanup routines ===========================*/
+
+static void
+ses_cache_free_elm_addlstatus(enc_softc_t *enc, enc_cache_t *cache)
+{
+ ses_cache_t *ses_cache;
+ ses_cache_t *other_ses_cache;
+ enc_element_t *cur_elm;
+ enc_element_t *last_elm;
+
+ ENC_DLOG(enc, "%s: enter\n", __func__);
+ ses_cache = cache->private;
+ if (ses_cache->elm_addlstatus_page == NULL)
+ return;
+
+ for (cur_elm = cache->elm_map,
+ last_elm = &cache->elm_map[cache->nelms - 1];
+ cur_elm <= last_elm; cur_elm++) {
+ ses_element_t *elmpriv;
+
+ elmpriv = cur_elm->elm_private;
+
+ /* Clear references to the additional status page. */
+ bzero(&elmpriv->addl, sizeof(elmpriv->addl));
+ }
+
+ other_ses_cache = enc_other_cache(enc, cache)->private;
+ if (other_ses_cache->elm_addlstatus_page
+ != ses_cache->elm_addlstatus_page)
+ ENC_FREE(ses_cache->elm_addlstatus_page);
+ ses_cache->elm_addlstatus_page = NULL;
+}
+
+static void
+ses_cache_free_elm_descs(enc_softc_t *enc, enc_cache_t *cache)
+{
+ ses_cache_t *ses_cache;
+ ses_cache_t *other_ses_cache;
+ enc_element_t *cur_elm;
+ enc_element_t *last_elm;
+
+ ENC_DLOG(enc, "%s: enter\n", __func__);
+ ses_cache = cache->private;
+ if (ses_cache->elm_descs_page == NULL)
+ return;
+
+ for (cur_elm = cache->elm_map,
+ last_elm = &cache->elm_map[cache->nelms - 1];
+ cur_elm <= last_elm; cur_elm++) {
+ ses_element_t *elmpriv;
+
+ elmpriv = cur_elm->elm_private;
+ elmpriv->descr_len = 0;
+ elmpriv->descr = NULL;
+ }
+
+ other_ses_cache = enc_other_cache(enc, cache)->private;
+ if (other_ses_cache->elm_descs_page
+ != ses_cache->elm_descs_page)
+ ENC_FREE(ses_cache->elm_descs_page);
+ ses_cache->elm_descs_page = NULL;
+}
+
+static void
+ses_cache_free_status(enc_softc_t *enc, enc_cache_t *cache)
+{
+ ses_cache_t *ses_cache;
+ ses_cache_t *other_ses_cache;
+
+ ENC_DLOG(enc, "%s: enter\n", __func__);
+ ses_cache = cache->private;
+ if (ses_cache->status_page == NULL)
+ return;
+
+ other_ses_cache = enc_other_cache(enc, cache)->private;
+ if (other_ses_cache->status_page != ses_cache->status_page)
+ ENC_FREE(ses_cache->status_page);
+ ses_cache->status_page = NULL;
+}
+
+static void
+ses_cache_free_elm_map(enc_softc_t *enc, enc_cache_t *cache)
+{
+ enc_element_t *cur_elm;
+ enc_element_t *last_elm;
+
+ ENC_DLOG(enc, "%s: enter\n", __func__);
+ if (cache->elm_map == NULL)
+ return;
+
+ ses_cache_free_elm_descs(enc, cache);
+ ses_cache_free_elm_addlstatus(enc, cache);
+ for (cur_elm = cache->elm_map,
+ last_elm = &cache->elm_map[cache->nelms - 1];
+ cur_elm <= last_elm; cur_elm++) {
+
+ ENC_FREE_AND_NULL(cur_elm->elm_private);
+ }
+ ENC_FREE_AND_NULL(cache->elm_map);
+ cache->nelms = 0;
+ ENC_DLOG(enc, "%s: exit\n", __func__);
+}
+
+static void
+ses_cache_free(enc_softc_t *enc, enc_cache_t *cache)
+{
+ ses_cache_t *other_ses_cache;
+ ses_cache_t *ses_cache;
+
+ ENC_DLOG(enc, "%s: enter\n", __func__);
+ ses_cache_free_elm_addlstatus(enc, cache);
+ ses_cache_free_status(enc, cache);
+ ses_cache_free_elm_map(enc, cache);
+
+ ses_cache = cache->private;
+ ses_cache->ses_ntypes = 0;
+
+ other_ses_cache = enc_other_cache(enc, cache)->private;
+ if (other_ses_cache->subencs != ses_cache->subencs)
+ ENC_FREE(ses_cache->subencs);
+ ses_cache->subencs = NULL;
+
+ if (other_ses_cache->ses_types != ses_cache->ses_types)
+ ENC_FREE(ses_cache->ses_types);
+ ses_cache->ses_types = NULL;
+
+ if (other_ses_cache->cfg_page != ses_cache->cfg_page)
+ ENC_FREE(ses_cache->cfg_page);
+ ses_cache->cfg_page = NULL;
+
+ ENC_DLOG(enc, "%s: exit\n", __func__);
+}
+
+static void
+ses_cache_clone(enc_softc_t *enc, enc_cache_t *src, enc_cache_t *dst)
+{
+ ses_cache_t *dst_ses_cache;
+ ses_cache_t *src_ses_cache;
+ enc_element_t *src_elm;
+ enc_element_t *dst_elm;
+ enc_element_t *last_elm;
+
+ ses_cache_free(enc, dst);
+ src_ses_cache = src->private;
+ dst_ses_cache = dst->private;
+
+ /*
+ * The cloned enclosure cache and ses specific cache are
+ * mostly identical to the source.
+ */
+ *dst = *src;
+ *dst_ses_cache = *src_ses_cache;
+
+ /*
+ * But the ses cache storage is still independent. Restore
+ * the pointer that was clobbered by the structure copy above.
+ */
+ dst->private = dst_ses_cache;
+
+ /*
+ * The element map is independent even though it starts out
+ * pointing to the same constant page data.
+ */
+ dst->elm_map = ENC_MALLOCZ(dst->nelms * sizeof(enc_element_t));
+ memcpy(dst->elm_map, src->elm_map, dst->nelms * sizeof(enc_element_t));
+ for (dst_elm = dst->elm_map, src_elm = src->elm_map,
+ last_elm = &src->elm_map[src->nelms - 1];
+ src_elm <= last_elm; src_elm++, dst_elm++) {
+
+ dst_elm->elm_private = ENC_MALLOCZ(sizeof(ses_element_t));
+ memcpy(dst_elm->elm_private, src_elm->elm_private,
+ sizeof(ses_element_t));
+ }
+}
+
+/* Structure accessors. These are strongly typed to avoid errors. */
+
+int
+ses_elm_sas_descr_type(union ses_elm_sas_hdr *obj)
+{
+ return ((obj)->base_hdr.byte1 >> 6);
+}
+int
+ses_elm_addlstatus_proto(struct ses_elm_addlstatus_base_hdr *hdr)
+{
+ return ((hdr)->byte0 & 0xf);
+}
+int
+ses_elm_addlstatus_eip(struct ses_elm_addlstatus_base_hdr *hdr)
+{
+ return ((hdr)->byte0 >> 4) & 0x1;
+}
+int
+ses_elm_addlstatus_invalid(struct ses_elm_addlstatus_base_hdr *hdr)
+{
+ return ((hdr)->byte0 >> 7);
+}
+int
+ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *hdr)
+{
+ return ((hdr)->type0_noneip.byte1 & 0x1);
+}
+int
+ses_elm_sas_dev_phy_sata_dev(struct ses_elm_sas_device_phy *phy)
+{
+ return ((phy)->target_ports & 0x1);
+}
+int
+ses_elm_sas_dev_phy_sata_port(struct ses_elm_sas_device_phy *phy)
+{
+ return ((phy)->target_ports >> 7);
+}
+int
+ses_elm_sas_dev_phy_dev_type(struct ses_elm_sas_device_phy *phy)
+{
+ return (((phy)->byte0 >> 4) & 0x7);
+}
+
+/**
+ * \brief Verify that the cached configuration data in our softc
+ * is valid for processing the page data corresponding to
+ * the provided page header.
+ *
+ * \param ses_cache The SES cache to validate.
+ * \param gen_code The 4 byte generation code from a SES diagnostic
+ * page header.
+ *
+ * \return non-zero if true, 0 if false.
+ */
+static int
+ses_config_cache_valid(ses_cache_t *ses_cache, const uint8_t *gen_code)
+{
+ uint32_t cache_gc;
+ uint32_t cur_gc;
+
+ if (ses_cache->cfg_page == NULL)
+ return (0);
+
+ cache_gc = scsi_4btoul(ses_cache->cfg_page->hdr.gen_code);
+ cur_gc = scsi_4btoul(gen_code);
+ return (cache_gc == cur_gc);
+}
+
+/**
+ * Function signature for consumers of the ses_devids_iter() interface.
+ */
+typedef void ses_devid_callback_t(enc_softc_t *, enc_element_t *,
+ struct scsi_vpd_id_descriptor *, void *);
+
+/**
+ * \brief Iterate over and create vpd device id records from the
+ * additional element status data for elm, passing that data
+ * to the provided callback.
+ *
+ * \param enc SES instance containing elm
+ * \param elm Element for which to extract device ID data.
+ * \param callback The callback function to invoke on each generated
+ * device id descriptor for elm.
+ * \param callback_arg Argument passed through to callback on each invocation.
+ */
+static void
+ses_devids_iter(enc_softc_t *enc, enc_element_t *elm,
+ ses_devid_callback_t *callback, void *callback_arg)
+{
+ ses_element_t *elmpriv;
+ struct ses_addl_status *addl;
+ u_int i;
+ size_t devid_record_size;
+
+ elmpriv = elm->elm_private;
+ addl = &(elmpriv->addl);
+
+ /*
+ * Don't assume this object has additional status information, or
+ * that it is a SAS device, or that it is a device slot device.
+ */
+ if (addl->hdr == NULL || addl->proto_hdr.sas == NULL
+ || addl->proto_data.sasdev_phys == NULL)
+ return;
+
+ devid_record_size = SVPD_DEVICE_ID_DESC_HDR_LEN
+ + sizeof(struct scsi_vpd_id_naa_ieee_reg);
+ for (i = 0; i < addl->proto_hdr.sas->base_hdr.num_phys; i++) {
+ uint8_t devid_buf[devid_record_size];
+ struct scsi_vpd_id_descriptor *devid;
+ uint8_t *phy_addr;
+
+ devid = (struct scsi_vpd_id_descriptor *)devid_buf;
+ phy_addr = addl->proto_data.sasdev_phys[i].phy_addr;
+ devid->proto_codeset = (SCSI_PROTO_SAS << SVPD_ID_PROTO_SHIFT)
+ | SVPD_ID_CODESET_BINARY;
+ devid->id_type = SVPD_ID_PIV
+ | SVPD_ID_ASSOC_PORT
+ | SVPD_ID_TYPE_NAA;
+ devid->reserved = 0;
+ devid->length = sizeof(struct scsi_vpd_id_naa_ieee_reg);
+ memcpy(devid->identifier, phy_addr, devid->length);
+
+ callback(enc, elm, devid, callback_arg);
+ }
+}
+
+/**
+ * Function signature for consumers of the ses_paths_iter() interface.
+ */
+typedef void ses_path_callback_t(enc_softc_t *, enc_element_t *,
+ struct cam_path *, void *);
+
+/**
+ * Argument package passed through ses_devids_iter() by
+ * ses_paths_iter() to ses_path_iter_devid_callback().
+ */
+typedef struct ses_path_iter_args {
+ ses_path_callback_t *callback;
+ void *callback_arg;
+} ses_path_iter_args_t;
+
+/**
+ * ses_devids_iter() callback function used by ses_paths_iter()
+ * to map device ids to peripheral driver instances.
+ *
+ * \param enc SES instance containing elm
+ * \param elm Element on which device ID matching is active.
+ * \param periph A device ID corresponding to elm.
+ * \param arg Argument passed through to callback on each invocation.
+ */
+static void
+ses_path_iter_devid_callback(enc_softc_t *enc, enc_element_t *elem,
+ struct scsi_vpd_id_descriptor *devid,
+ void *arg)
+{
+ struct ccb_dev_match cdm;
+ struct dev_match_pattern match_pattern;
+ struct dev_match_result match_result;
+ struct device_match_result *device_match;
+ struct device_match_pattern *device_pattern;
+ ses_path_iter_args_t *args;
+
+ args = (ses_path_iter_args_t *)arg;
+ match_pattern.type = DEV_MATCH_DEVICE;
+ device_pattern = &match_pattern.pattern.device_pattern;
+ device_pattern->flags = DEV_MATCH_DEVID;
+ device_pattern->data.devid_pat.id_len =
+ offsetof(struct scsi_vpd_id_descriptor, identifier)
+ + devid->length;
+ memcpy(device_pattern->data.devid_pat.id, devid,
+ device_pattern->data.devid_pat.id_len);
+
+ memset(&cdm, 0, sizeof(cdm));
+ if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL, CAM_XPT_PATH_ID,
+ CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD) != CAM_REQ_CMP)
+ return;
+
+ cdm.ccb_h.func_code = XPT_DEV_MATCH;
+ cdm.num_patterns = 1;
+ cdm.patterns = &match_pattern;
+ cdm.pattern_buf_len = sizeof(match_pattern);
+ cdm.match_buf_len = sizeof(match_result);
+ cdm.matches = &match_result;
+
+ xpt_action((union ccb *)&cdm);
+ xpt_free_path(cdm.ccb_h.path);
+
+ if ((cdm.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP
+ || (cdm.status != CAM_DEV_MATCH_LAST
+ && cdm.status != CAM_DEV_MATCH_MORE)
+ || cdm.num_matches == 0)
+ return;
+
+ device_match = &match_result.result.device_result;
+ if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL,
+ device_match->path_id,
+ device_match->target_id,
+ device_match->target_lun) != CAM_REQ_CMP)
+ return;
+
+ args->callback(enc, elem, cdm.ccb_h.path, args->callback_arg);
+ xpt_free_path(cdm.ccb_h.path);
+}
+
+/**
+ * \brief Iterate over and find the matching periph objects for the
+ * specified element.
+ *
+ * \param enc SES instance containing elm
+ * \param elm Element for which to perform periph object matching.
+ * \param callback The callback function to invoke with each matching
+ * periph object.
+ * \param callback_arg Argument passed through to callback on each invocation.
+ */
+static void
+ses_paths_iter(enc_softc_t *enc, enc_element_t *elm,
+ ses_path_callback_t *callback, void *callback_arg)
+{
+ ses_path_iter_args_t args;
+
+ args.callback = callback;
+ args.callback_arg = callback_arg;
+ ses_devids_iter(enc, elm, ses_path_iter_devid_callback, &args);
+}
+
+/**
+ * ses_paths_iter() callback function used by ses_get_elmdevname()
+ * to record periph driver instance strings corresponding to a SES
+ * element.
+ *
+ * \param enc SES instance containing elm
+ * \param elm Element on which periph matching is active.
+ * \param periph A periph instance that matches elm.
+ * \param arg Argument passed through to callback on each invocation.
+ */
+static void
+ses_elmdevname_callback(enc_softc_t *enc, enc_element_t *elem,
+ struct cam_path *path, void *arg)
+{
+ struct sbuf *sb;
+
+ sb = (struct sbuf *)arg;
+ cam_periph_list(path, sb);
+}
+
+/**
+ * Argument package passed through ses_paths_iter() to
+ * ses_getcampath_callback.
+ */
+typedef struct ses_setphyspath_callback_args {
+ struct sbuf *physpath;
+ int num_set;
+} ses_setphyspath_callback_args_t;
+
+/**
+ * \brief ses_paths_iter() callback to set the physical path on the
+ * CAM EDT entries corresponding to a given SES element.
+ *
+ * \param enc SES instance containing elm
+ * \param elm Element on which periph matching is active.
+ * \param periph A periph instance that matches elm.
+ * \param arg Argument passed through to callback on each invocation.
+ */
+static void
+ses_setphyspath_callback(enc_softc_t *enc, enc_element_t *elm,
+ struct cam_path *path, void *arg)
+{
+ struct ccb_dev_advinfo cdai;
+ ses_setphyspath_callback_args_t *args;
+ char *old_physpath;
+
+ args = (ses_setphyspath_callback_args_t *)arg;
+ old_physpath = malloc(MAXPATHLEN, M_SCSIENC, M_WAITOK|M_ZERO);
+
+ xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL);
+ cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
+ cdai.buftype = CDAI_TYPE_PHYS_PATH;
+ cdai.flags = 0;
+ cdai.bufsiz = MAXPATHLEN;
+ cdai.buf = old_physpath;
+ xpt_action((union ccb *)&cdai);
+ if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
+ cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE);
+
+ if (strcmp(old_physpath, sbuf_data(args->physpath)) != 0) {
+
+ xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL);
+ cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
+ cdai.buftype = CDAI_TYPE_PHYS_PATH;
+ cdai.flags |= CDAI_FLAG_STORE;
+ cdai.bufsiz = sbuf_len(args->physpath);
+ cdai.buf = sbuf_data(args->physpath);
+ xpt_action((union ccb *)&cdai);
+ if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
+ cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE);
+ if (cdai.ccb_h.status == CAM_REQ_CMP)
+ args->num_set++;
+ }
+ free(old_physpath, M_SCSIENC);
+}
+
+/**
+ * \brief Set a device's physical path string in CAM XPT.
+ *
+ * \param enc SES instance containing elm
+ * \param elm Element to publish physical path string for
+ * \param iter Iterator whose state corresponds to elm
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_set_physpath(enc_softc_t *enc, enc_element_t *elm,
+ struct ses_iterator *iter)
+{
+ struct ccb_dev_advinfo cdai;
+ ses_setphyspath_callback_args_t args;
+ int ret;
+ struct sbuf sb;
+ uint8_t *devid, *elmaddr;
+ ses_element_t *elmpriv;
+
+ ret = EIO;
+ devid = NULL;
+
+ /*
+ * Assemble the components of the physical path starting with
+ * the device ID of the enclosure itself.
+ */
+ xpt_setup_ccb(&cdai.ccb_h, enc->periph->path, CAM_PRIORITY_NORMAL);
+ cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
+ cdai.buftype = CDAI_TYPE_SCSI_DEVID;
+ cdai.bufsiz = CAM_SCSI_DEVID_MAXLEN;
+ cdai.buf = devid = ENC_MALLOCZ(cdai.bufsiz);
+ if (devid == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ xpt_action((union ccb *)&cdai);
+ if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
+ cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE);
+ if (cdai.ccb_h.status != CAM_REQ_CMP)
+ goto out;
+
+ elmaddr = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf,
+ cdai.provsiz, scsi_devid_is_naa_ieee_reg);
+ if (elmaddr == NULL)
+ goto out;
+
+ if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ /* Next, generate the physical path string */
+ sbuf_printf(&sb, "id1,enc@n%jx/type@%x/slot@%x",
+ scsi_8btou64(elmaddr), iter->type_index,
+ iter->type_element_index);
+ /* Append the element descriptor if one exists */
+ elmpriv = elm->elm_private;
+ if (elmpriv->descr != NULL && elmpriv->descr_len > 0) {
+ sbuf_cat(&sb, "/elmdesc@");
+ sbuf_bcat(&sb, elmpriv->descr, elmpriv->descr_len);
+ }
+ sbuf_finish(&sb);
+
+ /*
+ * Set this physical path on any CAM devices with a device ID
+ * descriptor that matches one created from the SES additional
+ * status data for this element.
+ */
+ args.physpath= &sb;
+ args.num_set = 0;
+ ses_paths_iter(enc, elm, ses_setphyspath_callback, &args);
+ sbuf_delete(&sb);
+
+ ret = args.num_set == 0 ? ENOENT : 0;
+
+out:
+ if (devid != NULL)
+ ENC_FREE(devid);
+ return (ret);
+}
+
+/**
+ * \brief Helper to set the CDB fields appropriately.
+ *
+ * \param cdb Buffer containing the cdb.
+ * \param pagenum SES diagnostic page to query for.
+ * \param dir Direction of query.
+ */
+static void
+ses_page_cdb(char *cdb, int bufsiz, SesDiagPageCodes pagenum, int dir)
+{
+
+ /* Ref: SPC-4 r25 Section 6.20 Table 223 */
+ if (dir == CAM_DIR_IN) {
+ cdb[0] = RECEIVE_DIAGNOSTIC;
+ cdb[1] = 1; /* Set page code valid bit */
+ cdb[2] = pagenum;
+ } else {
+ cdb[0] = SEND_DIAGNOSTIC;
+ cdb[1] = 0x10;
+ cdb[2] = pagenum;
+ }
+ cdb[3] = bufsiz >> 8; /* high bits */
+ cdb[4] = bufsiz & 0xff; /* low bits */
+ cdb[5] = 0;
+}
+
+/**
+ * \brief Discover whether this instance supports timed completion of a
+ * RECEIVE DIAGNOSTIC RESULTS command requesting the Enclosure Status
+ * page, and store the result in the softc, updating if necessary.
+ *
+ * \param enc SES instance to query and update.
+ * \param tc_en Value of timed completion to set (see \return).
+ *
+ * \return 1 if timed completion enabled, 0 otherwise.
+ */
+static int
+ses_set_timed_completion(enc_softc_t *enc, uint8_t tc_en)
+{
+ int err;
+ union ccb *ccb;
+ struct cam_periph *periph;
+ struct ses_mgmt_mode_page *mgmt;
+ uint8_t *mode_buf;
+ size_t mode_buf_len;
+ ses_softc_t *ses;
+
+ periph = enc->periph;
+ ses = enc->enc_private;
+ ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
+
+ mode_buf_len = sizeof(struct ses_mgmt_mode_page);
+ mode_buf = ENC_MALLOCZ(mode_buf_len);
+ if (mode_buf == NULL)
+ goto out;
+
+ scsi_mode_sense(&ccb->csio, /*retries*/4, enc_done, MSG_SIMPLE_Q_TAG,
+ /*dbd*/FALSE, SMS_PAGE_CTRL_CURRENT, SES_MGMT_MODE_PAGE_CODE,
+ mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60 * 1000);
+
+ /*
+ * Ignore illegal request errors, as they are quite common and we
+ * will print something out in that case anyway.
+ */
+ err = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS,
+ ENC_FLAGS|SF_QUIET_IR, NULL);
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ ENC_LOG(enc, "Timed Completion Unsupported\n");
+ goto release;
+ }
+
+ /* Skip the mode select if the desired value is already set */
+ mgmt = (struct ses_mgmt_mode_page *)mode_buf;
+ if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) == tc_en)
+ goto done;
+
+ /* Value is not what we wanted, set it */
+ if (tc_en)
+ mgmt->byte5 |= SES_MGMT_TIMED_COMP_EN;
+ else
+ mgmt->byte5 &= ~SES_MGMT_TIMED_COMP_EN;
+ /* SES2r20: a completion time of zero means as long as possible */
+ bzero(&mgmt->max_comp_time, sizeof(mgmt->max_comp_time));
+
+ scsi_mode_select(&ccb->csio, 5, enc_done, MSG_SIMPLE_Q_TAG,
+ /*page_fmt*/FALSE, /*save_pages*/TRUE, mode_buf, mode_buf_len,
+ SSD_FULL_SIZE, /*timeout*/60 * 1000);
+
+ err = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL);
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ ENC_LOG(enc, "Timed Completion Set Failed\n");
+ goto release;
+ }
+
+done:
+ if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) != 0) {
+ ENC_LOG(enc, "Timed Completion Enabled\n");
+ ses->ses_flags |= SES_FLAG_TIMEDCOMP;
+ } else {
+ ENC_LOG(enc, "Timed Completion Disabled\n");
+ ses->ses_flags &= ~SES_FLAG_TIMEDCOMP;
+ }
+release:
+ ENC_FREE(mode_buf);
+ xpt_release_ccb(ccb);
+out:
+ return (ses->ses_flags & SES_FLAG_TIMEDCOMP);
+}
+
+/**
+ * \brief Process the list of supported pages and update flags.
+ *
+ * \param enc SES device to query.
+ * \param buf Buffer containing the config page.
+ * \param xfer_len Length of the config page in the buffer.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_process_pages(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ ses_softc_t *ses;
+ struct scsi_diag_page *page;
+ int err, i, length;
+
+ CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
+ ("entering %s(%p, %d)\n", __func__, bufp, xfer_len));
+ ses = enc->enc_private;
+ err = -1;
+
+ if (error != 0) {
+ err = error;
+ goto out;
+ }
+ if (xfer_len < sizeof(*page)) {
+ ENC_LOG(enc, "Unable to parse Diag Pages List Header\n");
+ err = EIO;
+ goto out;
+ }
+ page = (struct scsi_diag_page *)*bufp;
+ length = scsi_2btoul(page->length);
+ if (length + offsetof(struct scsi_diag_page, params) > xfer_len) {
+ ENC_LOG(enc, "Diag Pages List Too Long\n");
+ goto out;
+ }
+ ENC_DLOG(enc, "%s: page length %d, xfer_len %d\n",
+ __func__, length, xfer_len);
+
+ err = 0;
+ for (i = 0; i < length; i++) {
+ if (page->params[i] == SesAddlElementStatus) {
+ ses->ses_flags |= SES_FLAG_ADDLSTATUS;
+ break;
+ }
+ }
+
+out:
+ ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err);
+ return (err);
+}
+
+/**
+ * \brief Process the config page and update associated structures.
+ *
+ * \param enc SES device to query.
+ * \param buf Buffer containing the config page.
+ * \param xfer_len Length of the config page in the buffer.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ struct ses_iterator iter;
+ ses_softc_t *ses;
+ enc_cache_t *enc_cache;
+ ses_cache_t *ses_cache;
+ uint8_t *buf;
+ int length;
+ int err;
+ int nelm;
+ int ntype;
+ struct ses_cfg_page *cfg_page;
+ struct ses_enc_desc *buf_subenc;
+ const struct ses_enc_desc **subencs;
+ const struct ses_enc_desc **cur_subenc;
+ const struct ses_enc_desc **last_subenc;
+ ses_type_t *ses_types;
+ ses_type_t *sestype;
+ const struct ses_elm_type_desc *cur_buf_type;
+ const struct ses_elm_type_desc *last_buf_type;
+ uint8_t *last_valid_byte;
+ enc_element_t *element;
+ const char *type_text;
+
+ CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
+ ("entering %s(%p, %d)\n", __func__, bufp, xfer_len));
+ ses = enc->enc_private;
+ enc_cache = &enc->enc_daemon_cache;
+ ses_cache = enc_cache->private;
+ buf = *bufp;
+ err = -1;;
+
+ if (error != 0) {
+ err = error;
+ goto out;
+ }
+ if (xfer_len < sizeof(cfg_page->hdr)) {
+ ENC_LOG(enc, "Unable to parse SES Config Header\n");
+ err = EIO;
+ goto out;
+ }
+
+ cfg_page = (struct ses_cfg_page *)buf;
+ length = ses_page_length(&cfg_page->hdr);
+ if (length > xfer_len) {
+ ENC_LOG(enc, "Enclosure Config Page Too Long\n");
+ goto out;
+ }
+ last_valid_byte = &buf[length - 1];
+
+ ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n",
+ __func__, length, xfer_len);
+
+ err = 0;
+ if (ses_config_cache_valid(ses_cache, cfg_page->hdr.gen_code)) {
+
+ /* Our cache is still valid. Proceed to fetching status. */
+ goto out;
+ }
+
+ /* Cache is no longer valid. Free old data to make way for new. */
+ ses_cache_free(enc, enc_cache);
+ ENC_VLOG(enc, "Generation Code 0x%x has %d SubEnclosures\n",
+ scsi_4btoul(cfg_page->hdr.gen_code),
+ ses_cfg_page_get_num_subenc(cfg_page));
+
+ /* Take ownership of the buffer. */
+ ses_cache->cfg_page = cfg_page;
+ *bufp = NULL;
+
+ /*
+ * Now waltz through all the subenclosures summing the number of
+ * types available in each.
+ */
+ subencs = ENC_MALLOCZ(ses_cfg_page_get_num_subenc(cfg_page)
+ * sizeof(*subencs));
+ if (subencs == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+ /*
+ * Sub-enclosure data is const after construction (i.e. when
+ * accessed via our cache object.
+ *
+ * The cast here is not required in C++ but C99 is not so
+ * sophisticated (see C99 6.5.16.1(1)).
+ */
+ ses_cache->subencs = subencs;
+
+ buf_subenc = cfg_page->subencs;
+ cur_subenc = subencs;
+ last_subenc = &subencs[ses_cfg_page_get_num_subenc(cfg_page) - 1];
+ ntype = 0;
+ while (cur_subenc <= last_subenc) {
+
+ if (!ses_enc_desc_is_complete(buf_subenc, last_valid_byte)) {
+ ENC_LOG(enc, "Enclosure %d Beyond End of "
+ "Descriptors\n", cur_subenc - subencs);
+ err = EIO;
+ goto out;
+ }
+
+ ENC_VLOG(enc, " SubEnclosure ID %d, %d Types With this ID, "
+ "Descriptor Length %d, offset %d\n", buf_subenc->subenc_id,
+ buf_subenc->num_types, buf_subenc->length,
+ &buf_subenc->byte0 - buf);
+ ENC_VLOG(enc, "WWN: %jx\n",
+ (uintmax_t)scsi_8btou64(buf_subenc->logical_id));
+
+ ntype += buf_subenc->num_types;
+ *cur_subenc = buf_subenc;
+ cur_subenc++;
+ buf_subenc = ses_enc_desc_next(buf_subenc);
+ }
+
+ /* Process the type headers. */
+ ses_types = ENC_MALLOCZ(ntype * sizeof(*ses_types));
+ if (ses_types == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+ /*
+ * Type data is const after construction (i.e. when accessed via
+ * our cache object.
+ */
+ ses_cache->ses_types = ses_types;
+
+ cur_buf_type = (const struct ses_elm_type_desc *)
+ (&(*last_subenc)->length + (*last_subenc)->length + 1);
+ last_buf_type = cur_buf_type + ntype - 1;
+ type_text = (const uint8_t *)(last_buf_type + 1);
+ nelm = 0;
+ sestype = ses_types;
+ while (cur_buf_type <= last_buf_type) {
+ if (&cur_buf_type->etype_txt_len > last_valid_byte) {
+ ENC_LOG(enc, "Runt Enclosure Type Header %d\n",
+ sestype - ses_types);
+ err = EIO;
+ goto out;
+ }
+ sestype->hdr = cur_buf_type;
+ sestype->text = type_text;
+ type_text += cur_buf_type->etype_txt_len;
+ ENC_LOG(enc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc "
+ "%d, Text Length %d: %.*s\n", sestype - ses_types,
+ sestype->hdr->etype_elm_type, sestype->hdr->etype_maxelt,
+ sestype->hdr->etype_subenc, sestype->hdr->etype_txt_len,
+ sestype->hdr->etype_txt_len, sestype->text);
+
+ nelm += sestype->hdr->etype_maxelt
+ + /*overall status element*/1;
+ sestype++;
+ cur_buf_type++;
+ }
+
+ /* Create the object map. */
+ enc_cache->elm_map = ENC_MALLOCZ(nelm * sizeof(enc_element_t));
+ if (enc_cache->elm_map == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+ ses_cache->ses_ntypes = (uint8_t)ntype;
+ enc_cache->nelms = nelm;
+
+ ses_iter_init(enc, enc_cache, &iter);
+ while ((element = ses_iter_next(&iter)) != NULL) {
+ const struct ses_elm_type_desc *thdr;
+
+ ENC_DLOG(enc, "%s: checking obj %d(%d,%d)\n", __func__,
+ iter.global_element_index, iter.type_index, nelm,
+ iter.type_element_index);
+ thdr = ses_cache->ses_types[iter.type_index].hdr;
+ element->subenclosure = thdr->etype_subenc;
+ element->enctype = thdr->etype_elm_type;
+ element->overall_status_elem = iter.type_element_index == 0;
+ element->elm_private = ENC_MALLOCZ(sizeof(ses_element_t));
+ if (element->elm_private == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+ ENC_DLOG(enc, "%s: creating elmpriv %d(%d,%d) subenc %d "
+ "type 0x%x\n", __func__, iter.global_element_index,
+ iter.type_index, iter.type_element_index,
+ thdr->etype_subenc, thdr->etype_elm_type);
+ }
+
+ err = 0;
+
+out:
+ if (err)
+ ses_softc_cleanup(enc);
+ else {
+ enc_update_request(enc, SES_UPDATE_GETSTATUS);
+ enc_update_request(enc, SES_UPDATE_GETELMDESCS);
+ if (ses->ses_flags & SES_FLAG_ADDLSTATUS)
+ enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS);
+ enc_update_request(enc, SES_PUBLISH_CACHE);
+ }
+ ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err);
+ return (err);
+}
+
+/**
+ * \brief Update the status page and associated structures.
+ *
+ * \param enc SES softc to update for.
+ * \param buf Buffer containing the status page.
+ * \param bufsz Amount of data in the buffer.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_process_status(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ struct ses_iterator iter;
+ enc_element_t *element;
+ ses_softc_t *ses;
+ enc_cache_t *enc_cache;
+ ses_cache_t *ses_cache;
+ uint8_t *buf;
+ int err = -1;
+ int length;
+ struct ses_status_page *page;
+ union ses_status_element *cur_stat;
+ union ses_status_element *last_stat;
+
+ ses = enc->enc_private;
+ enc_cache = &enc->enc_daemon_cache;
+ ses_cache = enc_cache->private;
+ buf = *bufp;
+
+ ENC_DLOG(enc, "%s: enter (%p, %p, %d)\n", __func__, enc, buf, xfer_len);
+ page = (struct ses_status_page *)buf;
+ length = ses_page_length(&page->hdr);
+
+ if (error != 0) {
+ err = error;
+ goto out;
+ }
+ /*
+ * Make sure the length fits in the buffer.
+ *
+ * XXX all this means is that the page is larger than the space
+ * we allocated. Since we use a statically sized buffer, this
+ * could happen... Need to use dynamic discovery of the size.
+ */
+ if (length > xfer_len) {
+ ENC_LOG(enc, "Enclosure Status Page Too Long\n");
+ goto out;
+ }
+ /* Make sure the length contains at least one header and status */
+ if (length < (sizeof(*page) + sizeof(*page->elements))) {
+ ENC_LOG(enc, "Enclosure Status Page Too Short\n");
+ goto out;
+ }
+
+ if (!ses_config_cache_valid(ses_cache, page->hdr.gen_code)) {
+ ENC_DLOG(enc, "%s: Generation count change detected\n",
+ __func__);
+ enc_update_request(enc, SES_UPDATE_GETCONFIG);
+ goto out;
+ }
+
+ ses_cache_free_status(enc, enc_cache);
+ ses_cache->status_page = page;
+ *bufp = NULL;
+
+ enc_cache->enc_status = page->hdr.page_specific_flags;
+
+ /*
+ * Read in individual element status. The element order
+ * matches the order reported in the config page (i.e. the
+ * order of an unfiltered iteration of the config objects)..
+ */
+ ses_iter_init(enc, enc_cache, &iter);
+ cur_stat = page->elements;
+ last_stat = (union ses_status_element *)
+ &buf[length - sizeof(*last_stat)];
+ ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n",
+ __func__, length, xfer_len);
+ while (cur_stat <= last_stat
+ && (element = ses_iter_next(&iter)) != NULL) {
+
+ ENC_DLOG(enc, "%s: obj %d(%d,%d) off=0x%tx status=%jx\n",
+ __func__, iter.global_element_index, iter.type_index,
+ iter.type_element_index, (uint8_t *)cur_stat - buf,
+ scsi_4btoul(cur_stat->bytes));
+
+ memcpy(&element->encstat, cur_stat, sizeof(element->encstat));
+ element->svalid = 1;
+ cur_stat++;
+ }
+
+ if (ses_iter_next(&iter) != NULL) {
+ ENC_LOG(enc, "Status page, length insufficient for "
+ "expected number of objects\n");
+ } else {
+ if (cur_stat <= last_stat)
+ ENC_LOG(enc, "Status page, exhausted objects before "
+ "exhausing page\n");
+ enc_update_request(enc, SES_PUBLISH_CACHE);
+ err = 0;
+ }
+out:
+ ENC_DLOG(enc, "%s: exiting with error %d\n", __func__, err);
+ return (err);
+}
+
+typedef enum {
+ /**
+ * The enclosure should not provide additional element
+ * status for this element type in page 0x0A.
+ *
+ * \note This status is returned for any types not
+ * listed SES3r02. Further types added in a
+ * future specification will be incorrectly
+ * classified.
+ */
+ TYPE_ADDLSTATUS_NONE,
+
+ /**
+ * The element type provides additional element status
+ * in page 0x0A.
+ */
+ TYPE_ADDLSTATUS_MANDATORY,
+
+ /**
+ * The element type may provide additional element status
+ * in page 0x0A, but i
+ */
+ TYPE_ADDLSTATUS_OPTIONAL
+} ses_addlstatus_avail_t;
+
+/**
+ * \brief Check to see whether a given type (as obtained via type headers) is
+ * supported by the additional status command.
+ *
+ * \param enc SES softc to check.
+ * \param typidx Type index to check for.
+ *
+ * \return An enumeration indicating if additional status is mandatory,
+ * optional, or not required for this type.
+ */
+static ses_addlstatus_avail_t
+ses_typehasaddlstatus(enc_softc_t *enc, uint8_t typidx)
+{
+ enc_cache_t *enc_cache;
+ ses_cache_t *ses_cache;
+
+ enc_cache = &enc->enc_daemon_cache;
+ ses_cache = enc_cache->private;
+ switch(ses_cache->ses_types[typidx].hdr->etype_elm_type) {
+ case ELMTYP_DEVICE:
+ case ELMTYP_ARRAY_DEV:
+ case ELMTYP_SAS_EXP:
+ return (TYPE_ADDLSTATUS_MANDATORY);
+ case ELMTYP_SCSI_INI:
+ case ELMTYP_SCSI_TGT:
+ case ELMTYP_ESCC:
+ return (TYPE_ADDLSTATUS_OPTIONAL);
+ default:
+ /* No additional status information available. */
+ break;
+ }
+ return (TYPE_ADDLSTATUS_NONE);
+}
+
+static int ses_get_elm_addlstatus_fc(enc_softc_t *, enc_cache_t *,
+ uint8_t *, int);
+static int ses_get_elm_addlstatus_sas(enc_softc_t *, enc_cache_t *, uint8_t *,
+ int, int, int, int);
+
+/**
+ * \brief Parse the additional status element data for each object.
+ *
+ * \param enc The SES softc to update.
+ * \param buf The buffer containing the additional status
+ * element response.
+ * \param xfer_len Size of the buffer.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ struct ses_iterator iter, titer;
+ int eip;
+ int err;
+ int ignore_index = 0;
+ int length;
+ int offset;
+ enc_cache_t *enc_cache;
+ ses_cache_t *ses_cache;
+ uint8_t *buf;
+ ses_element_t *elmpriv;
+ const struct ses_page_hdr *hdr;
+ enc_element_t *element, *telement;
+
+ enc_cache = &enc->enc_daemon_cache;
+ ses_cache = enc_cache->private;
+ buf = *bufp;
+ err = -1;
+
+ if (error != 0) {
+ err = error;
+ goto out;
+ }
+ ses_cache_free_elm_addlstatus(enc, enc_cache);
+ ses_cache->elm_addlstatus_page =
+ (struct ses_addl_elem_status_page *)buf;
+ *bufp = NULL;
+
+ /*
+ * The objects appear in the same order here as in Enclosure Status,
+ * which itself is ordered by the Type Descriptors from the Config
+ * page. However, it is necessary to skip elements that are not
+ * supported by this page when counting them.
+ */
+ hdr = &ses_cache->elm_addlstatus_page->hdr;
+ length = ses_page_length(hdr);
+ ENC_DLOG(enc, "Additional Element Status Page Length 0x%x\n", length);
+ /* Make sure the length includes at least one header. */
+ if (length < sizeof(*hdr)+sizeof(struct ses_elm_addlstatus_base_hdr)) {
+ ENC_LOG(enc, "Runt Additional Element Status Page\n");
+ goto out;
+ }
+ if (length > xfer_len) {
+ ENC_LOG(enc, "Additional Element Status Page Too Long\n");
+ goto out;
+ }
+
+ if (!ses_config_cache_valid(ses_cache, hdr->gen_code)) {
+ ENC_DLOG(enc, "%s: Generation count change detected\n",
+ __func__);
+ enc_update_request(enc, SES_UPDATE_GETCONFIG);
+ goto out;
+ }
+
+ offset = sizeof(struct ses_page_hdr);
+ ses_iter_init(enc, enc_cache, &iter);
+ while (offset < length
+ && (element = ses_iter_next(&iter)) != NULL) {
+ struct ses_elm_addlstatus_base_hdr *elm_hdr;
+ int proto_info_len;
+ ses_addlstatus_avail_t status_type;
+
+ /*
+ * Additional element status is only provided for
+ * individual elements (i.e. overal status elements
+ * are excluded) and those of the types specified
+ * in the SES spec.
+ */
+ status_type = ses_typehasaddlstatus(enc, iter.type_index);
+ if (iter.individual_element_index == ITERATOR_INDEX_INVALID
+ || status_type == TYPE_ADDLSTATUS_NONE)
+ continue;
+
+ elm_hdr = (struct ses_elm_addlstatus_base_hdr *)&buf[offset];
+ eip = ses_elm_addlstatus_eip(elm_hdr);
+ if (eip && !ignore_index) {
+ struct ses_elm_addlstatus_eip_hdr *eip_hdr;
+ int expected_index;
+
+ eip_hdr = (struct ses_elm_addlstatus_eip_hdr *)elm_hdr;
+ expected_index = iter.individual_element_index;
+ titer = iter;
+ telement = ses_iter_seek_to(&titer,
+ eip_hdr->element_index,
+ SES_ELEM_INDEX_INDIVIDUAL);
+ if (telement != NULL &&
+ (ses_typehasaddlstatus(enc, titer.type_index) !=
+ TYPE_ADDLSTATUS_NONE ||
+ titer.type_index > ELMTYP_SAS_CONN)) {
+ iter = titer;
+ element = telement;
+ } else
+ ignore_index = 1;
+
+ if (iter.individual_element_index > expected_index
+ && status_type == TYPE_ADDLSTATUS_MANDATORY) {
+ ENC_LOG(enc, "%s: provided element "
+ "index %d skips mandatory status "
+ " element at index %d\n",
+ __func__, eip_hdr->element_index,
+ expected_index);
+ }
+ }
+ elmpriv = element->elm_private;
+ elmpriv->addl.hdr = elm_hdr;
+ ENC_DLOG(enc, "%s: global element index=%d, type index=%d "
+ "type element index=%d, offset=0x%x, "
+ "byte0=0x%x, length=0x%x\n", __func__,
+ iter.global_element_index, iter.type_index,
+ iter.type_element_index, offset, elmpriv->addl.hdr->byte0,
+ elmpriv->addl.hdr->length);
+
+ /* Skip to after the length field */
+ offset += sizeof(struct ses_elm_addlstatus_base_hdr);
+
+ /* Make sure the descriptor is within bounds */
+ if ((offset + elmpriv->addl.hdr->length) > length) {
+ ENC_LOG(enc, "Element %d Beyond End "
+ "of Additional Element Status Descriptors\n",
+ iter.global_element_index);
+ err = EIO;
+ goto out;
+ }
+
+ /* Advance to the protocol data, skipping eip bytes if needed */
+ offset += (eip * SES_EIP_HDR_EXTRA_LEN);
+ proto_info_len = elmpriv->addl.hdr->length
+ - (eip * SES_EIP_HDR_EXTRA_LEN);
+
+ /* Errors in this block are ignored as they are non-fatal */
+ switch(ses_elm_addlstatus_proto(elmpriv->addl.hdr)) {
+ case SPSP_PROTO_FC:
+ if (elmpriv->addl.hdr->length == 0)
+ break;
+ ses_get_elm_addlstatus_fc(enc, enc_cache,
+ &buf[offset], proto_info_len);
+ break;
+ case SPSP_PROTO_SAS:
+ if (elmpriv->addl.hdr->length <= 2)
+ break;
+ ses_get_elm_addlstatus_sas(enc, enc_cache,
+ &buf[offset],
+ proto_info_len,
+ eip, iter.type_index,
+ iter.global_element_index);
+ break;
+ default:
+ ENC_LOG(enc, "Element %d: Unknown Additional Element "
+ "Protocol 0x%x\n", iter.global_element_index,
+ ses_elm_addlstatus_proto(elmpriv->addl.hdr));
+ goto out;
+ }
+
+ offset += proto_info_len;
+ }
+ err = 0;
+out:
+ if (err)
+ ses_cache_free_elm_addlstatus(enc, enc_cache);
+ enc_update_request(enc, SES_PUBLISH_PHYSPATHS);
+ enc_update_request(enc, SES_PUBLISH_CACHE);
+ return (err);
+}
+
+static int
+ses_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ ses_softc_t *ses;
+
+ ses = enc->enc_private;
+ /*
+ * Possible errors:
+ * o Generation count wrong.
+ * o Some SCSI status error.
+ */
+ ses_terminate_control_requests(&ses->ses_pending_requests, error);
+ enc_update_request(enc, SES_UPDATE_GETSTATUS);
+ return (0);
+}
+
+static int
+ses_publish_physpaths(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ struct ses_iterator iter;
+ enc_cache_t *enc_cache;
+ ses_cache_t *ses_cache;
+ enc_element_t *element;
+
+ enc_cache = &enc->enc_daemon_cache;
+ ses_cache = enc_cache->private;
+
+ ses_iter_init(enc, enc_cache, &iter);
+ while ((element = ses_iter_next(&iter)) != NULL) {
+ /*
+ * ses_set_physpath() returns success if we changed
+ * the physpath of any element. This allows us to
+ * only announce devices once regardless of how
+ * many times we process additional element status.
+ */
+ if (ses_set_physpath(enc, element, &iter) == 0)
+ ses_print_addl_data(enc, element);
+ }
+
+ return (0);
+}
+
+static int
+ses_publish_cache(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+
+ sx_xlock(&enc->enc_cache_lock);
+ ses_cache_clone(enc, /*src*/&enc->enc_daemon_cache,
+ /*dst*/&enc->enc_cache);
+ sx_xunlock(&enc->enc_cache_lock);
+
+ return (0);
+}
+
+/**
+ * \brief Parse the descriptors for each object.
+ *
+ * \param enc The SES softc to update.
+ * \param buf The buffer containing the descriptor list response.
+ * \param xfer_len Size of the buffer.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
+{
+ ses_softc_t *ses;
+ struct ses_iterator iter;
+ enc_element_t *element;
+ int err;
+ int offset;
+ u_long length, plength;
+ enc_cache_t *enc_cache;
+ ses_cache_t *ses_cache;
+ uint8_t *buf;
+ ses_element_t *elmpriv;
+ const struct ses_page_hdr *phdr;
+ const struct ses_elm_desc_hdr *hdr;
+
+ ses = enc->enc_private;
+ enc_cache = &enc->enc_daemon_cache;
+ ses_cache = enc_cache->private;
+ buf = *bufp;
+ err = -1;
+
+ if (error != 0) {
+ err = error;
+ goto out;
+ }
+ ses_cache_free_elm_descs(enc, enc_cache);
+ ses_cache->elm_descs_page = (struct ses_elem_descr_page *)buf;
+ *bufp = NULL;
+
+ phdr = &ses_cache->elm_descs_page->hdr;
+ plength = ses_page_length(phdr);
+ if (xfer_len < sizeof(struct ses_page_hdr)) {
+ ENC_LOG(enc, "Runt Element Descriptor Page\n");
+ goto out;
+ }
+ if (plength > xfer_len) {
+ ENC_LOG(enc, "Element Descriptor Page Too Long\n");
+ goto out;
+ }
+
+ if (!ses_config_cache_valid(ses_cache, phdr->gen_code)) {
+ ENC_VLOG(enc, "%s: Generation count change detected\n",
+ __func__);
+ enc_update_request(enc, SES_UPDATE_GETCONFIG);
+ goto out;
+ }
+
+ offset = sizeof(struct ses_page_hdr);
+
+ ses_iter_init(enc, enc_cache, &iter);
+ while (offset < plength
+ && (element = ses_iter_next(&iter)) != NULL) {
+
+ if ((offset + sizeof(struct ses_elm_desc_hdr)) > plength) {
+ ENC_LOG(enc, "Element %d Descriptor Header Past "
+ "End of Buffer\n", iter.global_element_index);
+ goto out;
+ }
+ hdr = (struct ses_elm_desc_hdr *)&buf[offset];
+ length = scsi_2btoul(hdr->length);
+ ENC_DLOG(enc, "%s: obj %d(%d,%d) length=%d off=%d\n", __func__,
+ iter.global_element_index, iter.type_index,
+ iter.type_element_index, length, offset);
+ if ((offset + sizeof(*hdr) + length) > plength) {
+ ENC_LOG(enc, "Element%d Descriptor Past "
+ "End of Buffer\n", iter.global_element_index);
+ goto out;
+ }
+ offset += sizeof(*hdr);
+
+ if (length > 0) {
+ elmpriv = element->elm_private;
+ elmpriv->descr_len = length;
+ elmpriv->descr = &buf[offset];
+ }
+
+ /* skip over the descriptor itself */
+ offset += length;
+ }
+
+ err = 0;
+out:
+ if (err == 0) {
+ if (ses->ses_flags & SES_FLAG_ADDLSTATUS)
+ enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS);
+ }
+ enc_update_request(enc, SES_PUBLISH_CACHE);
+ return (err);
+}
+
+static int
+ses_fill_rcv_diag_io(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t *buf)
+{
+
+ if (enc->enc_type == ENC_SEMB_SES) {
+ semb_receive_diagnostic_results(&ccb->ataio, /*retries*/5,
+ enc_done, MSG_SIMPLE_Q_TAG, /*pcv*/1,
+ state->page_code, buf, state->buf_size,
+ state->timeout);
+ } else {
+ scsi_receive_diagnostic_results(&ccb->csio, /*retries*/5,
+ enc_done, MSG_SIMPLE_Q_TAG, /*pcv*/1,
+ state->page_code, buf, state->buf_size,
+ SSD_FULL_SIZE, state->timeout);
+ }
+ return (0);
+}
+
+/**
+ * \brief Encode the object status into the response buffer, which is
+ * expected to contain the current enclosure status. This function
+ * turns off all the 'select' bits for the objects except for the
+ * object specified, then sends it back to the enclosure.
+ *
+ * \param enc SES enclosure the change is being applied to.
+ * \param buf Buffer containing the current enclosure status response.
+ * \param amt Length of the response in the buffer.
+ * \param req The control request to be applied to buf.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_encode(enc_softc_t *enc, uint8_t *buf, int amt, ses_control_request_t *req)
+{
+ struct ses_iterator iter;
+ enc_element_t *element;
+ int offset;
+ struct ses_control_page_hdr *hdr;
+
+ ses_iter_init(enc, &enc->enc_cache, &iter);
+ hdr = (struct ses_control_page_hdr *)buf;
+ if (req->elm_idx == -1) {
+ /* for enclosure status, at least 2 bytes are needed */
+ if (amt < 2)
+ return EIO;
+ hdr->control_flags =
+ req->elm_stat.comstatus & SES_SET_STATUS_MASK;
+ ENC_DLOG(enc, "Set EncStat %x\n", hdr->control_flags);
+ return (0);
+ }
+
+ element = ses_iter_seek_to(&iter, req->elm_idx, SES_ELEM_INDEX_GLOBAL);
+ if (element == NULL)
+ return (ENXIO);
+
+ /*
+ * Seek to the type set that corresponds to the requested object.
+ * The +1 is for the overall status element for the type.
+ */
+ offset = sizeof(struct ses_control_page_hdr)
+ + (iter.global_element_index * sizeof(struct ses_comstat));
+
+ /* Check for buffer overflow. */
+ if (offset + sizeof(struct ses_comstat) > amt)
+ return (EIO);
+
+ /* Set the status. */
+ memcpy(&buf[offset], &req->elm_stat, sizeof(struct ses_comstat));
+
+ ENC_DLOG(enc, "Set Type 0x%x Obj 0x%x (offset %d) with %x %x %x %x\n",
+ iter.type_index, iter.global_element_index, offset,
+ req->elm_stat.comstatus, req->elm_stat.comstat[0],
+ req->elm_stat.comstat[1], req->elm_stat.comstat[2]);
+
+ return (0);
+}
+
+static int
+ses_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state,
+ union ccb *ccb, uint8_t *buf)
+{
+ ses_softc_t *ses;
+ enc_cache_t *enc_cache;
+ ses_cache_t *ses_cache;
+ struct ses_control_page_hdr *hdr;
+ ses_control_request_t *req;
+ size_t plength;
+ size_t offset;
+
+ ses = enc->enc_private;
+ enc_cache = &enc->enc_daemon_cache;
+ ses_cache = enc_cache->private;
+ hdr = (struct ses_control_page_hdr *)buf;
+
+ if (ses_cache->status_page == NULL) {
+ ses_terminate_control_requests(&ses->ses_requests, EIO);
+ return (EIO);
+ }
+
+ plength = ses_page_length(&ses_cache->status_page->hdr);
+ memcpy(buf, ses_cache->status_page, plength);
+
+ /* Disable the select bits in all status entries. */
+ offset = sizeof(struct ses_control_page_hdr);
+ for (offset = sizeof(struct ses_control_page_hdr);
+ offset < plength; offset += sizeof(struct ses_comstat)) {
+ buf[offset] &= ~SESCTL_CSEL;
+ }
+
+ /* And make sure the INVOP bit is clear. */
+ hdr->control_flags &= ~SES_ENCSTAT_INVOP;
+
+ /* Apply incoming requests. */
+ while ((req = TAILQ_FIRST(&ses->ses_requests)) != NULL) {
+
+ TAILQ_REMOVE(&ses->ses_requests, req, links);
+ req->result = ses_encode(enc, buf, plength, req);
+ if (req->result != 0) {
+ wakeup(req);
+ continue;
+ }
+ TAILQ_INSERT_TAIL(&ses->ses_pending_requests, req, links);
+ }
+
+ if (TAILQ_EMPTY(&ses->ses_pending_requests) != 0)
+ return (ENOENT);
+
+ /* Fill out the ccb */
+ if (enc->enc_type == ENC_SEMB_SES) {
+ semb_send_diagnostic(&ccb->ataio, /*retries*/5, enc_done,
+ MSG_SIMPLE_Q_TAG,
+ buf, ses_page_length(&ses_cache->status_page->hdr),
+ state->timeout);
+ } else {
+ scsi_send_diagnostic(&ccb->csio, /*retries*/5, enc_done,
+ MSG_SIMPLE_Q_TAG, /*unit_offline*/0,
+ /*device_offline*/0, /*self_test*/0,
+ /*page_format*/1, /*self_test_code*/0,
+ buf, ses_page_length(&ses_cache->status_page->hdr),
+ SSD_FULL_SIZE, state->timeout);
+ }
+ return (0);
+}
+
+static int
+ses_get_elm_addlstatus_fc(enc_softc_t *enc, enc_cache_t *enc_cache,
+ uint8_t *buf, int bufsiz)
+{
+ ENC_LOG(enc, "FC Device Support Stubbed in Additional Status Page\n");
+ return (ENODEV);
+}
+
+#define SES_PRINT_PORTS(p, type) do { \
+ sbuf_printf(sbp, " %s(", type); \
+ if (((p) & SES_SASOBJ_DEV_PHY_PROTOMASK) == 0) \
+ sbuf_printf(sbp, " None"); \
+ else { \
+ if ((p) & SES_SASOBJ_DEV_PHY_SMP) \
+ sbuf_printf(sbp, " SMP"); \
+ if ((p) & SES_SASOBJ_DEV_PHY_STP) \
+ sbuf_printf(sbp, " STP"); \
+ if ((p) & SES_SASOBJ_DEV_PHY_SSP) \
+ sbuf_printf(sbp, " SSP"); \
+ } \
+ sbuf_printf(sbp, " )"); \
+} while(0)
+
+/**
+ * \brief Print the additional element status data for this object, for SAS
+ * type 0 objects. See SES2 r20 Section 6.1.13.3.2.
+ *
+ * \param sesname SES device name associated with the object.
+ * \param sbp Sbuf to print to.
+ * \param obj The object to print the data for.
+ * \param periph_name Peripheral string associated with the object.
+ */
+static void
+ses_print_addl_data_sas_type0(char *sesname, struct sbuf *sbp,
+ enc_element_t *obj, char *periph_name)
+{
+ int i;
+ ses_element_t *elmpriv;
+ struct ses_addl_status *addl;
+ struct ses_elm_sas_device_phy *phy;
+
+ elmpriv = obj->elm_private;
+ addl = &(elmpriv->addl);
+ if (addl->proto_hdr.sas == NULL)
+ return;
+ sbuf_printf(sbp, "%s: %s: SAS Device Slot Element:",
+ sesname, periph_name);
+ sbuf_printf(sbp, " %d Phys", addl->proto_hdr.sas->base_hdr.num_phys);
+ if (ses_elm_addlstatus_eip(addl->hdr))
+ sbuf_printf(sbp, " at Slot %d",
+ addl->proto_hdr.sas->type0_eip.dev_slot_num);
+ if (ses_elm_sas_type0_not_all_phys(addl->proto_hdr.sas))
+ sbuf_printf(sbp, ", Not All Phys");
+ sbuf_printf(sbp, "\n");
+ if (addl->proto_data.sasdev_phys == NULL)
+ return;
+ for (i = 0;i < addl->proto_hdr.sas->base_hdr.num_phys;i++) {
+ phy = &addl->proto_data.sasdev_phys[i];
+ sbuf_printf(sbp, "%s: phy %d:", sesname, i);
+ if (ses_elm_sas_dev_phy_sata_dev(phy))
+ /* Spec says all other fields are specific values */
+ sbuf_printf(sbp, " SATA device\n");
+ else {
+ sbuf_printf(sbp, " SAS device type %d id %d\n",
+ ses_elm_sas_dev_phy_dev_type(phy), phy->phy_id);
+ sbuf_printf(sbp, "%s: phy %d: protocols:", sesname, i);
+ SES_PRINT_PORTS(phy->initiator_ports, "Initiator");
+ SES_PRINT_PORTS(phy->target_ports, "Target");
+ sbuf_printf(sbp, "\n");
+ }
+ sbuf_printf(sbp, "%s: phy %d: parent %jx addr %jx\n",
+ sesname, i,
+ (uintmax_t)scsi_8btou64(phy->parent_addr),
+ (uintmax_t)scsi_8btou64(phy->phy_addr));
+ }
+}
+#undef SES_PRINT_PORTS
+
+/**
+ * \brief Report whether a given enclosure object is an expander.
+ *
+ * \param enc SES softc associated with object.
+ * \param obj Enclosure object to report for.
+ *
+ * \return 1 if true, 0 otherwise.
+ */
+static int
+ses_obj_is_expander(enc_softc_t *enc, enc_element_t *obj)
+{
+ return (obj->enctype == ELMTYP_SAS_EXP);
+}
+
+/**
+ * \brief Print the additional element status data for this object, for SAS
+ * type 1 objects. See SES2 r20 Sections 6.1.13.3.3 and 6.1.13.3.4.
+ *
+ * \param enc SES enclosure, needed for type identification.
+ * \param sesname SES device name associated with the object.
+ * \param sbp Sbuf to print to.
+ * \param obj The object to print the data for.
+ * \param periph_name Peripheral string associated with the object.
+ */
+static void
+ses_print_addl_data_sas_type1(enc_softc_t *enc, char *sesname,
+ struct sbuf *sbp, enc_element_t *obj, char *periph_name)
+{
+ int i, num_phys;
+ ses_element_t *elmpriv;
+ struct ses_addl_status *addl;
+ struct ses_elm_sas_expander_phy *exp_phy;
+ struct ses_elm_sas_port_phy *port_phy;
+
+ elmpriv = obj->elm_private;
+ addl = &(elmpriv->addl);
+ if (addl->proto_hdr.sas == NULL)
+ return;
+ sbuf_printf(sbp, "%s: %s: SAS ", sesname, periph_name);
+ if (ses_obj_is_expander(enc, obj)) {
+ num_phys = addl->proto_hdr.sas->base_hdr.num_phys;
+ sbuf_printf(sbp, "Expander: %d Phys", num_phys);
+ if (addl->proto_data.sasexp_phys == NULL)
+ return;
+ for (i = 0;i < num_phys;i++) {
+ exp_phy = &addl->proto_data.sasexp_phys[i];
+ sbuf_printf(sbp, "%s: phy %d: connector %d other %d\n",
+ sesname, i, exp_phy->connector_index,
+ exp_phy->other_index);
+ }
+ } else {
+ num_phys = addl->proto_hdr.sas->base_hdr.num_phys;
+ sbuf_printf(sbp, "Port: %d Phys", num_phys);
+ if (addl->proto_data.sasport_phys == NULL)
+ return;
+ for (i = 0;i < num_phys;i++) {
+ port_phy = &addl->proto_data.sasport_phys[i];
+ sbuf_printf(sbp,
+ "%s: phy %d: id %d connector %d other %d\n",
+ sesname, i, port_phy->phy_id,
+ port_phy->connector_index, port_phy->other_index);
+ sbuf_printf(sbp, "%s: phy %d: addr %jx\n", sesname, i,
+ (uintmax_t)scsi_8btou64(port_phy->phy_addr));
+ }
+ }
+}
+
+/**
+ * \brief Print the additional element status data for this object.
+ *
+ * \param enc SES softc associated with the object.
+ * \param obj The object to print the data for.
+ */
+static void
+ses_print_addl_data(enc_softc_t *enc, enc_element_t *obj)
+{
+ ses_element_t *elmpriv;
+ struct ses_addl_status *addl;
+ struct sbuf sesname, name, out;
+
+ elmpriv = obj->elm_private;
+ if (elmpriv == NULL)
+ return;
+
+ addl = &(elmpriv->addl);
+ if (addl->hdr == NULL)
+ return;
+
+ sbuf_new(&sesname, NULL, 16, SBUF_AUTOEXTEND);
+ sbuf_new(&name, NULL, 16, SBUF_AUTOEXTEND);
+ sbuf_new(&out, NULL, 512, SBUF_AUTOEXTEND);
+ ses_paths_iter(enc, obj, ses_elmdevname_callback, &name);
+ if (sbuf_len(&name) == 0)
+ sbuf_printf(&name, "(none)");
+ sbuf_finish(&name);
+ sbuf_printf(&sesname, "%s%d", enc->periph->periph_name,
+ enc->periph->unit_number);
+ sbuf_finish(&sesname);
+ if (elmpriv->descr != NULL)
+ sbuf_printf(&out, "%s: %s: Element descriptor: '%s'\n",
+ sbuf_data(&sesname), sbuf_data(&name), elmpriv->descr);
+ switch(ses_elm_addlstatus_proto(addl->hdr)) {
+ case SPSP_PROTO_SAS:
+ switch(ses_elm_sas_descr_type(addl->proto_hdr.sas)) {
+ case SES_SASOBJ_TYPE_SLOT:
+ ses_print_addl_data_sas_type0(sbuf_data(&sesname),
+ &out, obj, sbuf_data(&name));
+ break;
+ case SES_SASOBJ_TYPE_OTHER:
+ ses_print_addl_data_sas_type1(enc, sbuf_data(&sesname),
+ &out, obj, sbuf_data(&name));
+ break;
+ default:
+ break;
+ }
+ break;
+ case SPSP_PROTO_FC: /* stubbed for now */
+ break;
+ default:
+ break;
+ }
+ sbuf_finish(&out);
+ printf("%s", sbuf_data(&out));
+ sbuf_delete(&out);
+ sbuf_delete(&name);
+ sbuf_delete(&sesname);
+}
+
+/**
+ * \brief Update the softc with the additional element status data for this
+ * object, for SAS type 0 objects.
+ *
+ * \param enc SES softc to be updated.
+ * \param buf The additional element status response buffer.
+ * \param bufsiz Size of the response buffer.
+ * \param eip The EIP bit value.
+ * \param nobj Number of objects attached to the SES softc.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_get_elm_addlstatus_sas_type0(enc_softc_t *enc, enc_cache_t *enc_cache,
+ uint8_t *buf, int bufsiz, int eip, int nobj)
+{
+ int err, offset, physz;
+ enc_element_t *obj;
+ ses_element_t *elmpriv;
+ struct ses_addl_status *addl;
+
+ err = offset = 0;
+
+ /* basic object setup */
+ obj = &(enc_cache->elm_map[nobj]);
+ elmpriv = obj->elm_private;
+ addl = &(elmpriv->addl);
+
+ addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset];
+
+ /* Don't assume this object has any phys */
+ bzero(&addl->proto_data, sizeof(addl->proto_data));
+ if (addl->proto_hdr.sas->base_hdr.num_phys == 0)
+ goto out;
+
+ /* Skip forward to the phy list */
+ if (eip)
+ offset += sizeof(struct ses_elm_sas_type0_eip_hdr);
+ else
+ offset += sizeof(struct ses_elm_sas_type0_base_hdr);
+
+ /* Make sure the phy list fits in the buffer */
+ physz = addl->proto_hdr.sas->base_hdr.num_phys;
+ physz *= sizeof(struct ses_elm_sas_device_phy);
+ if (physz > (bufsiz - offset + 4)) {
+ ENC_LOG(enc, "Element %d Device Phy List Beyond End Of Buffer\n",
+ nobj);
+ err = EIO;
+ goto out;
+ }
+
+ /* Point to the phy list */
+ addl->proto_data.sasdev_phys =
+ (struct ses_elm_sas_device_phy *)&buf[offset];
+
+out:
+ return (err);
+}
+
+/**
+ * \brief Update the softc with the additional element status data for this
+ * object, for SAS type 1 objects.
+ *
+ * \param enc SES softc to be updated.
+ * \param buf The additional element status response buffer.
+ * \param bufsiz Size of the response buffer.
+ * \param eip The EIP bit value.
+ * \param nobj Number of objects attached to the SES softc.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_get_elm_addlstatus_sas_type1(enc_softc_t *enc, enc_cache_t *enc_cache,
+ uint8_t *buf, int bufsiz, int eip, int nobj)
+{
+ int err, offset, physz;
+ enc_element_t *obj;
+ ses_element_t *elmpriv;
+ struct ses_addl_status *addl;
+
+ err = offset = 0;
+
+ /* basic object setup */
+ obj = &(enc_cache->elm_map[nobj]);
+ elmpriv = obj->elm_private;
+ addl = &(elmpriv->addl);
+
+ addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset];
+
+ /* Don't assume this object has any phys */
+ bzero(&addl->proto_data, sizeof(addl->proto_data));
+ if (addl->proto_hdr.sas->base_hdr.num_phys == 0)
+ goto out;
+
+ /* Process expanders differently from other type1 cases */
+ if (ses_obj_is_expander(enc, obj)) {
+ offset += sizeof(struct ses_elm_sas_type1_expander_hdr);
+ physz = addl->proto_hdr.sas->base_hdr.num_phys *
+ sizeof(struct ses_elm_sas_expander_phy);
+ if (physz > (bufsiz - offset)) {
+ ENC_LOG(enc, "Element %d: Expander Phy List Beyond "
+ "End Of Buffer\n", nobj);
+ err = EIO;
+ goto out;
+ }
+ addl->proto_data.sasexp_phys =
+ (struct ses_elm_sas_expander_phy *)&buf[offset];
+ } else {
+ offset += sizeof(struct ses_elm_sas_type1_nonexpander_hdr);
+ physz = addl->proto_hdr.sas->base_hdr.num_phys *
+ sizeof(struct ses_elm_sas_port_phy);
+ if (physz > (bufsiz - offset + 4)) {
+ ENC_LOG(enc, "Element %d: Port Phy List Beyond End "
+ "Of Buffer\n", nobj);
+ err = EIO;
+ goto out;
+ }
+ addl->proto_data.sasport_phys =
+ (struct ses_elm_sas_port_phy *)&buf[offset];
+ }
+
+out:
+ return (err);
+}
+
+/**
+ * \brief Update the softc with the additional element status data for this
+ * object, for SAS objects.
+ *
+ * \param enc SES softc to be updated.
+ * \param buf The additional element status response buffer.
+ * \param bufsiz Size of the response buffer.
+ * \param eip The EIP bit value.
+ * \param tidx Type index for this object.
+ * \param nobj Number of objects attached to the SES softc.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_get_elm_addlstatus_sas(enc_softc_t *enc, enc_cache_t *enc_cache,
+ uint8_t *buf, int bufsiz, int eip, int tidx,
+ int nobj)
+{
+ int dtype, err;
+ ses_cache_t *ses_cache;
+ union ses_elm_sas_hdr *hdr;
+
+ /* Need to be able to read the descriptor type! */
+ if (bufsiz < sizeof(union ses_elm_sas_hdr)) {
+ err = EIO;
+ goto out;
+ }
+
+ ses_cache = enc_cache->private;
+
+ hdr = (union ses_elm_sas_hdr *)buf;
+ dtype = ses_elm_sas_descr_type(hdr);
+ switch(dtype) {
+ case SES_SASOBJ_TYPE_SLOT:
+ switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) {
+ case ELMTYP_DEVICE:
+ case ELMTYP_ARRAY_DEV:
+ break;
+ default:
+ ENC_LOG(enc, "Element %d has Additional Status type 0, "
+ "invalid for SES element type 0x%x\n", nobj,
+ ses_cache->ses_types[tidx].hdr->etype_elm_type);
+ err = ENODEV;
+ goto out;
+ }
+ err = ses_get_elm_addlstatus_sas_type0(enc, enc_cache,
+ buf, bufsiz, eip,
+ nobj);
+ break;
+ case SES_SASOBJ_TYPE_OTHER:
+ switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) {
+ case ELMTYP_SAS_EXP:
+ case ELMTYP_SCSI_INI:
+ case ELMTYP_SCSI_TGT:
+ case ELMTYP_ESCC:
+ break;
+ default:
+ ENC_LOG(enc, "Element %d has Additional Status type 1, "
+ "invalid for SES element type 0x%x\n", nobj,
+ ses_cache->ses_types[tidx].hdr->etype_elm_type);
+ err = ENODEV;
+ goto out;
+ }
+ err = ses_get_elm_addlstatus_sas_type1(enc, enc_cache, buf,
+ bufsiz, eip, nobj);
+ break;
+ default:
+ ENC_LOG(enc, "Element %d of type 0x%x has Additional Status "
+ "of unknown type 0x%x\n", nobj,
+ ses_cache->ses_types[tidx].hdr->etype_elm_type, dtype);
+ err = ENODEV;
+ break;
+ }
+
+out:
+ return (err);
+}
+
+static void
+ses_softc_invalidate(enc_softc_t *enc)
+{
+ ses_softc_t *ses;
+
+ ses = enc->enc_private;
+ ses_terminate_control_requests(&ses->ses_requests, ENXIO);
+}
+
+static void
+ses_softc_cleanup(enc_softc_t *enc)
+{
+
+ ses_cache_free(enc, &enc->enc_cache);
+ ses_cache_free(enc, &enc->enc_daemon_cache);
+ ENC_FREE_AND_NULL(enc->enc_private);
+ ENC_FREE_AND_NULL(enc->enc_cache.private);
+ ENC_FREE_AND_NULL(enc->enc_daemon_cache.private);
+}
+
+static int
+ses_init_enc(enc_softc_t *enc)
+{
+ return (0);
+}
+
+static int
+ses_get_enc_status(enc_softc_t *enc, int slpflag)
+{
+ /* Automatically updated, caller checks enc_cache->encstat itself */
+ return (0);
+}
+
+static int
+ses_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag)
+{
+ ses_control_request_t req;
+ ses_softc_t *ses;
+
+ ses = enc->enc_private;
+ req.elm_idx = SES_SETSTATUS_ENC_IDX;
+ req.elm_stat.comstatus = encstat & 0xf;
+
+ TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links);
+ enc_update_request(enc, SES_PROCESS_CONTROL_REQS);
+ cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0);
+
+ return (req.result);
+}
+
+static int
+ses_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag)
+{
+ unsigned int i = elms->elm_idx;
+
+ memcpy(elms->cstat, &enc->enc_cache.elm_map[i].encstat, 4);
+ return (0);
+}
+
+static int
+ses_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag)
+{
+ ses_control_request_t req;
+ ses_softc_t *ses;
+
+ /* If this is clear, we don't do diddly. */
+ if ((elms->cstat[0] & SESCTL_CSEL) == 0)
+ return (0);
+
+ ses = enc->enc_private;
+ req.elm_idx = elms->elm_idx;
+ memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat));
+
+ TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links);
+ enc_update_request(enc, SES_PROCESS_CONTROL_REQS);
+ cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0);
+
+ return (req.result);
+}
+
+static int
+ses_get_elm_desc(enc_softc_t *enc, encioc_elm_desc_t *elmd)
+{
+ int i = (int)elmd->elm_idx;
+ ses_element_t *elmpriv;
+
+ /* Assume caller has already checked obj_id validity */
+ elmpriv = enc->enc_cache.elm_map[i].elm_private;
+ /* object might not have a descriptor */
+ if (elmpriv == NULL || elmpriv->descr == NULL) {
+ elmd->elm_desc_len = 0;
+ return (0);
+ }
+ if (elmd->elm_desc_len > elmpriv->descr_len)
+ elmd->elm_desc_len = elmpriv->descr_len;
+ copyout(elmpriv->descr, elmd->elm_desc_str, elmd->elm_desc_len);
+ return (0);
+}
+
+/**
+ * \brief Respond to ENCIOC_GETELMDEVNAME, providing a device name for the
+ * given object id if one is available.
+ *
+ * \param enc SES softc to examine.
+ * \param objdn ioctl structure to read/write device name info.
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_get_elm_devnames(enc_softc_t *enc, encioc_elm_devnames_t *elmdn)
+{
+ struct sbuf sb;
+ int len;
+
+ len = elmdn->elm_names_size;
+ if (len < 0)
+ return (EINVAL);
+
+ sbuf_new(&sb, elmdn->elm_devnames, len, 0);
+
+ cam_periph_unlock(enc->periph);
+ ses_paths_iter(enc, &enc->enc_cache.elm_map[elmdn->elm_idx],
+ ses_elmdevname_callback, &sb);
+ sbuf_finish(&sb);
+ elmdn->elm_names_len = sbuf_len(&sb);
+ cam_periph_lock(enc->periph);
+ return (elmdn->elm_names_len > 0 ? 0 : ENODEV);
+}
+
+/**
+ * \brief Send a string to the primary subenclosure using the String Out
+ * SES diagnostic page.
+ *
+ * \param enc SES enclosure to run the command on.
+ * \param sstr SES string structure to operate on
+ * \param ioc Ioctl being performed
+ *
+ * \return 0 on success, errno otherwise.
+ */
+static int
+ses_handle_string(enc_softc_t *enc, encioc_string_t *sstr, int ioc)
+{
+ int amt, payload, ret;
+ char cdb[6];
+ uint8_t *buf;
+
+ /* Implement SES2r20 6.1.6 */
+ if (sstr->bufsiz > 0xffff)
+ return (EINVAL); /* buffer size too large */
+
+ if (ioc == ENCIOC_SETSTRING) {
+ payload = sstr->bufsiz + 4; /* header for SEND DIAGNOSTIC */
+ amt = 0 - payload;
+ buf = ENC_MALLOC(payload);
+ if (buf == NULL)
+ return ENOMEM;
+
+ ses_page_cdb(cdb, payload, 0, CAM_DIR_OUT);
+ /* Construct the page request */
+ buf[0] = SesStringOut;
+ buf[1] = 0;
+ buf[2] = sstr->bufsiz >> 8;
+ buf[3] = sstr->bufsiz & 0xff;
+ memcpy(&buf[4], sstr->buf, sstr->bufsiz);
+ } else if (ioc == ENCIOC_GETSTRING) {
+ payload = sstr->bufsiz;
+ amt = payload;
+ ses_page_cdb(cdb, payload, SesStringIn, CAM_DIR_IN);
+ buf = sstr->buf;
+ } else
+ return EINVAL;
+
+ ret = enc_runcmd(enc, cdb, 6, buf, &amt);
+ if (ioc == ENCIOC_SETSTRING)
+ ENC_FREE(buf);
+ return ret;
+}
+
+/**
+ * \invariant Called with cam_periph mutex held.
+ */
+static void
+ses_poll_status(enc_softc_t *enc)
+{
+ ses_softc_t *ses;
+
+ ses = enc->enc_private;
+ enc_update_request(enc, SES_UPDATE_GETSTATUS);
+ if (ses->ses_flags & SES_FLAG_ADDLSTATUS)
+ enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS);
+}
+
+/**
+ * \brief Notification received when CAM detects a new device in the
+ * SCSI domain in which this SEP resides.
+ *
+ * \param enc SES enclosure instance.
+ */
+static void
+ses_device_found(enc_softc_t *enc)
+{
+ ses_poll_status(enc);
+ enc_update_request(enc, SES_PUBLISH_PHYSPATHS);
+}
+
+static struct enc_vec ses_enc_vec =
+{
+ .softc_invalidate = ses_softc_invalidate,
+ .softc_cleanup = ses_softc_cleanup,
+ .init_enc = ses_init_enc,
+ .get_enc_status = ses_get_enc_status,
+ .set_enc_status = ses_set_enc_status,
+ .get_elm_status = ses_get_elm_status,
+ .set_elm_status = ses_set_elm_status,
+ .get_elm_desc = ses_get_elm_desc,
+ .get_elm_devnames = ses_get_elm_devnames,
+ .handle_string = ses_handle_string,
+ .device_found = ses_device_found,
+ .poll_status = ses_poll_status
+};
+
+/**
+ * \brief Initialize a new SES instance.
+ *
+ * \param enc SES softc structure to set up the instance in.
+ * \param doinit Do the initialization (see main driver).
+ *
+ * \return 0 on success, errno otherwise.
+ */
+int
+ses_softc_init(enc_softc_t *enc)
+{
+ ses_softc_t *ses_softc;
+
+ CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
+ ("entering enc_softc_init(%p)\n", enc));
+
+ enc->enc_vec = ses_enc_vec;
+ enc->enc_fsm_states = enc_fsm_states;
+
+ if (enc->enc_private == NULL)
+ enc->enc_private = ENC_MALLOCZ(sizeof(ses_softc_t));
+ if (enc->enc_cache.private == NULL)
+ enc->enc_cache.private = ENC_MALLOCZ(sizeof(ses_cache_t));
+ if (enc->enc_daemon_cache.private == NULL)
+ enc->enc_daemon_cache.private =
+ ENC_MALLOCZ(sizeof(ses_cache_t));
+
+ if (enc->enc_private == NULL
+ || enc->enc_cache.private == NULL
+ || enc->enc_daemon_cache.private == NULL) {
+ ENC_FREE_AND_NULL(enc->enc_private);
+ ENC_FREE_AND_NULL(enc->enc_cache.private);
+ ENC_FREE_AND_NULL(enc->enc_daemon_cache.private);
+ return (ENOMEM);
+ }
+
+ ses_softc = enc->enc_private;
+ TAILQ_INIT(&ses_softc->ses_requests);
+ TAILQ_INIT(&ses_softc->ses_pending_requests);
+
+ enc_update_request(enc, SES_UPDATE_PAGES);
+
+ // XXX: Move this to the FSM so it doesn't hang init
+ if (0) (void) ses_set_timed_completion(enc, 1);
+
+ return (0);
+}
+
diff --git a/sys/cam/scsi/scsi_ses.c b/sys/cam/scsi/scsi_ses.c
deleted file mode 100644
index 586e996..0000000
--- a/sys/cam/scsi/scsi_ses.c
+++ /dev/null
@@ -1,2533 +0,0 @@
-/*-
- * Copyright (c) 2000 Matthew Jacob
- * 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,
- * without modification, immediately at the beginning of the file.
- * 2. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/queue.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/types.h>
-#include <sys/malloc.h>
-#include <sys/fcntl.h>
-#include <sys/conf.h>
-#include <sys/errno.h>
-#include <machine/stdarg.h>
-
-#include <cam/cam.h>
-#include <cam/cam_ccb.h>
-#include <cam/cam_periph.h>
-#include <cam/cam_xpt_periph.h>
-#include <cam/cam_debug.h>
-#include <cam/cam_sim.h>
-
-#include <cam/scsi/scsi_all.h>
-#include <cam/scsi/scsi_message.h>
-#include <sys/ioccom.h>
-#include <cam/scsi/scsi_ses.h>
-
-#include <opt_ses.h>
-
-static MALLOC_DEFINE(M_SCSISES, "SCSI SES", "SCSI SES buffers");
-
-/*
- * Platform Independent Driver Internal Definitions for SES devices.
- */
-typedef enum {
- SES_NONE,
- SES_SES_SCSI2,
- SES_SES,
- SES_SES_PASSTHROUGH,
- SES_SEN,
- SES_SAFT
-} enctyp;
-
-struct ses_softc;
-typedef struct ses_softc ses_softc_t;
-typedef struct {
- int (*softc_init)(ses_softc_t *, int);
- int (*init_enc)(ses_softc_t *);
- int (*get_encstat)(ses_softc_t *, int);
- int (*set_encstat)(ses_softc_t *, ses_encstat, int);
- int (*get_objstat)(ses_softc_t *, ses_objstat *, int);
- int (*set_objstat)(ses_softc_t *, ses_objstat *, int);
-} encvec;
-
-#define ENCI_SVALID 0x80
-
-typedef struct {
- uint32_t
- enctype : 8, /* enclosure type */
- subenclosure : 8, /* subenclosure id */
- svalid : 1, /* enclosure information valid */
- priv : 15; /* private data, per object */
- uint8_t encstat[4]; /* state && stats */
-} encobj;
-
-#define SEN_ID "UNISYS SUN_SEN"
-#define SEN_ID_LEN 24
-
-
-static enctyp ses_type(void *, int);
-
-
-/* Forward reference to Enclosure Functions */
-static int ses_softc_init(ses_softc_t *, int);
-static int ses_init_enc(ses_softc_t *);
-static int ses_get_encstat(ses_softc_t *, int);
-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
- */
-
-#define STRNCMP strncmp
-#define PRINTF printf
-#define SES_LOG ses_log
-#ifdef DEBUG
-#define SES_DLOG ses_log
-#else
-#define SES_DLOG if (0) ses_log
-#endif
-#define SES_VLOG if (bootverbose) ses_log
-#define SES_MALLOC(amt) malloc(amt, M_SCSISES, M_NOWAIT)
-#define SES_FREE(ptr, amt) free(ptr, M_SCSISES)
-#define MEMZERO bzero
-#define MEMCPY(dest, src, amt) bcopy(src, dest, amt)
-
-static int ses_runcmd(struct ses_softc *, char *, int, char *, int *);
-static void ses_log(struct ses_softc *, const char *, ...);
-
-/*
- * Gerenal FreeBSD kernel stuff.
- */
-
-
-#define ccb_state ppriv_field0
-#define ccb_bp ppriv_ptr1
-
-struct ses_softc {
- enctyp ses_type; /* type of enclosure */
- encvec ses_vec; /* vector to handlers */
- void * ses_private; /* per-type private data */
- encobj * ses_objmap; /* objects */
- uint32_t ses_nobjects; /* number of objects */
- ses_encstat ses_encstat; /* overall status */
- uint8_t ses_flags;
- union ccb ses_saved_ccb;
- struct cdev *ses_dev;
- struct cam_periph *periph;
-};
-#define SES_FLAG_INVALID 0x01
-#define SES_FLAG_OPEN 0x02
-#define SES_FLAG_INITIALIZED 0x04
-
-static d_open_t sesopen;
-static d_close_t sesclose;
-static d_ioctl_t sesioctl;
-static periph_init_t sesinit;
-static periph_ctor_t sesregister;
-static periph_oninv_t sesoninvalidate;
-static periph_dtor_t sescleanup;
-static periph_start_t sesstart;
-
-static void sesasync(void *, uint32_t, struct cam_path *, void *);
-static void sesdone(struct cam_periph *, union ccb *);
-static int seserror(union ccb *, uint32_t, uint32_t);
-
-static struct periph_driver sesdriver = {
- sesinit, "ses",
- TAILQ_HEAD_INITIALIZER(sesdriver.units), /* generation */ 0
-};
-
-PERIPHDRIVER_DECLARE(ses, sesdriver);
-
-static struct cdevsw ses_cdevsw = {
- .d_version = D_VERSION,
- .d_open = sesopen,
- .d_close = sesclose,
- .d_ioctl = sesioctl,
- .d_name = "ses",
- .d_flags = 0,
-};
-
-static void
-sesinit(void)
-{
- cam_status status;
-
- /*
- * Install a global async callback. This callback will
- * receive async callbacks like "new device found".
- */
- status = xpt_register_async(AC_FOUND_DEVICE, sesasync, NULL, NULL);
-
- if (status != CAM_REQ_CMP) {
- printf("ses: Failed to attach master async callback "
- "due to status 0x%x!\n", status);
- }
-}
-
-static void
-sesoninvalidate(struct cam_periph *periph)
-{
- struct ses_softc *softc;
-
- softc = (struct ses_softc *)periph->softc;
-
- /*
- * Unregister any async callbacks.
- */
- xpt_register_async(0, sesasync, periph, periph->path);
-
- softc->ses_flags |= SES_FLAG_INVALID;
-
- xpt_print(periph->path, "lost device\n");
-}
-
-static void
-sescleanup(struct cam_periph *periph)
-{
- struct ses_softc *softc;
-
- softc = (struct ses_softc *)periph->softc;
-
- xpt_print(periph->path, "removing device entry\n");
- cam_periph_unlock(periph);
- destroy_dev(softc->ses_dev);
- cam_periph_lock(periph);
- free(softc, M_SCSISES);
-}
-
-static void
-sesasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
-{
- struct cam_periph *periph;
-
- periph = (struct cam_periph *)callback_arg;
-
- switch(code) {
- case AC_FOUND_DEVICE:
- {
- cam_status status;
- struct ccb_getdev *cgd;
- int inq_len;
-
- cgd = (struct ccb_getdev *)arg;
- if (arg == NULL) {
- break;
- }
-
- if (cgd->protocol != PROTO_SCSI)
- break;
-
- inq_len = cgd->inq_data.additional_length + 4;
-
- /*
- * 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, inq_len)) {
- case SES_SES:
- case SES_SES_SCSI2:
- case SES_SES_PASSTHROUGH:
- case SES_SEN:
- case SES_SAFT:
- break;
- default:
- return;
- }
-
- status = cam_periph_alloc(sesregister, sesoninvalidate,
- sescleanup, sesstart, "ses", CAM_PERIPH_BIO,
- cgd->ccb_h.path, sesasync, AC_FOUND_DEVICE, cgd);
-
- if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) {
- printf("sesasync: Unable to probe new device due to "
- "status 0x%x\n", status);
- }
- break;
- }
- default:
- cam_periph_async(periph, code, path, arg);
- break;
- }
-}
-
-static cam_status
-sesregister(struct cam_periph *periph, void *arg)
-{
- struct ses_softc *softc;
- struct ccb_getdev *cgd;
- char *tname;
-
- cgd = (struct ccb_getdev *)arg;
- if (periph == NULL) {
- printf("sesregister: periph was NULL!!\n");
- return (CAM_REQ_CMP_ERR);
- }
-
- if (cgd == NULL) {
- printf("sesregister: no getdev CCB, can't register device\n");
- return (CAM_REQ_CMP_ERR);
- }
-
- softc = SES_MALLOC(sizeof (struct ses_softc));
- if (softc == NULL) {
- printf("sesregister: Unable to probe new device. "
- "Unable to allocate softc\n");
- return (CAM_REQ_CMP_ERR);
- }
- bzero(softc, sizeof (struct ses_softc));
- periph->softc = softc;
- softc->periph = periph;
-
- softc->ses_type = ses_type(&cgd->inq_data, sizeof (cgd->inq_data));
-
- switch (softc->ses_type) {
- case SES_SES:
- case SES_SES_SCSI2:
- case SES_SES_PASSTHROUGH:
- softc->ses_vec.softc_init = ses_softc_init;
- softc->ses_vec.init_enc = ses_init_enc;
- softc->ses_vec.get_encstat = ses_get_encstat;
- softc->ses_vec.set_encstat = ses_set_encstat;
- softc->ses_vec.get_objstat = ses_get_objstat;
- softc->ses_vec.set_objstat = ses_set_objstat;
- break;
- 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:
- free(softc, M_SCSISES);
- return (CAM_REQ_CMP_ERR);
- }
-
- cam_periph_unlock(periph);
- softc->ses_dev = make_dev(&ses_cdevsw, periph->unit_number,
- UID_ROOT, GID_OPERATOR, 0600, "%s%d",
- periph->periph_name, periph->unit_number);
- cam_periph_lock(periph);
- softc->ses_dev->si_drv1 = periph;
-
- /*
- * Add an async callback so that we get
- * notified if this device goes away.
- */
- xpt_register_async(AC_LOST_DEVICE, sesasync, periph, periph->path);
-
- switch (softc->ses_type) {
- default:
- case SES_NONE:
- tname = "No SES device";
- break;
- case SES_SES_SCSI2:
- tname = "SCSI-2 SES Device";
- break;
- case SES_SES:
- tname = "SCSI-3 SES Device";
- break;
- case SES_SES_PASSTHROUGH:
- tname = "SES Passthrough Device";
- break;
- case SES_SEN:
- tname = "UNISYS SEN Device (NOT HANDLED YET)";
- break;
- case SES_SAFT:
- tname = "SAF-TE Compliant Device";
- break;
- }
- xpt_announce_periph(periph, tname);
- return (CAM_REQ_CMP);
-}
-
-static int
-sesopen(struct cdev *dev, int flags, int fmt, struct thread *td)
-{
- struct cam_periph *periph;
- struct ses_softc *softc;
- int error = 0;
-
- periph = (struct cam_periph *)dev->si_drv1;
- if (periph == NULL) {
- return (ENXIO);
- }
-
- if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
- cam_periph_unlock(periph);
- return (ENXIO);
- }
-
- cam_periph_lock(periph);
-
- softc = (struct ses_softc *)periph->softc;
-
- if (softc->ses_flags & SES_FLAG_INVALID) {
- error = ENXIO;
- goto out;
- }
- if (softc->ses_flags & SES_FLAG_OPEN) {
- error = EBUSY;
- goto out;
- }
- if (softc->ses_vec.softc_init == NULL) {
- error = ENXIO;
- goto out;
- }
-
- softc->ses_flags |= SES_FLAG_OPEN;
- if ((softc->ses_flags & SES_FLAG_INITIALIZED) == 0) {
- error = (*softc->ses_vec.softc_init)(softc, 1);
- if (error)
- softc->ses_flags &= ~SES_FLAG_OPEN;
- else
- softc->ses_flags |= SES_FLAG_INITIALIZED;
- }
-
-out:
- cam_periph_unlock(periph);
- if (error) {
- cam_periph_release(periph);
- }
- return (error);
-}
-
-static int
-sesclose(struct cdev *dev, int flag, int fmt, struct thread *td)
-{
- struct cam_periph *periph;
- struct ses_softc *softc;
- int error;
-
- error = 0;
-
- periph = (struct cam_periph *)dev->si_drv1;
- if (periph == NULL)
- return (ENXIO);
-
- cam_periph_lock(periph);
-
- softc = (struct ses_softc *)periph->softc;
- softc->ses_flags &= ~SES_FLAG_OPEN;
-
- cam_periph_unlock(periph);
- cam_periph_release(periph);
-
- return (0);
-}
-
-static void
-sesstart(struct cam_periph *p, union ccb *sccb)
-{
- if (p->immediate_priority <= p->pinfo.priority) {
- SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle);
- p->immediate_priority = CAM_PRIORITY_NONE;
- wakeup(&p->ccb_list);
- }
-}
-
-static void
-sesdone(struct cam_periph *periph, union ccb *dccb)
-{
- wakeup(&dccb->ccb_h.cbfcnp);
-}
-
-static int
-seserror(union ccb *ccb, uint32_t cflags, uint32_t sflags)
-{
- struct ses_softc *softc;
- struct cam_periph *periph;
-
- periph = xpt_path_periph(ccb->ccb_h.path);
- softc = (struct ses_softc *)periph->softc;
-
- return (cam_periph_error(ccb, cflags, sflags, &softc->ses_saved_ccb));
-}
-
-static int
-sesioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, struct thread *td)
-{
- struct cam_periph *periph;
- ses_encstat tmp;
- ses_objstat objs;
- ses_object *uobj;
- struct ses_softc *ssc;
- void *addr;
- int error, i;
-
-
- if (arg_addr)
- addr = *((caddr_t *) arg_addr);
- else
- addr = NULL;
-
- periph = (struct cam_periph *)dev->si_drv1;
- if (periph == NULL)
- return (ENXIO);
-
- CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering sesioctl\n"));
-
- cam_periph_lock(periph);
- ssc = (struct ses_softc *)periph->softc;
-
- /*
- * Now check to see whether we're initialized or not.
- * This actually should never fail as we're not supposed
- * to get past ses_open w/o successfully initializing
- * things.
- */
- if ((ssc->ses_flags & SES_FLAG_INITIALIZED) == 0) {
- cam_periph_unlock(periph);
- return (ENXIO);
- }
- cam_periph_unlock(periph);
-
- error = 0;
-
- CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
- ("trying to do ioctl %#lx\n", cmd));
-
- /*
- * If this command can change the device's state,
- * we must have the device open for writing.
- *
- * For commands that get information about the
- * device- we don't need to lock the peripheral
- * if we aren't running a command. The number
- * of objects and the contents will stay stable
- * after the first open that does initialization.
- * The periph also can't go away while a user
- * process has it open.
- */
- switch (cmd) {
- case SESIOC_GETNOBJ:
- case SESIOC_GETOBJMAP:
- case SESIOC_GETENCSTAT:
- case SESIOC_GETOBJSTAT:
- break;
- default:
- if ((flag & FWRITE) == 0) {
- return (EBADF);
- }
- }
-
- switch (cmd) {
- case SESIOC_GETNOBJ:
- error = copyout(&ssc->ses_nobjects, addr,
- sizeof (ssc->ses_nobjects));
- break;
-
- case SESIOC_GETOBJMAP:
- for (uobj = addr, i = 0; i != ssc->ses_nobjects; i++) {
- ses_object kobj;
- kobj.obj_id = i;
- kobj.subencid = ssc->ses_objmap[i].subenclosure;
- kobj.object_type = ssc->ses_objmap[i].enctype;
- error = copyout(&kobj, &uobj[i], sizeof (ses_object));
- if (error) {
- break;
- }
- }
- break;
-
- case SESIOC_GETENCSTAT:
- cam_periph_lock(periph);
- error = (*ssc->ses_vec.get_encstat)(ssc, 1);
- if (error) {
- cam_periph_unlock(periph);
- break;
- }
- tmp = ssc->ses_encstat & ~ENCI_SVALID;
- cam_periph_unlock(periph);
- error = copyout(&tmp, addr, sizeof (ses_encstat));
- ssc->ses_encstat = tmp;
- break;
-
- case SESIOC_SETENCSTAT:
- error = copyin(addr, &tmp, sizeof (ses_encstat));
- if (error)
- break;
- cam_periph_lock(periph);
- error = (*ssc->ses_vec.set_encstat)(ssc, tmp, 1);
- cam_periph_unlock(periph);
- break;
-
- case SESIOC_GETOBJSTAT:
- error = copyin(addr, &objs, sizeof (ses_objstat));
- if (error)
- break;
- if (objs.obj_id >= ssc->ses_nobjects) {
- error = EINVAL;
- break;
- }
- cam_periph_lock(periph);
- error = (*ssc->ses_vec.get_objstat)(ssc, &objs, 1);
- cam_periph_unlock(periph);
- if (error)
- break;
- error = copyout(&objs, addr, sizeof (ses_objstat));
- /*
- * Always (for now) invalidate entry.
- */
- ssc->ses_objmap[objs.obj_id].svalid = 0;
- break;
-
- case SESIOC_SETOBJSTAT:
- error = copyin(addr, &objs, sizeof (ses_objstat));
- if (error)
- break;
-
- if (objs.obj_id >= ssc->ses_nobjects) {
- error = EINVAL;
- break;
- }
- cam_periph_lock(periph);
- error = (*ssc->ses_vec.set_objstat)(ssc, &objs, 1);
- cam_periph_unlock(periph);
-
- /*
- * Always (for now) invalidate entry.
- */
- ssc->ses_objmap[objs.obj_id].svalid = 0;
- break;
-
- case SESIOC_INIT:
-
- cam_periph_lock(periph);
- error = (*ssc->ses_vec.init_enc)(ssc);
- cam_periph_unlock(periph);
- break;
-
- default:
- cam_periph_lock(periph);
- error = cam_periph_ioctl(periph, cmd, arg_addr, seserror);
- cam_periph_unlock(periph);
- break;
- }
- return (error);
-}
-
-#define SES_CFLAGS CAM_RETRY_SELTO
-#define SES_FLAGS SF_NO_PRINT | SF_RETRY_UA
-static int
-ses_runcmd(struct ses_softc *ssc, char *cdb, int cdbl, char *dptr, int *dlenp)
-{
- int error, dlen;
- ccb_flags ddf;
- union ccb *ccb;
-
- if (dptr) {
- if ((dlen = *dlenp) < 0) {
- dlen = -dlen;
- ddf = CAM_DIR_OUT;
- } else {
- ddf = CAM_DIR_IN;
- }
- } else {
- dlen = 0;
- ddf = CAM_DIR_NONE;
- }
-
- if (cdbl > IOCDBLEN) {
- cdbl = IOCDBLEN;
- }
-
- ccb = cam_periph_getccb(ssc->periph, 1);
- cam_fill_csio(&ccb->csio, 0, sesdone, ddf, MSG_SIMPLE_Q_TAG, dptr,
- dlen, sizeof (struct scsi_sense_data), cdbl, 60 * 1000);
- bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl);
-
- error = cam_periph_runccb(ccb, seserror, SES_CFLAGS, SES_FLAGS, NULL);
- if (error) {
- if (dptr) {
- *dlenp = dlen;
- }
- } else {
- if (dptr) {
- *dlenp = ccb->csio.resid;
- }
- }
- xpt_release_ccb(ccb);
- return (error);
-}
-
-static void
-ses_log(struct ses_softc *ssc, const char *fmt, ...)
-{
- va_list ap;
-
- printf("%s%d: ", ssc->periph->periph_name, ssc->periph->unit_number);
- va_start(ap, fmt);
- vprintf(fmt, ap);
- va_end(ap);
-}
-
-/*
- * The code after this point runs on many platforms,
- * so forgive the slightly awkward and nonconforming
- * appearance.
- */
-
-/*
- * Is this a device that supports enclosure services?
- *
- * It's a pretty simple ruleset- if it is device type 0x0D (13), it's
- * an SES device. If it happens to be an old UNISYS SEN device, we can
- * handle that too.
- */
-
-#define SAFTE_START 44
-#define SAFTE_END 50
-#define SAFTE_LEN SAFTE_END-SAFTE_START
-
-static enctyp
-ses_type(void *buf, int buflen)
-{
- unsigned char *iqd = buf;
-
- if (buflen < 8+SEN_ID_LEN)
- return (SES_NONE);
-
- if ((iqd[0] & 0x1f) == T_ENCLOSURE) {
- if (STRNCMP(&iqd[8], SEN_ID, SEN_ID_LEN) == 0) {
- return (SES_SEN);
- } else if ((iqd[2] & 0x7) > 2) {
- return (SES_SES);
- } else {
- return (SES_SES_SCSI2);
- }
- return (SES_NONE);
- }
-
-#ifdef SES_ENABLE_PASSTHROUGH
- if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) {
- /*
- * PassThrough Device.
- */
- return (SES_SES_PASSTHROUGH);
- }
-#endif
-
- /*
- * The comparison is short for a reason-
- * some vendors were chopping it short.
- */
-
- if (buflen < SAFTE_END - 2) {
- return (SES_NONE);
- }
-
- if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) {
- return (SES_SAFT);
- }
- return (SES_NONE);
-}
-
-/*
- * SES Native Type Device Support
- */
-
-/*
- * SES Diagnostic Page Codes
- */
-
-typedef enum {
- SesConfigPage = 0x1,
- SesControlPage,
-#define SesStatusPage SesControlPage
- SesHelpTxt,
- SesStringOut,
-#define SesStringIn SesStringOut
- SesThresholdOut,
-#define SesThresholdIn SesThresholdOut
- SesArrayControl,
-#define SesArrayStatus SesArrayControl
- SesElementDescriptor,
- SesShortStatus
-} SesDiagPageCodes;
-
-/*
- * minimal amounts
- */
-
-/*
- * Minimum amount of data, starting from byte 0, to have
- * the config header.
- */
-#define SES_CFGHDR_MINLEN 12
-
-/*
- * Minimum amount of data, starting from byte 0, to have
- * the config header and one enclosure header.
- */
-#define SES_ENCHDR_MINLEN 48
-
-/*
- * Take this value, subtract it from VEnclen and you know
- * the length of the vendor unique bytes.
- */
-#define SES_ENCHDR_VMIN 36
-
-/*
- * SES Data Structures
- */
-
-typedef struct {
- uint32_t GenCode; /* Generation Code */
- uint8_t Nsubenc; /* Number of Subenclosures */
-} SesCfgHdr;
-
-typedef struct {
- uint8_t Subencid; /* SubEnclosure Identifier */
- uint8_t Ntypes; /* # of supported types */
- uint8_t VEnclen; /* Enclosure Descriptor Length */
-} SesEncHdr;
-
-typedef struct {
- uint8_t encWWN[8]; /* XXX- Not Right Yet */
- uint8_t encVid[8];
- uint8_t encPid[16];
- uint8_t encRev[4];
- uint8_t encVen[1];
-} SesEncDesc;
-
-typedef struct {
- uint8_t enc_type; /* type of element */
- uint8_t enc_maxelt; /* maximum supported */
- uint8_t enc_subenc; /* in SubEnc # N */
- uint8_t enc_tlen; /* Type Descriptor Text Length */
-} SesThdr;
-
-typedef struct {
- uint8_t comstatus;
- uint8_t comstat[3];
-} SesComStat;
-
-struct typidx {
- int ses_tidx;
- int ses_oidx;
-};
-
-struct sscfg {
- uint8_t ses_ntypes; /* total number of types supported */
-
- /*
- * We need to keep a type index as well as an
- * object index for each object in an enclosure.
- */
- struct typidx *ses_typidx;
-
- /*
- * We also need to keep track of the number of elements
- * per type of element. This is needed later so that we
- * can find precisely in the returned status data the
- * status for the Nth element of the Kth type.
- */
- uint8_t * ses_eltmap;
-};
-
-
-/*
- * (de)canonicalization defines
- */
-#define sbyte(x, byte) ((((uint32_t)(x)) >> (byte * 8)) & 0xff)
-#define sbit(x, bit) (((uint32_t)(x)) << bit)
-#define sset8(outp, idx, sval) (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0)
-
-#define sset16(outp, idx, sval) \
- (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \
- (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0)
-
-
-#define sset24(outp, idx, sval) \
- (((uint8_t *)(outp))[idx++]) = sbyte(sval, 2), \
- (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \
- (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0)
-
-
-#define sset32(outp, idx, sval) \
- (((uint8_t *)(outp))[idx++]) = sbyte(sval, 3), \
- (((uint8_t *)(outp))[idx++]) = sbyte(sval, 2), \
- (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \
- (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0)
-
-#define gbyte(x, byte) ((((uint32_t)(x)) & 0xff) << (byte * 8))
-#define gbit(lv, in, idx, shft, mask) lv = ((in[idx] >> shft) & mask)
-#define sget8(inp, idx, lval) lval = (((uint8_t *)(inp))[idx++])
-#define gget8(inp, idx, lval) lval = (((uint8_t *)(inp))[idx])
-
-#define sget16(inp, idx, lval) \
- lval = gbyte((((uint8_t *)(inp))[idx]), 1) | \
- (((uint8_t *)(inp))[idx+1]), idx += 2
-
-#define gget16(inp, idx, lval) \
- lval = gbyte((((uint8_t *)(inp))[idx]), 1) | \
- (((uint8_t *)(inp))[idx+1])
-
-#define sget24(inp, idx, lval) \
- lval = gbyte((((uint8_t *)(inp))[idx]), 2) | \
- gbyte((((uint8_t *)(inp))[idx+1]), 1) | \
- (((uint8_t *)(inp))[idx+2]), idx += 3
-
-#define gget24(inp, idx, lval) \
- lval = gbyte((((uint8_t *)(inp))[idx]), 2) | \
- gbyte((((uint8_t *)(inp))[idx+1]), 1) | \
- (((uint8_t *)(inp))[idx+2])
-
-#define sget32(inp, idx, lval) \
- lval = gbyte((((uint8_t *)(inp))[idx]), 3) | \
- gbyte((((uint8_t *)(inp))[idx+1]), 2) | \
- gbyte((((uint8_t *)(inp))[idx+2]), 1) | \
- (((uint8_t *)(inp))[idx+3]), idx += 4
-
-#define gget32(inp, idx, lval) \
- lval = gbyte((((uint8_t *)(inp))[idx]), 3) | \
- gbyte((((uint8_t *)(inp))[idx+1]), 2) | \
- gbyte((((uint8_t *)(inp))[idx+2]), 1) | \
- (((uint8_t *)(inp))[idx+3])
-
-#define SCSZ 0x2000
-#define CFLEN (256 + SES_ENCHDR_MINLEN)
-
-/*
- * Routines specific && private to SES only
- */
-
-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, 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 *);
-
-static int
-ses_softc_init(ses_softc_t *ssc, int doinit)
-{
- if (doinit == 0) {
- struct sscfg *cc;
- if (ssc->ses_nobjects) {
- SES_FREE(ssc->ses_objmap,
- ssc->ses_nobjects * sizeof (encobj));
- ssc->ses_objmap = NULL;
- }
- if ((cc = ssc->ses_private) != NULL) {
- if (cc->ses_eltmap && cc->ses_ntypes) {
- SES_FREE(cc->ses_eltmap, cc->ses_ntypes);
- cc->ses_eltmap = NULL;
- cc->ses_ntypes = 0;
- }
- if (cc->ses_typidx && ssc->ses_nobjects) {
- SES_FREE(cc->ses_typidx,
- ssc->ses_nobjects * sizeof (struct typidx));
- cc->ses_typidx = NULL;
- }
- SES_FREE(cc, sizeof (struct sscfg));
- ssc->ses_private = NULL;
- }
- ssc->ses_nobjects = 0;
- return (0);
- }
- if (ssc->ses_private == NULL) {
- ssc->ses_private = SES_MALLOC(sizeof (struct sscfg));
- }
- if (ssc->ses_private == NULL) {
- return (ENOMEM);
- }
- ssc->ses_nobjects = 0;
- ssc->ses_encstat = 0;
- return (ses_getconfig(ssc));
-}
-
-static int
-ses_init_enc(ses_softc_t *ssc)
-{
- return (0);
-}
-
-static int
-ses_get_encstat(ses_softc_t *ssc, int slpflag)
-{
- SesComStat ComStat;
- int status;
-
- if ((status = ses_getputstat(ssc, -1, &ComStat, slpflag, 1)) != 0) {
- return (status);
- }
- ssc->ses_encstat = ComStat.comstatus | ENCI_SVALID;
- return (0);
-}
-
-static int
-ses_set_encstat(ses_softc_t *ssc, uint8_t encstat, int slpflag)
-{
- SesComStat ComStat;
- int status;
-
- ComStat.comstatus = encstat & 0xf;
- if ((status = ses_getputstat(ssc, -1, &ComStat, slpflag, 0)) != 0) {
- return (status);
- }
- ssc->ses_encstat = encstat & 0xf; /* note no SVALID set */
- return (0);
-}
-
-static int
-ses_get_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflag)
-{
- int i = (int)obp->obj_id;
-
- if (ssc->ses_objmap[i].svalid == 0) {
- SesComStat ComStat;
- int err = ses_getputstat(ssc, i, &ComStat, slpflag, 1);
- if (err)
- return (err);
- ssc->ses_objmap[i].encstat[0] = ComStat.comstatus;
- ssc->ses_objmap[i].encstat[1] = ComStat.comstat[0];
- ssc->ses_objmap[i].encstat[2] = ComStat.comstat[1];
- ssc->ses_objmap[i].encstat[3] = ComStat.comstat[2];
- ssc->ses_objmap[i].svalid = 1;
- }
- 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);
-}
-
-static int
-ses_set_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflag)
-{
- SesComStat ComStat;
- int err;
- /*
- * If this is clear, we don't do diddly.
- */
- if ((obp->cstat[0] & SESCTL_CSEL) == 0) {
- return (0);
- }
- ComStat.comstatus = obp->cstat[0];
- ComStat.comstat[0] = obp->cstat[1];
- ComStat.comstat[1] = obp->cstat[2];
- ComStat.comstat[2] = obp->cstat[3];
- err = ses_getputstat(ssc, (int)obp->obj_id, &ComStat, slpflag, 0);
- ssc->ses_objmap[(int)obp->obj_id].svalid = 0;
- return (err);
-}
-
-static int
-ses_getconfig(ses_softc_t *ssc)
-{
- struct sscfg *cc;
- SesCfgHdr cf;
- SesEncHdr hd;
- SesEncDesc *cdp;
- SesThdr thdr;
- int err, amt, i, nobj, ntype, maxima;
- char storage[CFLEN], *sdata;
- static char cdb[6] = {
- RECEIVE_DIAGNOSTIC, 0x1, SesConfigPage, SCSZ >> 8, SCSZ & 0xff, 0
- };
-
- cc = ssc->ses_private;
- if (cc == NULL) {
- return (ENXIO);
- }
-
- sdata = SES_MALLOC(SCSZ);
- if (sdata == NULL)
- return (ENOMEM);
-
- amt = SCSZ;
- err = ses_runcmd(ssc, cdb, 6, sdata, &amt);
- if (err) {
- SES_FREE(sdata, SCSZ);
- return (err);
- }
- amt = SCSZ - amt;
-
- if (ses_cfghdr((uint8_t *) sdata, amt, &cf)) {
- SES_LOG(ssc, "Unable to parse SES Config Header\n");
- SES_FREE(sdata, SCSZ);
- return (EIO);
- }
- if (amt < SES_ENCHDR_MINLEN) {
- SES_LOG(ssc, "runt enclosure length (%d)\n", amt);
- SES_FREE(sdata, SCSZ);
- return (EIO);
- }
-
- SES_VLOG(ssc, "GenCode %x %d Subenclosures\n", cf.GenCode, cf.Nsubenc);
-
- /*
- * Now waltz through all the subenclosures toting up the
- * number of types available in each. For this, we only
- * really need the enclosure header. However, we get the
- * enclosure descriptor for debug purposes, as well
- * as self-consistency checking purposes.
- */
-
- maxima = cf.Nsubenc + 1;
- cdp = (SesEncDesc *) storage;
- for (ntype = i = 0; i < maxima; i++) {
- MEMZERO((caddr_t)cdp, sizeof (*cdp));
- if (ses_enchdr((uint8_t *) sdata, amt, i, &hd)) {
- SES_LOG(ssc, "Cannot Extract Enclosure Header %d\n", i);
- SES_FREE(sdata, SCSZ);
- return (EIO);
- }
- SES_VLOG(ssc, " SubEnclosure ID %d, %d Types With this ID, En"
- "closure Length %d\n", hd.Subencid, hd.Ntypes, hd.VEnclen);
-
- if (ses_encdesc((uint8_t *)sdata, amt, i, cdp)) {
- SES_LOG(ssc, "Can't get Enclosure Descriptor %d\n", i);
- SES_FREE(sdata, SCSZ);
- return (EIO);
- }
- SES_VLOG(ssc, " WWN: %02x%02x%02x%02x%02x%02x%02x%02x\n",
- cdp->encWWN[0], cdp->encWWN[1], cdp->encWWN[2],
- cdp->encWWN[3], cdp->encWWN[4], cdp->encWWN[5],
- cdp->encWWN[6], cdp->encWWN[7]);
- ntype += hd.Ntypes;
- }
-
- /*
- * Now waltz through all the types that are available, getting
- * the type header so we can start adding up the number of
- * objects available.
- */
- for (nobj = i = 0; i < ntype; i++) {
- if (ses_getthdr((uint8_t *)sdata, amt, i, &thdr)) {
- SES_LOG(ssc, "Can't get Enclosure Type Header %d\n", i);
- SES_FREE(sdata, SCSZ);
- return (EIO);
- }
- SES_LOG(ssc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc "
- "%d, Text Length %d\n", i, thdr.enc_type, thdr.enc_maxelt,
- thdr.enc_subenc, thdr.enc_tlen);
- nobj += thdr.enc_maxelt;
- }
-
-
- /*
- * Now allocate the object array and type map.
- */
-
- ssc->ses_objmap = SES_MALLOC(nobj * sizeof (encobj));
- cc->ses_typidx = SES_MALLOC(nobj * sizeof (struct typidx));
- cc->ses_eltmap = SES_MALLOC(ntype);
-
- if (ssc->ses_objmap == NULL || cc->ses_typidx == NULL ||
- cc->ses_eltmap == NULL) {
- if (ssc->ses_objmap) {
- SES_FREE(ssc->ses_objmap, (nobj * sizeof (encobj)));
- ssc->ses_objmap = NULL;
- }
- if (cc->ses_typidx) {
- SES_FREE(cc->ses_typidx,
- (nobj * sizeof (struct typidx)));
- cc->ses_typidx = NULL;
- }
- if (cc->ses_eltmap) {
- SES_FREE(cc->ses_eltmap, ntype);
- cc->ses_eltmap = NULL;
- }
- SES_FREE(sdata, SCSZ);
- return (ENOMEM);
- }
- MEMZERO(ssc->ses_objmap, nobj * sizeof (encobj));
- MEMZERO(cc->ses_typidx, nobj * sizeof (struct typidx));
- MEMZERO(cc->ses_eltmap, ntype);
- cc->ses_ntypes = (uint8_t) ntype;
- ssc->ses_nobjects = nobj;
-
- /*
- * Now waltz through the # of types again to fill in the types
- * (and subenclosure ids) of the allocated objects.
- */
- nobj = 0;
- for (i = 0; i < ntype; i++) {
- int j;
- if (ses_getthdr((uint8_t *)sdata, amt, i, &thdr)) {
- continue;
- }
- cc->ses_eltmap[i] = thdr.enc_maxelt;
- for (j = 0; j < thdr.enc_maxelt; j++) {
- cc->ses_typidx[nobj].ses_tidx = i;
- cc->ses_typidx[nobj].ses_oidx = j;
- ssc->ses_objmap[nobj].subenclosure = thdr.enc_subenc;
- ssc->ses_objmap[nobj++].enctype = thdr.enc_type;
- }
- }
- SES_FREE(sdata, SCSZ);
- return (0);
-}
-
-static int
-ses_getputstat(ses_softc_t *ssc, int objid, SesComStat *sp, int slp, int in)
-{
- struct sscfg *cc;
- int err, amt, bufsiz, tidx, oidx;
- char cdb[6], *sdata;
-
- cc = ssc->ses_private;
- if (cc == NULL) {
- return (ENXIO);
- }
-
- /*
- * If we're just getting overall enclosure status,
- * we only need 2 bytes of data storage.
- *
- * If we're getting anything else, we know how much
- * storage we need by noting that starting at offset
- * 8 in returned data, all object status bytes are 4
- * bytes long, and are stored in chunks of types(M)
- * and nth+1 instances of type M.
- */
- if (objid == -1) {
- bufsiz = 2;
- } else {
- bufsiz = (ssc->ses_nobjects * 4) + (cc->ses_ntypes * 4) + 8;
- }
- sdata = SES_MALLOC(bufsiz);
- if (sdata == NULL)
- return (ENOMEM);
-
- cdb[0] = RECEIVE_DIAGNOSTIC;
- cdb[1] = 1;
- cdb[2] = SesStatusPage;
- cdb[3] = bufsiz >> 8;
- cdb[4] = bufsiz & 0xff;
- cdb[5] = 0;
- amt = bufsiz;
- err = ses_runcmd(ssc, cdb, 6, sdata, &amt);
- if (err) {
- SES_FREE(sdata, bufsiz);
- return (err);
- }
- amt = bufsiz - amt;
-
- if (objid == -1) {
- tidx = -1;
- oidx = -1;
- } else {
- tidx = cc->ses_typidx[objid].ses_tidx;
- oidx = cc->ses_typidx[objid].ses_oidx;
- }
- if (in) {
- if (ses_decode(sdata, amt, cc->ses_eltmap, tidx, oidx, sp)) {
- err = ENODEV;
- }
- } else {
- if (ses_encode(sdata, amt, cc->ses_eltmap, tidx, oidx, sp)) {
- err = ENODEV;
- } else {
- cdb[0] = SEND_DIAGNOSTIC;
- cdb[1] = 0x10;
- cdb[2] = 0;
- cdb[3] = bufsiz >> 8;
- cdb[4] = bufsiz & 0xff;
- cdb[5] = 0;
- amt = -bufsiz;
- err = ses_runcmd(ssc, cdb, 6, sdata, &amt);
- }
- }
- SES_FREE(sdata, bufsiz);
- return (0);
-}
-
-
-/*
- * Routines to parse returned SES data structures.
- * Architecture and compiler independent.
- */
-
-static int
-ses_cfghdr(uint8_t *buffer, int buflen, SesCfgHdr *cfp)
-{
- if (buflen < SES_CFGHDR_MINLEN) {
- return (-1);
- }
- gget8(buffer, 1, cfp->Nsubenc);
- gget32(buffer, 4, cfp->GenCode);
- return (0);
-}
-
-static int
-ses_enchdr(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncHdr *chp)
-{
- int s, off = 8;
- for (s = 0; s < SubEncId; s++) {
- if (off + 3 > amt)
- return (-1);
- off += buffer[off+3] + 4;
- }
- if (off + 3 > amt) {
- return (-1);
- }
- gget8(buffer, off+1, chp->Subencid);
- gget8(buffer, off+2, chp->Ntypes);
- gget8(buffer, off+3, chp->VEnclen);
- return (0);
-}
-
-static int
-ses_encdesc(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncDesc *cdp)
-{
- int s, e, enclen, off = 8;
- for (s = 0; s < SubEncId; s++) {
- if (off + 3 > amt)
- return (-1);
- off += buffer[off+3] + 4;
- }
- if (off + 3 > amt) {
- return (-1);
- }
- gget8(buffer, off+3, enclen);
- off += 4;
- if (off >= amt)
- return (-1);
-
- e = off + enclen;
- if (e > amt) {
- e = amt;
- }
- MEMCPY(cdp, &buffer[off], e - off);
- return (0);
-}
-
-static int
-ses_getthdr(uint8_t *buffer, int amt, int nth, SesThdr *thp)
-{
- int s, off = 8;
-
- if (amt < SES_CFGHDR_MINLEN) {
- return (-1);
- }
- for (s = 0; s < buffer[1]; s++) {
- if (off + 3 > amt)
- return (-1);
- off += buffer[off+3] + 4;
- }
- if (off + 3 > amt) {
- return (-1);
- }
- off += buffer[off+3] + 4 + (nth * 4);
- if (amt < (off + 4))
- return (-1);
-
- gget8(buffer, off++, thp->enc_type);
- gget8(buffer, off++, thp->enc_maxelt);
- gget8(buffer, off++, thp->enc_subenc);
- gget8(buffer, off, thp->enc_tlen);
- return (0);
-}
-
-/*
- * This function needs a little explanation.
- *
- * The arguments are:
- *
- *
- * char *b, int amt
- *
- * These describes the raw input SES status data and length.
- *
- * uint8_t *ep
- *
- * This is a map of the number of types for each element type
- * in the enclosure.
- *
- * int elt
- *
- * This is the element type being sought. If elt is -1,
- * then overall enclosure status is being sought.
- *
- * int elm
- *
- * This is the ordinal Mth element of type elt being sought.
- *
- * SesComStat *sp
- *
- * This is the output area to store the status for
- * the Mth element of type Elt.
- */
-
-static int
-ses_decode(char *b, int amt, uint8_t *ep, int elt, int elm, SesComStat *sp)
-{
- int idx, i;
-
- /*
- * If it's overall enclosure status being sought, get that.
- * We need at least 2 bytes of status data to get that.
- */
- if (elt == -1) {
- if (amt < 2)
- return (-1);
- gget8(b, 1, sp->comstatus);
- sp->comstat[0] = 0;
- sp->comstat[1] = 0;
- sp->comstat[2] = 0;
- return (0);
- }
-
- /*
- * Check to make sure that the Mth element is legal for type Elt.
- */
-
- if (elm >= ep[elt])
- return (-1);
-
- /*
- * Starting at offset 8, start skipping over the storage
- * for the element types we're not interested in.
- */
- for (idx = 8, i = 0; i < elt; i++) {
- idx += ((ep[i] + 1) * 4);
- }
-
- /*
- * Skip over Overall status for this element type.
- */
- idx += 4;
-
- /*
- * And skip to the index for the Mth element that we're going for.
- */
- idx += (4 * elm);
-
- /*
- * Make sure we haven't overflowed the buffer.
- */
- if (idx+4 > amt)
- return (-1);
-
- /*
- * Retrieve the status.
- */
- gget8(b, idx++, sp->comstatus);
- gget8(b, idx++, sp->comstat[0]);
- gget8(b, idx++, sp->comstat[1]);
- gget8(b, idx++, sp->comstat[2]);
-#if 0
- PRINTF("Get Elt 0x%x Elm 0x%x (idx %d)\n", elt, elm, idx-4);
-#endif
- return (0);
-}
-
-/*
- * This is the mirror function to ses_decode, but we set the 'select'
- * bit for the object which we're interested in. All other objects,
- * after a status fetch, should have that bit off. Hmm. It'd be easy
- * enough to ensure this, so we will.
- */
-
-static int
-ses_encode(char *b, int amt, uint8_t *ep, int elt, int elm, SesComStat *sp)
-{
- int idx, i;
-
- /*
- * If it's overall enclosure status being sought, get that.
- * We need at least 2 bytes of status data to get that.
- */
- if (elt == -1) {
- if (amt < 2)
- return (-1);
- i = 0;
- sset8(b, i, 0);
- sset8(b, i, sp->comstatus & 0xf);
-#if 0
- PRINTF("set EncStat %x\n", sp->comstatus);
-#endif
- return (0);
- }
-
- /*
- * Check to make sure that the Mth element is legal for type Elt.
- */
-
- if (elm >= ep[elt])
- return (-1);
-
- /*
- * Starting at offset 8, start skipping over the storage
- * for the element types we're not interested in.
- */
- for (idx = 8, i = 0; i < elt; i++) {
- idx += ((ep[i] + 1) * 4);
- }
-
- /*
- * Skip over Overall status for this element type.
- */
- idx += 4;
-
- /*
- * And skip to the index for the Mth element that we're going for.
- */
- idx += (4 * elm);
-
- /*
- * Make sure we haven't overflowed the buffer.
- */
- if (idx+4 > amt)
- return (-1);
-
- /*
- * Set the status.
- */
- sset8(b, idx, sp->comstatus);
- sset8(b, idx, sp->comstat[0]);
- sset8(b, idx, sp->comstat[1]);
- sset8(b, idx, sp->comstat[2]);
- idx -= 4;
-
-#if 0
- PRINTF("Set Elt 0x%x Elm 0x%x (idx %d) with %x %x %x %x\n",
- elt, elm, idx, sp->comstatus, sp->comstat[0],
- sp->comstat[1], sp->comstat[2]);
-#endif
-
- /*
- * Now make sure all other 'Select' bits are off.
- */
- for (i = 8; i < amt; i += 4) {
- if (i != idx)
- b[i] &= ~0x80;
- }
- /*
- * And make sure the INVOP bit is clear.
- */
- b[2] &= ~0x10;
-
- 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); \
- }
-
-
-static 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);
-}
-
-static int
-safte_init_enc(ses_softc_t *ssc)
-{
- int err;
- static char cdb0[6] = { SEND_DIAGNOSTIC };
-
- err = ses_runcmd(ssc, cdb0, 6, NULL, 0);
- if (err) {
- return (err);
- }
- DELAY(5000);
- err = wrbuf16(ssc, SAFTE_WT_GLOBAL, 0, 0, 0, 1);
- return (err);
-}
-
-static int
-safte_get_encstat(ses_softc_t *ssc, int slpflg)
-{
- return (safte_rdstat(ssc, slpflg));
-}
-
-static 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));
-}
-
-static 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);
-}
-
-
-static int
-safte_set_objstat(ses_softc_t *ssc, ses_objstat *obp, int slp)
-{
- int idx, err;
- encobj *ep;
- struct scfg *cc;
-
-
- SES_DLOG(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.
- */
-
- /*
- * Actually.... All of the units that people out in the world
- * seem to have do not come even close to setting a value that
- * complies with this spec.
- *
- * The closest explanation I could find was in an
- * LSI-Logic manual, which seemed to indicate that
- * this value would be set by whatever the I2C code
- * would interpolate from the output of an LM75
- * temperature sensor.
- *
- * This means that it is impossible to use the actual
- * numeric value to predict anything. But we don't want
- * to lose the value. So, we'll propagate the *uncorrected*
- * value and set SES_OBJSTAT_NOTAVAIL. We'll depend on the
- * temperature flags for warnings.
- */
- ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NOTAVAIL;
- ssc->ses_objmap[oid].encstat[1] = 0;
- ssc->ses_objmap[oid].encstat[2] = sdata[r];
- ssc->ses_objmap[oid].encstat[3] = 0;
- 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_DLOG(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_DLOG(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_DLOG(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_DLOG(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);
-}
diff --git a/sys/cam/scsi/scsi_ses.h b/sys/cam/scsi/scsi_ses.h
index a52d517..ffc5493 100644
--- a/sys/cam/scsi/scsi_ses.h
+++ b/sys/cam/scsi/scsi_ses.h
@@ -29,107 +29,2131 @@
*
*/
-#define SESIOC ('s' - 040)
-#define SESIOC_GETNOBJ _IO(SESIOC, 1)
-#define SESIOC_GETOBJMAP _IO(SESIOC, 2)
-#define SESIOC_GETENCSTAT _IO(SESIOC, 3)
-#define SESIOC_SETENCSTAT _IO(SESIOC, 4)
-#define SESIOC_GETOBJSTAT _IO(SESIOC, 5)
-#define SESIOC_SETOBJSTAT _IO(SESIOC, 6)
-#define SESIOC_GETTEXT _IO(SESIOC, 7)
-#define SESIOC_INIT _IO(SESIOC, 8)
+#ifndef _SCSI_SES_H_
+#define _SCSI_SES_H_
-/*
- * Platform Independent Definitions for SES devices.
- */
-/*
- * SCSI Based Environmental Services Application Defines
- *
- * Based almost entirely on SCSI-3 SES Revision 8A specification,
- * but slightly abstracted as the underlying device may in fact
- * be a SAF-TE or vendor unique device.
- */
-/*
- * SES Driver Operations:
- * (The defines themselves are platform and access method specific)
- *
- * SESIOC_GETNOBJ
- * SESIOC_GETOBJMAP
- * SESIOC_GETENCSTAT
- * SESIOC_SETENCSTAT
- * SESIOC_GETOBJSTAT
- * SESIOC_SETOBJSTAT
- * SESIOC_INIT
- *
- *
- * An application finds out how many objects an SES instance
- * is managing by performing a SESIOC_GETNOBJ operation. It then
- * performs a SESIOC_GETOBJMAP to get the map that contains the
- * object identifiers for all objects (see ses_object below).
- * This information is static.
- *
- * The application may perform SESIOC_GETOBJSTAT operations to retrieve
- * status on an object (see the ses_objstat structure below), SESIOC_SETOBJSTAT
- * operations to set status for an object.
- *
- * Similarly overall enclosure status me be fetched or set via
- * SESIOC_GETENCSTAT or SESIOC_SETENCSTAT operations (see ses_encstat below).
- *
- * Readers should note that there is nothing that requires either a set
- * or a clear operation to actually latch and do anything in the target.
- *
- * A SESIOC_INIT operation causes the enclosure to be initialized.
- */
+#include <cam/scsi/scsi_all.h>
-typedef struct {
- unsigned int obj_id; /* Object Identifier */
- unsigned char subencid; /* SubEnclosure ID */
- unsigned char object_type; /* Object Type */
-} ses_object;
-
-/* Object Types */
-#define SESTYP_UNSPECIFIED 0x00
-#define SESTYP_DEVICE 0x01
-#define SESTYP_POWER 0x02
-#define SESTYP_FAN 0x03
-#define SESTYP_THERM 0x04
-#define SESTYP_DOORLOCK 0x05
-#define SESTYP_ALARM 0x06
-#define SESTYP_ESCC 0x07 /* Enclosure SCC */
-#define SESTYP_SCC 0x08 /* SCC */
-#define SESTYP_NVRAM 0x09
-#define SESTYP_UPS 0x0b
-#define SESTYP_DISPLAY 0x0c
-#define SESTYP_KEYPAD 0x0d
-#define SESTYP_ENCLOSURE 0x0e
-#define SESTYP_SCSIXVR 0x0f
-#define SESTYP_LANGUAGE 0x10
-#define SESTYP_COMPORT 0x11
-#define SESTYP_VOM 0x12
-#define SESTYP_AMMETER 0x13
-#define SESTYP_SCSI_TGT 0x14
-#define SESTYP_SCSI_INI 0x15
-#define SESTYP_SUBENC 0x16
-#define SESTYP_ARRAY 0x17
-#define SESTYP_SASEXPANDER 0x18
-#define SESTYP_SASCONNECTOR 0x19
+/*========================== Field Extraction Macros =========================*/
+#define MK_ENUM(S, F, SUFFIX) S ## _ ## F ## SUFFIX
-/*
- * Overall Enclosure Status
- */
-typedef unsigned char ses_encstat;
-#define SES_ENCSTAT_UNRECOV 0x1
-#define SES_ENCSTAT_CRITICAL 0x2
-#define SES_ENCSTAT_NONCRITICAL 0x4
-#define SES_ENCSTAT_INFO 0x8
+#define GEN_GETTER(LS, US, LF, UF) \
+static inline int \
+LS ## _get_ ## LF(struct LS *elem) { \
+ return ((elem->bytes[MK_ENUM(US,UF,_BYTE)] & MK_ENUM(US,UF,_MASK)) \
+ >> MK_ENUM(US,UF,_SHIFT)); \
+}
+
+#define GEN_SETTER(LS, US, LF, UF) \
+static inline void \
+LS ## _set_ ## LF(struct LS *elem, int val) { \
+ elem->bytes[MK_ENUM(US,UF,_BYTE)] &= ~MK_ENUM(US,UF,_MASK); \
+ elem->bytes[MK_ENUM(US,UF,_BYTE)] |= \
+ (val << MK_ENUM(US,UF,_SHIFT)) & MK_ENUM(US,UF,_MASK); \
+}
+
+#define GEN_HDR_GETTER(LS, US, LF, UF) \
+static inline int \
+LS ## _get_ ## LF(struct LS *page) { \
+ return ((page->hdr.page_specific_flags & MK_ENUM(US,UF,_MASK)) \
+ >> MK_ENUM(US,UF,_SHIFT)); \
+}
+
+#define GEN_HDR_SETTER(LS, US, LF, UF) \
+static inline void \
+LS ## _set_ ## LF(struct LS *page, int val) { \
+ page->hdr.page_specific_flags &= ~MK_ENUM(US,UF,_MASK); \
+ page->hdr.page_specific_flags |= \
+ (val << MK_ENUM(US,UF,_SHIFT)) & MK_ENUM(US,UF,_MASK); \
+}
+
+#define GEN_ACCESSORS(LS, US, LF, UF) \
+GEN_GETTER(LS, US, LF, UF) \
+GEN_SETTER(LS, US, LF, UF)
+
+#define GEN_HDR_ACCESSORS(LS, US, LF, UF) \
+GEN_HDR_GETTER(LS, US, LF, UF) \
+GEN_HDR_SETTER(LS, US, LF, UF)
+
+/*=============== Common SCSI ENC Diagnostic Page Structures ===============*/
+struct ses_page_hdr {
+ uint8_t page_code;
+ uint8_t page_specific_flags;
+ uint8_t length[2];
+ uint8_t gen_code[4];
+};
+
+static inline size_t
+ses_page_length(const struct ses_page_hdr *hdr)
+{
+ /*
+ * The page length as received only accounts for bytes that
+ * follow the length field, namely starting with the generation
+ * code field.
+ */
+ return (scsi_2btoul(hdr->length)
+ + offsetof(struct ses_page_hdr, gen_code));
+}
+
+/*============= SCSI ENC Configuration Diagnostic Page Structures ============*/
+struct ses_enc_desc {
+ uint8_t byte0;
+ /*
+ * reserved0 : 1,
+ * rel_id : 3, relative enclosure process id
+ * reserved1 : 1,
+ * num_procs : 3; number of enclosure procesenc
+ */
+ uint8_t subenc_id; /* Sub-enclosure Identifier */
+ uint8_t num_types; /* # of supported types */
+ uint8_t length; /* Enclosure Descriptor Length */
+ uint8_t logical_id[8]; /* formerly wwn */
+ uint8_t vendor_id[8];
+ uint8_t product_id[16];
+ uint8_t product_rev[4];
+ uint8_t vendor_bytes[];
+};
+
+static inline uint8_t *
+ses_enc_desc_last_byte(struct ses_enc_desc *encdesc)
+{
+ return (&encdesc->length + encdesc->length + 1);
+}
+
+static inline struct ses_enc_desc *
+ses_enc_desc_next(struct ses_enc_desc *encdesc)
+{
+ return ((struct ses_enc_desc *)(ses_enc_desc_last_byte(encdesc) + 1));
+}
+
+static inline int
+ses_enc_desc_is_complete(struct ses_enc_desc *encdesc, uint8_t *last_buf_byte)
+{
+ return (&encdesc->length <= last_buf_byte
+ && ses_enc_desc_last_byte(encdesc) <= last_buf_byte);
+}
+
+struct ses_elm_type_desc {
+ uint8_t etype_elm_type; /* type of element */
+ uint8_t etype_maxelt; /* maximum supported */
+ uint8_t etype_subenc; /* in sub-enclosure #n */
+ uint8_t etype_txt_len; /* Type Descriptor Text Length */
+};
+
+struct ses_cfg_page {
+ struct ses_page_hdr hdr;
+ struct ses_enc_desc subencs[];
+ /* type descriptors */
+ /* type text */
+};
+
+static inline int
+ses_cfg_page_get_num_subenc(struct ses_cfg_page *page)
+{
+ return (page->hdr.page_specific_flags + 1);
+}
+
+
+/*================ SCSI SES Control Diagnostic Page Structures ==============*/
+struct ses_ctrl_common {
+ uint8_t bytes[1];
+};
+
+enum ses_ctrl_common_field_data {
+ SES_CTRL_COMMON_SELECT_BYTE = 0,
+ SES_CTRL_COMMON_SELECT_MASK = 0x80,
+ SES_CTRL_COMMON_SELECT_SHIFT = 7,
+
+ SES_CTRL_COMMON_PRDFAIL_BYTE = 0,
+ SES_CTRL_COMMON_PRDFAIL_MASK = 0x40,
+ SES_CTRL_COMMON_PRDFAIL_SHIFT = 6,
+
+ SES_CTRL_COMMON_DISABLE_BYTE = 0,
+ SES_CTRL_COMMON_DISABLE_MASK = 0x20,
+ SES_CTRL_COMMON_DISABLE_SHIFT = 5,
+
+ SES_CTRL_COMMON_RST_SWAP_BYTE = 0,
+ SES_CTRL_COMMON_RST_SWAP_MASK = 0x10,
+ SES_CTRL_COMMON_RST_SWAP_SHIFT = 4
+};
+
+#define GEN_SES_CTRL_COMMON_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_common, SES_CTRL_COMMON, LCASE, UCASE)
+GEN_SES_CTRL_COMMON_ACCESSORS(select, SELECT)
+GEN_SES_CTRL_COMMON_ACCESSORS(prdfail, PRDFAIL)
+GEN_SES_CTRL_COMMON_ACCESSORS(disable, DISABLE)
+GEN_SES_CTRL_COMMON_ACCESSORS(rst_swap, RST_SWAP)
+#undef GEN_SES_CTRL_COMMON_ACCESSORS
+
+/*------------------------ Device Slot Control Element ----------------------*/
+struct ses_ctrl_dev_slot {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_dev_slot_field_data {
+ SES_CTRL_DEV_SLOT_RQST_ACTIVE_BYTE = 1,
+ SES_CTRL_DEV_SLOT_RQST_ACTIVE_MASK = 0x80,
+ SES_CTRL_DEV_SLOT_RQST_ACTIVE_SHIFT = 7,
+
+ SES_CTRL_DEV_SLOT_DO_NOT_REMOVE_BYTE = 1,
+ SES_CTRL_DEV_SLOT_DO_NOT_REMOVE_MASK = 0x40,
+ SES_CTRL_DEV_SLOT_DO_NOT_REMOVE_SHIFT = 6,
+
+ SES_CTRL_DEV_SLOT_RQST_MISSING_BYTE = 1,
+ SES_CTRL_DEV_SLOT_RQST_MISSING_MASK = 0x10,
+ SES_CTRL_DEV_SLOT_RQST_MISSING_SHIFT = 4,
+
+ SES_CTRL_DEV_SLOT_RQST_INSERT_BYTE = 1,
+ SES_CTRL_DEV_SLOT_RQST_INSERT_MASK = 0x08,
+ SES_CTRL_DEV_SLOT_RQST_INSERT_SHIFT = 3,
+
+ SES_CTRL_DEV_SLOT_RQST_REMOVE_BYTE = 1,
+ SES_CTRL_DEV_SLOT_RQST_REMOVE_MASK = 0x04,
+ SES_CTRL_DEV_SLOT_RQST_REMOVE_SHIFT = 2,
+
+ SES_CTRL_DEV_SLOT_RQST_IDENT_BYTE = 1,
+ SES_CTRL_DEV_SLOT_RQST_IDENT_MASK = 0x02,
+ SES_CTRL_DEV_SLOT_RQST_IDENT_SHIFT = 1,
+
+ SES_CTRL_DEV_SLOT_RQST_FAULT_BYTE = 2,
+ SES_CTRL_DEV_SLOT_RQST_FAULT_MASK = 0x20,
+ SES_CTRL_DEV_SLOT_RQST_FAULT_SHIFT = 5,
+
+ SES_CTRL_DEV_SLOT_DEVICE_OFF_BYTE = 2,
+ SES_CTRL_DEV_SLOT_DEVICE_OFF_MASK = 0x10,
+ SES_CTRL_DEV_SLOT_DEVICE_OFF_SHIFT = 4,
+
+ SES_CTRL_DEV_SLOT_ENABLE_BYP_A_BYTE = 2,
+ SES_CTRL_DEV_SLOT_ENABLE_BYP_A_MASK = 0x08,
+ SES_CTRL_DEV_SLOT_ENABLE_BYP_A_SHIFT = 3,
+
+ SES_CTRL_DEV_SLOT_ENABLE_BYP_B_BYTE = 2,
+ SES_CTRL_DEV_SLOT_ENABLE_BYP_B_MASK = 0x04,
+ SES_CTRL_DEV_SLOT_ENABLE_BYP_B_SHIFT = 2
+};
+#define GEN_SES_CTRL_DEV_SLOT_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_dev_slot, SES_CTRL_DEV_SLOT, LCASE, UCASE)
+
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_active, RQST_ACTIVE)
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(do_not_remove, DO_NOT_REMOVE)
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_missing, RQST_MISSING)
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_insert, RQST_INSERT)
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_remove, RQST_REMOVE)
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_fault, RQST_FAULT)
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(device_off, DEVICE_OFF)
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(enable_byp_a, ENABLE_BYP_A)
+GEN_SES_CTRL_DEV_SLOT_ACCESSORS(enable_byp_b, ENABLE_BYP_B)
+#undef GEN_SES_CTRL_DEV_SLOT_ACCESSORS
+
+/*--------------------- Array Device Slot Control Element --------------------*/
+struct ses_ctrl_array_dev_slot {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_array_dev_slot_field_data {
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_OK_BYTE = 0,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_OK_MASK = 0x80,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_OK_SHIFT = 7,
+
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_RSVD_DEVICE_BYTE = 0,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_RSVD_DEVICE_MASK = 0x40,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_RSVD_DEVICE_SHIFT = 6,
+
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_HOT_SPARE_BYTE = 0,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_HOT_SPARE_MASK = 0x20,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_HOT_SPARE_SHIFT = 5,
+
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_CONS_CHECK_BYTE = 0,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_CONS_CHECK_MASK = 0x10,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_CONS_CHECK_SHIFT = 4,
+
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_CRIT_ARRAY_BYTE = 0,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_CRIT_ARRAY_MASK = 0x08,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_CRIT_ARRAY_SHIFT = 3,
+
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_FAILED_ARRAY_BYTE = 0,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_FAILED_ARRAY_MASK = 0x04,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_FAILED_ARRAY_SHIFT = 2,
+
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_BYTE = 0,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_MASK = 0x02,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_SHIFT = 1,
+
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_ABORT_BYTE = 0,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_ABORT_MASK = 0x01,
+ SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_ABORT_SHIFT = 0
+
+ /*
+ * The remaining fields are identical to the device
+ * slot element type. Access them through the device slot
+ * element type and its accessors.
+ */
+};
+#define GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_array_dev_slot, SES_CTRL_ARRAY_DEV_SLOT, \
+ LCASE, UCASE)
+GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_ok, RQST_OK)
+GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_rsvd_device, RQST_RSVD_DEVICE)
+GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_hot_spare, RQST_HOT_SPARE)
+GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_cons_check, RQST_CONS_CHECK)
+GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_in_crit_array, RQST_IN_CRIT_ARRAY)
+GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_in_failed_array,
+ RQST_IN_FAILED_ARRAY)
+GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_rebuild_remap, RQST_REBUILD_REMAP)
+GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_rebuild_remap_abort,
+ RQST_REBUILD_REMAP_ABORT)
+#undef GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS
+
+/*----------------------- Power Supply Control Element -----------------------*/
+struct ses_ctrl_power_supply {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_power_supply_field_data {
+ SES_CTRL_POWER_SUPPLY_RQST_IDENT_BYTE = 0,
+ SES_CTRL_POWER_SUPPLY_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_POWER_SUPPLY_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_POWER_SUPPLY_RQST_FAIL_BYTE = 2,
+ SES_CTRL_POWER_SUPPLY_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_POWER_SUPPLY_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_POWER_SUPPLY_RQST_ON_BYTE = 2,
+ SES_CTRL_POWER_SUPPLY_RQST_ON_MASK = 0x20,
+ SES_CTRL_POWER_SUPPLY_RQST_ON_SHIFT = 5
+};
+
+#define GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_power_supply, SES_CTRL_POWER_SUPPLY, LCASE, UCASE)
+GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(rqst_on, RQST_ON)
+#undef GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS
+
+/*-------------------------- Cooling Control Element -------------------------*/
+struct ses_ctrl_cooling {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_cooling_field_data {
+ SES_CTRL_COOLING_RQST_IDENT_BYTE = 0,
+ SES_CTRL_COOLING_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_COOLING_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_COOLING_RQST_FAIL_BYTE = 2,
+ SES_CTRL_COOLING_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_COOLING_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_COOLING_RQST_ON_BYTE = 2,
+ SES_CTRL_COOLING_RQST_ON_MASK = 0x20,
+ SES_CTRL_COOLING_RQST_ON_SHIFT = 5,
+
+ SES_CTRL_COOLING_RQSTED_SPEED_CODE_BYTE = 2,
+ SES_CTRL_COOLING_RQSTED_SPEED_CODE_MASK = 0x07,
+ SES_CTRL_COOLING_RQSTED_SPEED_CODE_SHIFT = 2,
+ SES_CTRL_COOLING_RQSTED_SPEED_CODE_UNCHANGED = 0x00,
+ SES_CTRL_COOLING_RQSTED_SPEED_CODE_LOWEST = 0x01,
+ SES_CTRL_COOLING_RQSTED_SPEED_CODE_HIGHEST = 0x07
+};
+
+#define GEN_SES_CTRL_COOLING_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_cooling, SES_CTRL_COOLING, LCASE, UCASE)
+GEN_SES_CTRL_COOLING_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_COOLING_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_COOLING_ACCESSORS(rqst_on, RQST_ON)
+GEN_SES_CTRL_COOLING_ACCESSORS(rqsted_speed_code, RQSTED_SPEED_CODE)
+#undef GEN_SES_CTRL_COOLING_ACCESSORS
+
+/*-------------------- Temperature Sensor Control Element --------------------*/
+struct ses_ctrl_temp_sensor {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_temp_sensor_field_data {
+ SES_CTRL_TEMP_SENSOR_RQST_IDENT_BYTE = 0,
+ SES_CTRL_TEMP_SENSOR_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_TEMP_SENSOR_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_TEMP_SENSOR_RQST_FAIL_BYTE = 0,
+ SES_CTRL_TEMP_SENSOR_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_TEMP_SENSOR_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_temp_sensor, SES_CTRL_TEMP_SENSOR, LCASE, UCASE)
+GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS
+
+/*------------------------- Door Lock Control Element ------------------------*/
+struct ses_ctrl_door_lock {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_door_lock_field_data {
+ SES_CTRL_DOOR_LOCK_RQST_IDENT_BYTE = 0,
+ SES_CTRL_DOOR_LOCK_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_DOOR_LOCK_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_DOOR_LOCK_RQST_FAIL_BYTE = 0,
+ SES_CTRL_DOOR_LOCK_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_DOOR_LOCK_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_DOOR_LOCK_UNLOCK_BYTE = 2,
+ SES_CTRL_DOOR_LOCK_UNLOCK_MASK = 0x01,
+ SES_CTRL_DOOR_LOCK_UNLOCK_SHIFT = 0
+};
+
+#define GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_door_lock, SES_CTRL_DOOR_LOCK, LCASE, UCASE)
+GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(unlock, UNLOCK)
+#undef GEN_SES_CTRL_DOOR_LOCK_ACCESSORS
+
+/*----------------------- Audible Alarm Control Element ----------------------*/
+struct ses_ctrl_audible_alarm {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_audible_alarm_field_data {
+ SES_CTRL_AUDIBLE_ALARM_RQST_IDENT_BYTE = 0,
+ SES_CTRL_AUDIBLE_ALARM_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_AUDIBLE_ALARM_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_AUDIBLE_ALARM_RQST_FAIL_BYTE = 0,
+ SES_CTRL_AUDIBLE_ALARM_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_AUDIBLE_ALARM_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_AUDIBLE_ALARM_SET_MUTE_BYTE = 2,
+ SES_CTRL_AUDIBLE_ALARM_SET_MUTE_MASK = 0x40,
+ SES_CTRL_AUDIBLE_ALARM_SET_MUTE_SHIFT = 6,
+
+ SES_CTRL_AUDIBLE_ALARM_SET_REMIND_BYTE = 2,
+ SES_CTRL_AUDIBLE_ALARM_SET_REMIND_MASK = 0x10,
+ SES_CTRL_AUDIBLE_ALARM_SET_REMIND_SHIFT = 4,
+
+ SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_BYTE = 2,
+ SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_MASK = 0x0F,
+ SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_SHIFT = 0,
+ SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_INFO = 0x08,
+ SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_NON_CRIT = 0x04,
+ SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_CRIT = 0x02,
+ SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_UNRECOV = 0x01
+};
+
+#define GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_audible_alarm, SES_CTRL_AUDIBLE_ALARM, LCASE, UCASE)
+GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(set_mute, SET_MUTE)
+GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(set_remind, SET_REMIND)
+GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(tone_control, TONE_CONTROL)
+#undef GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS
+
+/*--------- Enclosure Services Controller Electronics Control Element --------*/
+struct ses_ctrl_ecc_electronics {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_ecc_electronics_field_data {
+ SES_CTRL_ECC_ELECTRONICS_RQST_IDENT_BYTE = 0,
+ SES_CTRL_ECC_ELECTRONICS_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_ECC_ELECTRONICS_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_ECC_ELECTRONICS_RQST_FAIL_BYTE = 0,
+ SES_CTRL_ECC_ELECTRONICS_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_ECC_ELECTRONICS_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_ECC_ELECTRONICS_SELECT_ELEMENT_BYTE = 1,
+ SES_CTRL_ECC_ELECTRONICS_SELECT_ELEMENT_MASK = 0x01,
+ SES_CTRL_ECC_ELECTRONICS_SELECT_ELEMENT_SHIFT = 0
+};
+
+#define GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_ecc_electronics, SES_CTRL_ECC_ELECTRONICS, \
+ LCASE, UCASE)
+GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(select_element, SELECT_ELEMENT)
+#undef GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS
+
+/*----------- SCSI Services Controller Electronics Control Element -----------*/
+struct ses_ctrl_scc_electronics {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_scc_electronics_field_data {
+ SES_CTRL_SCC_ELECTRONICS_RQST_IDENT_BYTE = 0,
+ SES_CTRL_SCC_ELECTRONICS_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_SCC_ELECTRONICS_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_SCC_ELECTRONICS_RQST_FAIL_BYTE = 0,
+ SES_CTRL_SCC_ELECTRONICS_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_SCC_ELECTRONICS_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_scc_electronics, SES_CTRL_SCC_ELECTRONICS, \
+ LCASE, UCASE)
+GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS
+
+/*--------------------- Nonvolatile Cache Control Element --------------------*/
+struct ses_ctrl_nv_cache {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_nv_cache_field_data {
+ SES_CTRL_NV_CACHE_RQST_IDENT_BYTE = 0,
+ SES_CTRL_NV_CACHE_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_NV_CACHE_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_NV_CACHE_RQST_FAIL_BYTE = 0,
+ SES_CTRL_NV_CACHE_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_NV_CACHE_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_NV_CACHE_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_nv_cache, SES_CTRL_NV_CACHE, LCASE, UCASE)
+GEN_SES_CTRL_NV_CACHE_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_NV_CACHE_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_NV_CACHE_ACCESSORS
+
+/*----------------- Invalid Operation Reason Control Element -----------------*/
+struct ses_ctrl_invalid_op_reason {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+/* There are no element specific fields currently defined in the spec. */
+
+/*--------------- Uninterruptible Power Supply Control Element ---------------*/
+struct ses_ctrl_ups {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_ups_field_data {
+ SES_CTRL_UPS_RQST_IDENT_BYTE = 2,
+ SES_CTRL_UPS_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_UPS_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_UPS_RQST_FAIL_BYTE = 2,
+ SES_CTRL_UPS_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_UPS_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_UPS_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_ups, SES_CTRL_UPS, LCASE, UCASE)
+GEN_SES_CTRL_UPS_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_UPS_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_UPS_ACCESSORS
+
+/*-------------------------- Display Control Element -------------------------*/
+struct ses_ctrl_display {
+ struct ses_ctrl_common common;
+ uint8_t bytes[1];
+ uint8_t display_character[2];
+};
+
+enum ses_ctrl_display_field_data {
+ SES_CTRL_DISPLAY_RQST_IDENT_BYTE = 0,
+ SES_CTRL_DISPLAY_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_DISPLAY_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_DISPLAY_RQST_FAIL_BYTE = 0,
+ SES_CTRL_DISPLAY_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_DISPLAY_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_DISPLAY_DISPLAY_MODE_BYTE = 0,
+ SES_CTRL_DISPLAY_DISPLAY_MODE_MASK = 0x03,
+ SES_CTRL_DISPLAY_DISPLAY_MODE_SHIFT = 6,
+ SES_CTRL_DISPLAY_DISPLAY_MODE_UNCHANGED = 0x0,
+ SES_CTRL_DISPLAY_DISPLAY_MODE_ESP = 0x1,
+ SES_CTRL_DISPLAY_DISPLAY_MODE_DC_FIELD = 0x2
+};
+
+#define GEN_SES_CTRL_DISPLAY_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_display, SES_CTRL_DISPLAY, LCASE, UCASE)
+GEN_SES_CTRL_DISPLAY_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_DISPLAY_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_DISPLAY_ACCESSORS(display_mode, DISPLAY_MODE)
+#undef GEN_SES_CTRL_DISPLAY_ACCESSORS
+
+/*----------------------- Key Pad Entry Control Element ----------------------*/
+struct ses_ctrl_key_pad_entry {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_key_pad_entry_field_data {
+ SES_CTRL_KEY_PAD_ENTRY_RQST_IDENT_BYTE = 0,
+ SES_CTRL_KEY_PAD_ENTRY_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_KEY_PAD_ENTRY_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_KEY_PAD_ENTRY_RQST_FAIL_BYTE = 0,
+ SES_CTRL_KEY_PAD_ENTRY_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_KEY_PAD_ENTRY_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_key_pad_entry, SES_CTRL_KEY_PAD_ENTRY, LCASE, UCASE)
+GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS
+
+/*------------------------- Enclosure Control Element ------------------------*/
+struct ses_ctrl_enclosure {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_enclosure_field_data {
+ SES_CTRL_ENCLOSURE_RQST_IDENT_BYTE = 0,
+ SES_CTRL_ENCLOSURE_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_ENCLOSURE_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_BYTE = 1,
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_MASK = 0xC0,
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_SHIFT = 6,
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_NONE = 0x0,
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_AFTER_DELAY = 0x1,
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_CANCEL = 0x2,
+
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_BYTE = 1,
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_MASK = 0x3F,
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_SHIFT = 0,
+ SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_MAX = 60,/*minutes*/
+
+ SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_BYTE = 2,
+ SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_MASK = 0xFC,
+ SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_SHIFT = 2,
+ SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_MAX_AUTO = 60,
+ SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_MANUAL = 63,
+
+ SES_CTRL_ENCLOSURE_RQST_FAIL_BYTE = 2,
+ SES_CTRL_ENCLOSURE_RQST_FAIL_MASK = 0x02,
+ SES_CTRL_ENCLOSURE_RQST_FAIL_SHIFT = 1,
+
+ SES_CTRL_ENCLOSURE_RQST_WARN_BYTE = 2,
+ SES_CTRL_ENCLOSURE_RQST_WARN_MASK = 0x01,
+ SES_CTRL_ENCLOSURE_RQST_WARN_SHIFT = 0
+};
+
+#define GEN_SES_CTRL_ENCLOSURE_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_enclosure, SES_CTRL_ENCLOSURE, LCASE, UCASE)
+GEN_SES_CTRL_ENCLOSURE_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_ENCLOSURE_ACCESSORS(power_cycle_rqst, POWER_CYCLE_RQST)
+GEN_SES_CTRL_ENCLOSURE_ACCESSORS(power_cycle_delay, POWER_CYCLE_DELAY)
+GEN_SES_CTRL_ENCLOSURE_ACCESSORS(power_off_duration, POWER_OFF_DURATION)
+GEN_SES_CTRL_ENCLOSURE_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_ENCLOSURE_ACCESSORS(rqst_warn, RQST_WARN)
+#undef GEN_SES_CTRL_ENCLOSURE_ACCESSORS
+
+/*------------------- SCSI Port/Transceiver Control Element ------------------*/
+struct ses_ctrl_scsi_port_or_xcvr {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_scsi_port_or_xcvr_field_data {
+ SES_CTRL_SCSI_PORT_OR_XCVR_RQST_IDENT_BYTE = 0,
+ SES_CTRL_SCSI_PORT_OR_XCVR_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_SCSI_PORT_OR_XCVR_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_SCSI_PORT_OR_XCVR_RQST_FAIL_BYTE = 0,
+ SES_CTRL_SCSI_PORT_OR_XCVR_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_SCSI_PORT_OR_XCVR_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_SCSI_PORT_OR_XCVR_DISABLE_BYTE = 2,
+ SES_CTRL_SCSI_PORT_OR_XCVR_DISABLE_MASK = 0x10,
+ SES_CTRL_SCSI_PORT_OR_XCVR_DISABLE_SHIFT = 4
+};
+
+#define GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_scsi_port_or_xcvr, SES_CTRL_SCSI_PORT_OR_XCVR,\
+ LCASE, UCASE)
+GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(disable, DISABLE)
+GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS
+
+/*------------------------- Language Control Element -------------------------*/
+struct ses_ctrl_language {
+ struct ses_ctrl_common common;
+ uint8_t bytes[1];
+ uint8_t language_code[2];
+};
+
+enum ses_ctrl_language_field_data {
+ SES_CTRL_LANGUAGE_RQST_IDENT_BYTE = 0,
+ SES_CTRL_LANGUAGE_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_LANGUAGE_RQST_IDENT_SHIFT = 7
+};
+
+#define GEN_SES_CTRL_LANGUAGE_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_language, SES_CTRL_LANGUAGE, LCASE, UCASE)
+GEN_SES_CTRL_LANGUAGE_ACCESSORS(rqst_ident, RQST_IDENT)
+#undef GEN_SES_CTRL_LANGUAGE_ACCESSORS
+
+/*-------------------- Communication Port Control Element --------------------*/
+struct ses_ctrl_comm_port {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_comm_port_field_data {
+ SES_CTRL_COMM_PORT_RQST_IDENT_BYTE = 0,
+ SES_CTRL_COMM_PORT_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_COMM_PORT_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_COMM_PORT_RQST_FAIL_BYTE = 0,
+ SES_CTRL_COMM_PORT_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_COMM_PORT_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_COMM_PORT_DISABLE_BYTE = 2,
+ SES_CTRL_COMM_PORT_DISABLE_MASK = 0x01,
+ SES_CTRL_COMM_PORT_DISABLE_SHIFT = 0
+};
+
+#define GEN_SES_CTRL_COMM_PORT_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_comm_port, SES_CTRL_COMM_PORT, LCASE, UCASE)
+GEN_SES_CTRL_COMM_PORT_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_COMM_PORT_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_COMM_PORT_ACCESSORS(disable, DISABLE)
+#undef GEN_SES_CTRL_COMM_PORT_ACCESSORS
+
+/*---------------------- Voltage Sensor Control Element ----------------------*/
+struct ses_ctrl_voltage_sensor {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_voltage_sensor_field_data {
+ SES_CTRL_VOLTAGE_SENSOR_RQST_IDENT_BYTE = 0,
+ SES_CTRL_VOLTAGE_SENSOR_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_VOLTAGE_SENSOR_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_VOLTAGE_SENSOR_RQST_FAIL_BYTE = 0,
+ SES_CTRL_VOLTAGE_SENSOR_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_VOLTAGE_SENSOR_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_voltage_sensor, SES_CTRL_VOLTAGE_SENSOR, \
+ LCASE, UCASE)
+GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS
+
+/*---------------------- Current Sensor Control Element ----------------------*/
+struct ses_ctrl_current_sensor {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_current_sensor_field_data {
+ SES_CTRL_CURRENT_SENSOR_RQST_IDENT_BYTE = 0,
+ SES_CTRL_CURRENT_SENSOR_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_CURRENT_SENSOR_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_CURRENT_SENSOR_RQST_FAIL_BYTE = 0,
+ SES_CTRL_CURRENT_SENSOR_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_CURRENT_SENSOR_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_current_sensor, SES_CTRL_CURRENT_SENSOR, \
+ LCASE, UCASE)
+GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS
+
+/*--------------------- SCSI Target Port Control Element ---------------------*/
+struct ses_ctrl_target_port {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_scsi_target_port_field_data {
+ SES_CTRL_TARGET_PORT_RQST_IDENT_BYTE = 0,
+ SES_CTRL_TARGET_PORT_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_TARGET_PORT_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_TARGET_PORT_RQST_FAIL_BYTE = 0,
+ SES_CTRL_TARGET_PORT_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_TARGET_PORT_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_TARGET_PORT_ENABLE_BYTE = 2,
+ SES_CTRL_TARGET_PORT_ENABLE_MASK = 0x01,
+ SES_CTRL_TARGET_PORT_ENABLE_SHIFT = 0
+};
+
+#define GEN_SES_CTRL_TARGET_PORT_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_target_port, SES_CTRL_TARGET_PORT, LCASE, UCASE)
+GEN_SES_CTRL_TARGET_PORT_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_TARGET_PORT_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_TARGET_PORT_ACCESSORS(enable, ENABLE)
+#undef GEN_SES_CTRL_TARGET_PORT_ACCESSORS
+
+/*-------------------- SCSI Initiator Port Control Element -------------------*/
+struct ses_ctrl_initiator_port {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_initiator_port_field_data {
+ SES_CTRL_INITIATOR_PORT_RQST_IDENT_BYTE = 0,
+ SES_CTRL_INITIATOR_PORT_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_INITIATOR_PORT_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_INITIATOR_PORT_RQST_FAIL_BYTE = 0,
+ SES_CTRL_INITIATOR_PORT_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_INITIATOR_PORT_RQST_FAIL_SHIFT = 6,
+
+ SES_CTRL_INITIATOR_PORT_ENABLE_BYTE = 2,
+ SES_CTRL_INITIATOR_PORT_ENABLE_MASK = 0x01,
+ SES_CTRL_INITIATOR_PORT_ENABLE_SHIFT = 0
+};
+
+#define GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_initiator_port, SES_CTRL_INITIATOR_PORT, \
+ LCASE, UCASE)
+GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(rqst_fail, RQST_FAIL)
+GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(enable, ENABLE)
+#undef GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS
+
+/*-------------------- Simple Subenclosure Control Element -------------------*/
+struct ses_ctrl_simple_subenc {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_simple_subenc_field_data {
+ SES_CTRL_SIMPlE_SUBSES_RQST_IDENT_BYTE = 0,
+ SES_CTRL_SIMPlE_SUBSES_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_SIMPlE_SUBSES_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_SIMPlE_SUBSES_RQST_FAIL_BYTE = 0,
+ SES_CTRL_SIMPlE_SUBSES_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_SIMPlE_SUBSES_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_simple_subenc, SES_CTRL_SIMPlE_SUBSES, \
+ LCASE, UCASE)
+GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS
+
+/*----------------------- SAS Expander Control Element -----------------------*/
+struct ses_ctrl_sas_expander {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_sas_expander_field_data {
+ SES_CTRL_SAS_EXPANDER_RQST_IDENT_BYTE = 0,
+ SES_CTRL_SAS_EXPANDER_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_SAS_EXPANDER_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_SAS_EXPANDER_RQST_FAIL_BYTE = 0,
+ SES_CTRL_SAS_EXPANDER_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_SAS_EXPANDER_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_sas_expander, SES_CTRL_SAS_EXPANDER, LCASE, UCASE)
+GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS
+
+/*----------------------- SAS Connector Control Element ----------------------*/
+struct ses_ctrl_sas_connector {
+ struct ses_ctrl_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_ctrl_sas_connector_field_data {
+ SES_CTRL_SAS_CONNECTOR_RQST_IDENT_BYTE = 0,
+ SES_CTRL_SAS_CONNECTOR_RQST_IDENT_MASK = 0x80,
+ SES_CTRL_SAS_CONNECTOR_RQST_IDENT_SHIFT = 7,
+
+ SES_CTRL_SAS_CONNECTOR_RQST_FAIL_BYTE = 2,
+ SES_CTRL_SAS_CONNECTOR_RQST_FAIL_MASK = 0x40,
+ SES_CTRL_SAS_CONNECTOR_RQST_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS(LCASE, UCASE) \
+ GEN_ACCESSORS(ses_ctrl_sas_connector, SES_CTRL_SAS_CONNECTOR, \
+ LCASE, UCASE)
+GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS(rqst_ident, RQST_IDENT)
+GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS(rqst_fail, RQST_FAIL)
+#undef GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS
+
+/*------------------------- Universal Control Element ------------------------*/
+union ses_ctrl_element {
+ struct ses_ctrl_common common;
+ struct ses_ctrl_dev_slot dev_slot;
+ struct ses_ctrl_array_dev_slot array_dev_slot;
+ struct ses_ctrl_power_supply power_supply;
+ struct ses_ctrl_cooling cooling;
+ struct ses_ctrl_temp_sensor temp_sensor;
+ struct ses_ctrl_door_lock door_lock;
+ struct ses_ctrl_audible_alarm audible_alarm;
+ struct ses_ctrl_ecc_electronics ecc_electronics;
+ struct ses_ctrl_scc_electronics scc_electronics;
+ struct ses_ctrl_nv_cache nv_cache;
+ struct ses_ctrl_invalid_op_reason invalid_op_reason;
+ struct ses_ctrl_ups ups;
+ struct ses_ctrl_display display;
+ struct ses_ctrl_key_pad_entry key_pad_entry;
+ struct ses_ctrl_scsi_port_or_xcvr scsi_port_or_xcvr;
+ struct ses_ctrl_language language;
+ struct ses_ctrl_comm_port comm_port;
+ struct ses_ctrl_voltage_sensor voltage_sensor;
+ struct ses_ctrl_current_sensor current_sensor;
+ struct ses_ctrl_target_port target_port;
+ struct ses_ctrl_initiator_port initiator_port;
+ struct ses_ctrl_simple_subenc simple_subenc;
+ struct ses_ctrl_sas_expander sas_expander;
+ struct ses_ctrl_sas_connector sas_connector;
+};
+
+/*--------------------- SCSI SES Control Diagnostic Page ---------------------*/
+struct ses_ctrl_page {
+ struct ses_page_hdr hdr;
+ union ses_ctrl_element elements[];
+};
+
+enum ses_ctrl_page_field_data {
+ SES_CTRL_PAGE_INFO_MASK = 0x08,
+ SES_CTRL_PAGE_INFO_SHIFT = 3,
+
+ SES_CTRL_PAGE_NON_CRIT_MASK = 0x04,
+ SES_CTRL_PAGE_NON_CRIT_SHIFT = 2,
+
+ SES_CTRL_PAGE_CRIT_MASK = 0x02,
+ SES_CTRL_PAGE_CRIT_SHIFT = 1,
+
+ SES_CTRL_PAGE_UNRECOV_MASK = 0x01,
+ SES_CTRL_PAGE_UNRECOV_SHIFT = 0
+};
+
+#define GEN_SES_CTRL_PAGE_ACCESSORS(LCASE, UCASE) \
+ GEN_HDR_ACCESSORS(ses_ctrl_page, SES_CTRL_PAGE, LCASE, UCASE)
+
+GEN_SES_CTRL_PAGE_ACCESSORS(info, INFO)
+GEN_SES_CTRL_PAGE_ACCESSORS(non_crit, NON_CRIT)
+GEN_SES_CTRL_PAGE_ACCESSORS(crit, CRIT)
+GEN_SES_CTRL_PAGE_ACCESSORS(unrecov, UNRECOV)
+#undef GEN_SES_CTRL_PAGE_ACCESSORS
+
+/*================= SCSI SES Status Diagnostic Page Structures ===============*/
+struct ses_status_common {
+ uint8_t bytes[1];
+};
+
+enum ses_status_common_field_data {
+ SES_STATUS_COMMON_PRDFAIL_BYTE = 0,
+ SES_STATUS_COMMON_PRDFAIL_MASK = 0x40,
+ SES_STATUS_COMMON_PRDFAIL_SHIFT = 6,
+
+ SES_STATUS_COMMON_DISABLED_BYTE = 0,
+ SES_STATUS_COMMON_DISABLED_MASK = 0x20,
+ SES_STATUS_COMMON_DISABLED_SHIFT = 5,
+
+ SES_STATUS_COMMON_SWAP_BYTE = 0,
+ SES_STATUS_COMMON_SWAP_MASK = 0x10,
+ SES_STATUS_COMMON_SWAP_SHIFT = 4,
+
+ SES_STATUS_COMMON_ELEMENT_STATUS_CODE_BYTE = 0,
+ SES_STATUS_COMMON_ELEMENT_STATUS_CODE_MASK = 0x0F,
+ SES_STATUS_COMMON_ELEMENT_STATUS_CODE_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_COMMON_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_common, SES_STATUS_COMMON, LCASE, UCASE)
+
+GEN_SES_STATUS_COMMON_ACCESSORS(prdfail, PRDFAIL)
+GEN_SES_STATUS_COMMON_ACCESSORS(disabled, DISABLED)
+GEN_SES_STATUS_COMMON_ACCESSORS(swap, SWAP)
+GEN_SES_STATUS_COMMON_ACCESSORS(element_status_code, ELEMENT_STATUS_CODE)
+#undef GEN_SES_STATUS_COMMON_ACCESSORS
+
+/*------------------------- Device Slot Status Element -----------------------*/
+struct ses_status_dev_slot {
+ struct ses_status_common common;
+ uint8_t slot_address;
+ uint8_t bytes[2];
+};
+
+enum ses_status_dev_slot_field_data {
+ SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_A_BYTE = 0,
+ SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_A_MASK = 0x80,
+ SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_A_SHIFT = 7,
+
+ SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_BYTE = 0,
+ SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_MASK = 0x40,
+ SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_SHIFT = 6,
+
+ SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_A_BYTE = 0,
+ SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_A_MASK = 0x20,
+ SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_A_SHIFT = 5,
+
+ SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_B_BYTE = 0,
+ SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_B_MASK = 0x10,
+ SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_B_SHIFT = 4,
+
+ SES_STATUS_DEV_SLOT_INSERT_READY_BYTE = 0,
+ SES_STATUS_DEV_SLOT_INSERT_READY_MASK = 0x08,
+ SES_STATUS_DEV_SLOT_INSERT_READY_SHIFT = 3,
+
+ SES_STATUS_DEV_SLOT_REMOVE_BYTE = 0,
+ SES_STATUS_DEV_SLOT_REMOVE_MASK = 0x04,
+ SES_STATUS_DEV_SLOT_REMOVE_SHIFT = 2,
+
+ SES_STATUS_DEV_SLOT_IDENT_BYTE = 0,
+ SES_STATUS_DEV_SLOT_IDENT_MASK = 0x02,
+ SES_STATUS_DEV_SLOT_IDENT_SHIFT = 1,
+
+ SES_STATUS_DEV_SLOT_REPORT_BYTE = 0,
+ SES_STATUS_DEV_SLOT_REPORT_MASK = 0x01,
+ SES_STATUS_DEV_SLOT_REPORT_SHIFT = 0,
+
+ SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_B_BYTE = 1,
+ SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_B_MASK = 0x80,
+ SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_B_SHIFT = 7,
+
+ SES_STATUS_DEV_SLOT_FAULT_SENSED_BYTE = 1,
+ SES_STATUS_DEV_SLOT_FAULT_SENSED_MASK = 0x40,
+ SES_STATUS_DEV_SLOT_FAULT_SENSED_SHIFT = 6,
+
+ SES_STATUS_DEV_SLOT_FAULT_REQUESTED_BYTE = 1,
+ SES_STATUS_DEV_SLOT_FAULT_REQUESTED_MASK = 0x20,
+ SES_STATUS_DEV_SLOT_FAULT_REQUESTED_SHIFT = 5,
+
+ SES_STATUS_DEV_SLOT_DEVICE_OFF_BYTE = 1,
+ SES_STATUS_DEV_SLOT_DEVICE_OFF_MASK = 0x10,
+ SES_STATUS_DEV_SLOT_DEVICE_OFF_SHIFT = 4,
+
+ SES_STATUS_DEV_SLOT_BYPED_A_BYTE = 1,
+ SES_STATUS_DEV_SLOT_BYPED_A_MASK = 0x08,
+ SES_STATUS_DEV_SLOT_BYPED_A_SHIFT = 3,
+
+ SES_STATUS_DEV_SLOT_BYPED_B_BYTE = 1,
+ SES_STATUS_DEV_SLOT_BYPED_B_MASK = 0x04,
+ SES_STATUS_DEV_SLOT_BYPED_B_SHIFT = 2,
+
+ SES_STATUS_DEV_SLOT_DEVICE_BYPED_A_BYTE = 1,
+ SES_STATUS_DEV_SLOT_DEVICE_BYPED_A_MASK = 0x02,
+ SES_STATUS_DEV_SLOT_DEVICE_BYPED_A_SHIFT = 1,
+
+ SES_STATUS_DEV_SLOT_DEVICE_BYPED_B_BYTE = 1,
+ SES_STATUS_DEV_SLOT_DEVICE_BYPED_B_MASK = 0x01,
+ SES_STATUS_DEV_SLOT_DEVICE_BYPED_B_SHIFT = 0
+};
+#define GEN_SES_STATUS_DEV_SLOT_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_dev_slot, SES_STATUS_DEV_SLOT, LCASE, UCASE)
+
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(app_client_byped_a, APP_CLIENT_BYPED_A)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(do_not_remove, DO_NOT_REMOVE)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_a, ENCLOSURE_BYPED_A)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_b, ENCLOSURE_BYPED_B)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(insert_ready, INSERT_READY)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(remove, REMOVE)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(report, REPORT)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(app_client_byped_b, APP_CLIENT_BYPED_B)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(fault_sensed, FAULT_SENSED)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(fault_requested, FAULT_REQUESTED)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(device_off, DEVICE_OFF)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(byped_a, BYPED_A)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(byped_b, BYPED_B)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(device_byped_a, DEVICE_BYPED_A)
+GEN_SES_STATUS_DEV_SLOT_ACCESSORS(device_byped_b, DEVICE_BYPED_B)
+#undef GEN_SES_STATUS_DEV_SLOT_ACCESSORS
+
+/*---------------------- Array Device Slot Status Element --------------------*/
+struct ses_status_array_dev_slot {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_array_dev_slot_field_data {
+ SES_STATUS_ARRAY_DEV_SLOT_OK_BYTE = 0,
+ SES_STATUS_ARRAY_DEV_SLOT_OK_MASK = 0x80,
+ SES_STATUS_ARRAY_DEV_SLOT_OK_SHIFT = 7,
+
+ SES_STATUS_ARRAY_DEV_SLOT_RSVD_DEVICE_BYTE = 0,
+ SES_STATUS_ARRAY_DEV_SLOT_RSVD_DEVICE_MASK = 0x40,
+ SES_STATUS_ARRAY_DEV_SLOT_RSVD_DEVICE_SHIFT = 6,
+
+ SES_STATUS_ARRAY_DEV_SLOT_HOT_SPARE_BYTE = 0,
+ SES_STATUS_ARRAY_DEV_SLOT_HOT_SPARE_MASK = 0x20,
+ SES_STATUS_ARRAY_DEV_SLOT_HOT_SPARE_SHIFT = 5,
+
+ SES_STATUS_ARRAY_DEV_SLOT_CONS_CHECK_BYTE = 0,
+ SES_STATUS_ARRAY_DEV_SLOT_CONS_CHECK_MASK = 0x10,
+ SES_STATUS_ARRAY_DEV_SLOT_CONS_CHECK_SHIFT = 4,
+
+ SES_STATUS_ARRAY_DEV_SLOT_IN_CRIT_ARRAY_BYTE = 0,
+ SES_STATUS_ARRAY_DEV_SLOT_IN_CRIT_ARRAY_MASK = 0x08,
+ SES_STATUS_ARRAY_DEV_SLOT_IN_CRIT_ARRAY_SHIFT = 3,
+
+ SES_STATUS_ARRAY_DEV_SLOT_IN_FAILED_ARRAY_BYTE = 0,
+ SES_STATUS_ARRAY_DEV_SLOT_IN_FAILED_ARRAY_MASK = 0x04,
+ SES_STATUS_ARRAY_DEV_SLOT_IN_FAILED_ARRAY_SHIFT = 2,
+
+ SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_BYTE = 0,
+ SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_MASK = 0x02,
+ SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_SHIFT = 1,
+
+ SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_ABORT_BYTE = 0,
+ SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_ABORT_MASK = 0x01,
+ SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_ABORT_SHIFT = 0
+
+ /*
+ * The remaining fields are identical to the device
+ * slot element type. Access them through the device slot
+ * element type and its accessors.
+ */
+};
+#define GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_array_dev_slot, SES_STATUS_ARRAY_DEV_SLOT, \
+ LCASE, UCASE)
+GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(ok, OK)
+GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(rsvd_device, RSVD_DEVICE)
+GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(hot_spare, HOT_SPARE)
+GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(cons_check, CONS_CHECK)
+GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(in_crit_array, IN_CRIT_ARRAY)
+GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(in_failed_array, IN_FAILED_ARRAY)
+GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(rebuild_remap, REBUILD_REMAP)
+GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(rebuild_remap_abort,
+ REBUILD_REMAP_ABORT)
+#undef GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS
+
+/*----------------------- Power Supply Status Element ------------------------*/
+struct ses_status_power_supply {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+enum ses_status_power_supply_field_data {
+ SES_STATUS_POWER_SUPPLY_IDENT_BYTE = 0,
+ SES_STATUS_POWER_SUPPLY_IDENT_MASK = 0x80,
+ SES_STATUS_POWER_SUPPLY_IDENT_SHIFT = 7,
+
+ SES_STATUS_POWER_SUPPLY_DC_OVER_VOLTAGE_BYTE = 1,
+ SES_STATUS_POWER_SUPPLY_DC_OVER_VOLTAGE_MASK = 0x08,
+ SES_STATUS_POWER_SUPPLY_DC_OVER_VOLTAGE_SHIFT = 3,
+
+ SES_STATUS_POWER_SUPPLY_DC_UNDER_VOLTAGE_BYTE = 1,
+ SES_STATUS_POWER_SUPPLY_DC_UNDER_VOLTAGE_MASK = 0x04,
+ SES_STATUS_POWER_SUPPLY_DC_UNDER_VOLTAGE_SHIFT = 2,
+
+ SES_STATUS_POWER_SUPPLY_DC_OVER_CURRENT_BYTE = 1,
+ SES_STATUS_POWER_SUPPLY_DC_OVER_CURRENT_MASK = 0x02,
+ SES_STATUS_POWER_SUPPLY_DC_OVER_CURRENT_SHIFT = 1,
+
+ SES_STATUS_POWER_SUPPLY_HOT_SWAP_BYTE = 2,
+ SES_STATUS_POWER_SUPPLY_HOT_SWAP_MASK = 0x80,
+ SES_STATUS_POWER_SUPPLY_HOT_SWAP_SHIFT = 7,
+
+ SES_STATUS_POWER_SUPPLY_FAIL_BYTE = 2,
+ SES_STATUS_POWER_SUPPLY_FAIL_MASK = 0x40,
+ SES_STATUS_POWER_SUPPLY_FAIL_SHIFT = 6,
+
+ SES_STATUS_POWER_SUPPLY_REQUESTED_ON_BYTE = 2,
+ SES_STATUS_POWER_SUPPLY_REQUESTED_ON_MASK = 0x20,
+ SES_STATUS_POWER_SUPPLY_REQUESTED_ON_SHIFT = 5,
+
+ SES_STATUS_POWER_SUPPLY_OFF_BYTE = 2,
+ SES_STATUS_POWER_SUPPLY_OFF_MASK = 0x10,
+ SES_STATUS_POWER_SUPPLY_OFF_SHIFT = 4,
+
+ SES_STATUS_POWER_SUPPLY_OVERTMP_FAIL_BYTE = 2,
+ SES_STATUS_POWER_SUPPLY_OVERTMP_FAIL_MASK = 0x08,
+ SES_STATUS_POWER_SUPPLY_OVERTMP_FAIL_SHIFT = 3,
+
+ SES_STATUS_POWER_SUPPLY_TEMP_WARN_BYTE = 2,
+ SES_STATUS_POWER_SUPPLY_TEMP_WARN_MASK = 0x04,
+ SES_STATUS_POWER_SUPPLY_TEMP_WARN_SHIFT = 2,
+
+ SES_STATUS_POWER_SUPPLY_AC_FAIL_BYTE = 2,
+ SES_STATUS_POWER_SUPPLY_AC_FAIL_MASK = 0x02,
+ SES_STATUS_POWER_SUPPLY_AC_FAIL_SHIFT = 1,
+
+ SES_STATUS_POWER_SUPPLY_DC_FAIL_BYTE = 2,
+ SES_STATUS_POWER_SUPPLY_DC_FAIL_MASK = 0x01,
+ SES_STATUS_POWER_SUPPLY_DC_FAIL_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_power_supply, SES_STATUS_POWER_SUPPLY, LCASE, UCASE)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_over_voltage, DC_OVER_VOLTAGE)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_under_voltage, DC_UNDER_VOLTAGE)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_over_current, DC_OVER_CURRENT)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(hot_swap, HOT_SWAP)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(requested_on, REQUESTED_ON)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(off, OFF)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(overtmp_fail, OVERTMP_FAIL)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(temp_warn, TEMP_WARN)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(ac_fail, AC_FAIL)
+GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_fail, DC_FAIL)
+#undef GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS
+
+/*-------------------------- Cooling Status Element --------------------------*/
+struct ses_status_cooling {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_cooling_field_data {
+ SES_STATUS_COOLING_IDENT_BYTE = 0,
+ SES_STATUS_COOLING_IDENT_MASK = 0x80,
+ SES_STATUS_COOLING_IDENT_SHIFT = 7,
+
+ SES_STATUS_COOLING_ACTUAL_FAN_SPEED_MSB_BYTE = 0,
+ SES_STATUS_COOLING_ACTUAL_FAN_SPEED_MSB_MASK = 0x07,
+ SES_STATUS_COOLING_ACTUAL_FAN_SPEED_MSB_SHIFT = 0,
+
+ SES_STATUS_COOLING_ACTUAL_FAN_SPEED_LSB_BYTE = 1,
+ SES_STATUS_COOLING_ACTUAL_FAN_SPEED_LSB_MASK = 0xFF,
+ SES_STATUS_COOLING_ACTUAL_FAN_SPEED_LSB_SHIFT = 0,
+
+ SES_STATUS_COOLING_HOT_SWAP_BYTE = 2,
+ SES_STATUS_COOLING_HOT_SWAP_MASK = 0x40,
+ SES_STATUS_COOLING_HOT_SWAP_SHIFT = 6,
+
+ SES_STATUS_COOLING_FAIL_BYTE = 2,
+ SES_STATUS_COOLING_FAIL_MASK = 0x40,
+ SES_STATUS_COOLING_FAIL_SHIFT = 6,
+
+ SES_STATUS_COOLING_REQUESTED_ON_BYTE = 2,
+ SES_STATUS_COOLING_REQUESTED_ON_MASK = 0x20,
+ SES_STATUS_COOLING_REQUESTED_ON_SHIFT = 5,
+
+ SES_STATUS_COOLING_OFF_BYTE = 2,
+ SES_STATUS_COOLING_OFF_MASK = 0x20,
+ SES_STATUS_COOLING_OFF_SHIFT = 5,
+
+ SES_STATUS_COOLING_ACTUAL_SPEED_CODE_BYTE = 2,
+ SES_STATUS_COOLING_ACTUAL_SPEED_CODE_MASK = 0x07,
+ SES_STATUS_COOLING_ACTUAL_SPEED_CODE_SHIFT = 2,
+ SES_STATUS_COOLING_ACTUAL_SPEED_CODE_STOPPED = 0x00,
+ SES_STATUS_COOLING_ACTUAL_SPEED_CODE_LOWEST = 0x01,
+ SES_STATUS_COOLING_ACTUAL_SPEED_CODE_HIGHEST = 0x07
+};
+
+#define GEN_SES_STATUS_COOLING_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_cooling, SES_STATUS_COOLING, LCASE, UCASE)
+GEN_SES_STATUS_COOLING_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_COOLING_ACCESSORS(actual_fan_speed_msb, ACTUAL_FAN_SPEED_MSB)
+GEN_SES_STATUS_COOLING_ACCESSORS(actual_fan_speed_lsb, ACTUAL_FAN_SPEED_LSB)
+GEN_SES_STATUS_COOLING_ACCESSORS(hot_swap, HOT_SWAP)
+GEN_SES_STATUS_COOLING_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_COOLING_ACCESSORS(requested_on, REQUESTED_ON)
+GEN_SES_STATUS_COOLING_ACCESSORS(off, OFF)
+GEN_SES_STATUS_COOLING_ACCESSORS(actual_speed_code, ACTUAL_SPEED_CODE)
+#undef GEN_SES_STATUS_COOLING_ACCESSORS
+
+static inline int
+ses_status_cooling_get_actual_fan_speed(struct ses_status_cooling *elem)
+{
+ return (ses_status_cooling_get_actual_fan_speed_msb(elem) << 8
+ | ses_status_cooling_get_actual_fan_speed_lsb(elem));
+}
+
+/*-------------------- Temperature Sensor Status Element ---------------------*/
+struct ses_status_temp_sensor {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_temp_sensor_field_data {
+ SES_STATUS_TEMP_SENSOR_IDENT_BYTE = 0,
+ SES_STATUS_TEMP_SENSOR_IDENT_MASK = 0x80,
+ SES_STATUS_TEMP_SENSOR_IDENT_SHIFT = 7,
+
+ SES_STATUS_TEMP_SENSOR_FAIL_BYTE = 0,
+ SES_STATUS_TEMP_SENSOR_FAIL_MASK = 0x40,
+ SES_STATUS_TEMP_SENSOR_FAIL_SHIFT = 6,
+
+ SES_STATUS_TEMP_SENSOR_TEMPERATURE_BYTE = 1,
+ SES_STATUS_TEMP_SENSOR_TEMPERATURE_MASK = 0xFF,
+ SES_STATUS_TEMP_SENSOR_TEMPERATURE_SHIFT = 0,
+
+ SES_STATUS_TEMP_SENSOR_OT_FAILURE_BYTE = 2,
+ SES_STATUS_TEMP_SENSOR_OT_FAILURE_MASK = 0x08,
+ SES_STATUS_TEMP_SENSOR_OT_FAILURE_SHIFT = 3,
+
+ SES_STATUS_TEMP_SENSOR_OT_WARNING_BYTE = 2,
+ SES_STATUS_TEMP_SENSOR_OT_WARNING_MASK = 0x04,
+ SES_STATUS_TEMP_SENSOR_OT_WARNING_SHIFT = 2,
+
+ SES_STATUS_TEMP_SENSOR_UT_FAILURE_BYTE = 2,
+ SES_STATUS_TEMP_SENSOR_UT_FAILURE_MASK = 0x02,
+ SES_STATUS_TEMP_SENSOR_UT_FAILURE_SHIFT = 1,
+
+ SES_STATUS_TEMP_SENSOR_UT_WARNING_BYTE = 2,
+ SES_STATUS_TEMP_SENSOR_UT_WARNING_MASK = 0x01,
+ SES_STATUS_TEMP_SENSOR_UT_WARNING_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_temp_sensor, SES_STATUS_TEMP_SENSOR, LCASE, UCASE)
+GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(temperature, TEMPERATURE)
+GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ot_failure, OT_FAILURE)
+GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ot_warning, OT_WARNING)
+GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ut_failure, UT_FAILURE)
+GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ut_warning, UT_WARNING)
+#undef GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS
+
+/*------------------------- Door Lock Status Element -------------------------*/
+struct ses_status_door_lock {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_door_lock_field_data {
+ SES_STATUS_DOOR_LOCK_IDENT_BYTE = 0,
+ SES_STATUS_DOOR_LOCK_IDENT_MASK = 0x80,
+ SES_STATUS_DOOR_LOCK_IDENT_SHIFT = 7,
+
+ SES_STATUS_DOOR_LOCK_FAIL_BYTE = 0,
+ SES_STATUS_DOOR_LOCK_FAIL_MASK = 0x40,
+ SES_STATUS_DOOR_LOCK_FAIL_SHIFT = 6,
+
+ SES_STATUS_DOOR_LOCK_UNLOCKED_BYTE = 2,
+ SES_STATUS_DOOR_LOCK_UNLOCKED_MASK = 0x01,
+ SES_STATUS_DOOR_LOCK_UNLOCKED_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_door_lock, SES_STATUS_DOOR_LOCK, LCASE, UCASE)
+GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(unlocked, UNLOCKED)
+#undef GEN_SES_STATUS_DOOR_LOCK_ACCESSORS
+
+/*----------------------- Audible Alarm Status Element -----------------------*/
+struct ses_status_audible_alarm {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_audible_alarm_field_data {
+ SES_STATUS_AUDIBLE_ALARM_IDENT_BYTE = 0,
+ SES_STATUS_AUDIBLE_ALARM_IDENT_MASK = 0x80,
+ SES_STATUS_AUDIBLE_ALARM_IDENT_SHIFT = 7,
+
+ SES_STATUS_AUDIBLE_ALARM_FAIL_BYTE = 0,
+ SES_STATUS_AUDIBLE_ALARM_FAIL_MASK = 0x40,
+ SES_STATUS_AUDIBLE_ALARM_FAIL_SHIFT = 6,
+
+ SES_STATUS_AUDIBLE_ALARM_RQST_MUTE_BYTE = 2,
+ SES_STATUS_AUDIBLE_ALARM_RQST_MUTE_MASK = 0x80,
+ SES_STATUS_AUDIBLE_ALARM_RQST_MUTE_SHIFT = 7,
+
+ SES_STATUS_AUDIBLE_ALARM_MUTED_BYTE = 2,
+ SES_STATUS_AUDIBLE_ALARM_MUTED_MASK = 0x40,
+ SES_STATUS_AUDIBLE_ALARM_MUTED_SHIFT = 6,
+
+ SES_STATUS_AUDIBLE_ALARM_REMIND_BYTE = 2,
+ SES_STATUS_AUDIBLE_ALARM_REMIND_MASK = 0x10,
+ SES_STATUS_AUDIBLE_ALARM_REMIND_SHIFT = 4,
+
+ SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_BYTE = 2,
+ SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_MASK = 0x0F,
+ SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_SHIFT = 0,
+ SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_INFO = 0x08,
+ SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_NON_CRIT = 0x04,
+ SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_CRIT = 0x02,
+ SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_UNRECOV = 0x01
+};
+
+#define GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_audible_alarm, SES_STATUS_AUDIBLE_ALARM, LCASE, UCASE)
+GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(rqst_mute, RQST_MUTE)
+GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(muted, MUTED)
+GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(remind, REMIND)
+GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(tone_indicator, TONE_INDICATOR)
+#undef GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS
+
+/*---------- Enclosure Services Statusler Electronics Status Element ---------*/
+struct ses_status_ecc_electronics {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_ecc_electronics_field_data {
+ SES_STATUS_ECC_ELECTRONICS_IDENT_BYTE = 0,
+ SES_STATUS_ECC_ELECTRONICS_IDENT_MASK = 0x80,
+ SES_STATUS_ECC_ELECTRONICS_IDENT_SHIFT = 7,
+
+ SES_STATUS_ECC_ELECTRONICS_FAIL_BYTE = 0,
+ SES_STATUS_ECC_ELECTRONICS_FAIL_MASK = 0x40,
+ SES_STATUS_ECC_ELECTRONICS_FAIL_SHIFT = 6,
+
+ SES_STATUS_ECC_ELECTRONICS_REPORT_BYTE = 1,
+ SES_STATUS_ECC_ELECTRONICS_REPORT_MASK = 0x01,
+ SES_STATUS_ECC_ELECTRONICS_REPORT_SHIFT = 0,
+
+ SES_STATUS_ECC_ELECTRONICS_HOT_SWAP_BYTE = 2,
+ SES_STATUS_ECC_ELECTRONICS_HOT_SWAP_MASK = 0x80,
+ SES_STATUS_ECC_ELECTRONICS_HOT_SWAP_SHIFT = 7
+};
+
+#define GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_ecc_electronics, SES_STATUS_ECC_ELECTRONICS, \
+ LCASE, UCASE)
+GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(report, REPORT)
+GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(hot_swap, HOT_SWAP)
+#undef GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS
+
+/*------------ SCSI Services Statusler Electronics Status Element ------------*/
+struct ses_status_scc_electronics {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_scc_electronics_field_data {
+ SES_STATUS_SCC_ELECTRONICS_IDENT_BYTE = 0,
+ SES_STATUS_SCC_ELECTRONICS_IDENT_MASK = 0x80,
+ SES_STATUS_SCC_ELECTRONICS_IDENT_SHIFT = 7,
+
+ SES_STATUS_SCC_ELECTRONICS_FAIL_BYTE = 0,
+ SES_STATUS_SCC_ELECTRONICS_FAIL_MASK = 0x40,
+ SES_STATUS_SCC_ELECTRONICS_FAIL_SHIFT = 6,
+
+ SES_STATUS_SCC_ELECTRONICS_REPORT_BYTE = 1,
+ SES_STATUS_SCC_ELECTRONICS_REPORT_MASK = 0x01,
+ SES_STATUS_SCC_ELECTRONICS_REPORT_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_scc_electronics, SES_STATUS_SCC_ELECTRONICS, \
+ LCASE, UCASE)
+GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(report, REPORT)
+#undef GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS
+
+/*--------------------- Nonvolatile Cache Status Element ---------------------*/
+struct ses_status_nv_cache {
+ struct ses_status_common common;
+ uint8_t bytes[1];
+ uint8_t cache_size[2];
+};
+
+enum ses_status_nv_cache_field_data {
+ SES_STATUS_NV_CACHE_IDENT_BYTE = 0,
+ SES_STATUS_NV_CACHE_IDENT_MASK = 0x80,
+ SES_STATUS_NV_CACHE_IDENT_SHIFT = 7,
+
+ SES_STATUS_NV_CACHE_FAIL_BYTE = 0,
+ SES_STATUS_NV_CACHE_FAIL_MASK = 0x40,
+ SES_STATUS_NV_CACHE_FAIL_SHIFT = 6,
+
+ SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_BYTE = 0,
+ SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_MASK = 0x03,
+ SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_SHIFT = 0,
+ SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_BYTES = 0x0,
+ SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_KBYTES = 0x1,
+ SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_MBYTES = 0x2,
+ SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_GBYTES = 0x3
+};
+
+#define GEN_SES_STATUS_NV_CACHE_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_nv_cache, SES_STATUS_NV_CACHE, LCASE, UCASE)
+GEN_SES_STATUS_NV_CACHE_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_NV_CACHE_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_NV_CACHE_ACCESSORS(size_multiplier, SIZE_MULTIPLIER)
+#undef GEN_SES_STATUS_NV_CACHE_ACCESSORS
+
+static inline uintmax_t
+ses_status_nv_cache_get_cache_size(struct ses_status_nv_cache *elem)
+{
+ uintmax_t cache_size;
+ int multiplier;
+
+ /* Multiplier is in units of 2^10 */
+ cache_size = scsi_2btoul(elem->cache_size);
+ multiplier = 10 * ses_status_nv_cache_get_size_multiplier(elem);
+ return (cache_size << multiplier);
+}
+
+/*----------------- Invalid Operation Reason Status Element ------------------*/
+struct ses_status_invalid_op_reason {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_invalid_op_field_data {
+ SES_STATUS_INVALID_OP_REASON_TYPE_BYTE = 0,
+ SES_STATUS_INVALID_OP_REASON_TYPE_MASK = 0xC0,
+ SES_STATUS_INVALID_OP_REASON_TYPE_SHIFT = 6,
+ SES_STATUS_INVALID_OP_REASON_TYPE_PC_ERROR = 0x00,
+ SES_STATUS_INVALID_OP_REASON_TYPE_PF_ERROR = 0x01,
+ SES_STATUS_INVALID_OP_REASON_TYPE_VS_ERROR = 0x03,
+
+ SES_STATUS_INVALID_OP_REASON_PC_ERROR_PC_NOT_SUPPORTED_BYTE = 0,
+ SES_STATUS_INVALID_OP_REASON_PC_ERROR_PC_NOT_SUPPORTED_MASK = 0x01,
+ SES_STATUS_INVALID_OP_REASON_PC_ERROR_PC_NOT_SUPPORTED_SHIFT = 0,
+
+ SES_STATUS_INVALID_OP_REASON_PF_ERROR_BIT_NUMBER_BYTE = 0,
+ SES_STATUS_INVALID_OP_REASON_PF_ERROR_BIT_NUMBER_MASK = 0x03,
+ SES_STATUS_INVALID_OP_REASON_PF_ERROR_BIT_NUMBER_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_invalid_op_reason, SES_STATUS_INVALID_OP_REASON, \
+ LCASE, UCASE)
+GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(type, TYPE)
+GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(pc_error_pc_not_supported,
+ PC_ERROR_PC_NOT_SUPPORTED)
+GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(pf_error_bit_number,
+ PF_ERROR_BIT_NUMBER)
+#undef GEN_SES_STATUS_INVALID_OP_ACCESSORS
+
+/*--------------- Uninterruptible Power Supply Status Element ----------------*/
+struct ses_status_ups {
+ struct ses_status_common common;
+ /* Minutes of remaining capacity. */
+ uint8_t battery_status;
+ uint8_t bytes[2];
+};
+
+enum ses_status_ups_field_data {
+ SES_STATUS_UPS_AC_LO_BYTE = 0,
+ SES_STATUS_UPS_AC_LO_MASK = 0x80,
+ SES_STATUS_UPS_AC_LO_SHIFT = 7,
+
+ SES_STATUS_UPS_AC_HI_BYTE = 0,
+ SES_STATUS_UPS_AC_HI_MASK = 0x40,
+ SES_STATUS_UPS_AC_HI_SHIFT = 6,
+
+ SES_STATUS_UPS_AC_QUAL_BYTE = 0,
+ SES_STATUS_UPS_AC_QUAL_MASK = 0x20,
+ SES_STATUS_UPS_AC_QUAL_SHIFT = 5,
+
+ SES_STATUS_UPS_AC_FAIL_BYTE = 0,
+ SES_STATUS_UPS_AC_FAIL_MASK = 0x10,
+ SES_STATUS_UPS_AC_FAIL_SHIFT = 4,
+
+ SES_STATUS_UPS_DC_FAIL_BYTE = 0,
+ SES_STATUS_UPS_DC_FAIL_MASK = 0x08,
+ SES_STATUS_UPS_DC_FAIL_SHIFT = 3,
+
+ SES_STATUS_UPS_UPS_FAIL_BYTE = 0,
+ SES_STATUS_UPS_UPS_FAIL_MASK = 0x04,
+ SES_STATUS_UPS_UPS_FAIL_SHIFT = 2,
+
+ SES_STATUS_UPS_WARN_BYTE = 0,
+ SES_STATUS_UPS_WARN_MASK = 0x02,
+ SES_STATUS_UPS_WARN_SHIFT = 1,
+
+ SES_STATUS_UPS_INTF_FAIL_BYTE = 0,
+ SES_STATUS_UPS_INTF_FAIL_MASK = 0x01,
+ SES_STATUS_UPS_INTF_FAIL_SHIFT = 0,
+
+ SES_STATUS_UPS_IDENT_BYTE = 0,
+ SES_STATUS_UPS_IDENT_MASK = 0x80,
+ SES_STATUS_UPS_IDENT_SHIFT = 7,
+
+ SES_STATUS_UPS_FAIL_BYTE = 1,
+ SES_STATUS_UPS_FAIL_MASK = 0x40,
+ SES_STATUS_UPS_FAIL_SHIFT = 6,
+
+ SES_STATUS_UPS_BATT_FAIL_BYTE = 1,
+ SES_STATUS_UPS_BATT_FAIL_MASK = 0x02,
+ SES_STATUS_UPS_BATT_FAIL_SHIFT = 1,
+
+ SES_STATUS_UPS_BPF_BYTE = 1,
+ SES_STATUS_UPS_BPF_MASK = 0x01,
+ SES_STATUS_UPS_BPF_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_UPS_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_ups, SES_STATUS_UPS, LCASE, UCASE)
+GEN_SES_STATUS_UPS_ACCESSORS(ac_lo, AC_LO)
+GEN_SES_STATUS_UPS_ACCESSORS(ac_hi, AC_HI)
+GEN_SES_STATUS_UPS_ACCESSORS(ac_qual, AC_QUAL)
+GEN_SES_STATUS_UPS_ACCESSORS(ac_fail, AC_FAIL)
+GEN_SES_STATUS_UPS_ACCESSORS(dc_fail, DC_FAIL)
+GEN_SES_STATUS_UPS_ACCESSORS(ups_fail, UPS_FAIL)
+GEN_SES_STATUS_UPS_ACCESSORS(warn, WARN)
+GEN_SES_STATUS_UPS_ACCESSORS(intf_fail, INTF_FAIL)
+GEN_SES_STATUS_UPS_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_UPS_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_UPS_ACCESSORS(batt_fail, BATT_FAIL)
+GEN_SES_STATUS_UPS_ACCESSORS(bpf, BPF)
+#undef GEN_SES_STATUS_UPS_ACCESSORS
+
+/*-------------------------- Display Status Element --------------------------*/
+struct ses_status_display {
+ struct ses_status_common common;
+ uint8_t bytes[1];
+ uint8_t display_character[2];
+};
+
+enum ses_status_display_field_data {
+ SES_STATUS_DISPLAY_IDENT_BYTE = 0,
+ SES_STATUS_DISPLAY_IDENT_MASK = 0x80,
+ SES_STATUS_DISPLAY_IDENT_SHIFT = 7,
+
+ SES_STATUS_DISPLAY_FAIL_BYTE = 0,
+ SES_STATUS_DISPLAY_FAIL_MASK = 0x40,
+ SES_STATUS_DISPLAY_FAIL_SHIFT = 6,
+
+ SES_STATUS_DISPLAY_DISPLAY_MODE_BYTE = 0,
+ SES_STATUS_DISPLAY_DISPLAY_MODE_MASK = 0x03,
+ SES_STATUS_DISPLAY_DISPLAY_MODE_SHIFT = 6,
+ SES_STATUS_DISPLAY_DISPLAY_MODE_DC_FIELD_UNSUPP = 0x0,
+ SES_STATUS_DISPLAY_DISPLAY_MODE_DC_FIELD_SUPP = 0x1,
+ SES_STATUS_DISPLAY_DISPLAY_MODE_DC_FIELD = 0x2
+};
+
+#define GEN_SES_STATUS_DISPLAY_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_display, SES_STATUS_DISPLAY, LCASE, UCASE)
+GEN_SES_STATUS_DISPLAY_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_DISPLAY_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_DISPLAY_ACCESSORS(display_mode, DISPLAY_MODE)
+#undef GEN_SES_STATUS_DISPLAY_ACCESSORS
+
+/*----------------------- Key Pad Entry Status Element -----------------------*/
+struct ses_status_key_pad_entry {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_key_pad_entry_field_data {
+ SES_STATUS_KEY_PAD_ENTRY_IDENT_BYTE = 0,
+ SES_STATUS_KEY_PAD_ENTRY_IDENT_MASK = 0x80,
+ SES_STATUS_KEY_PAD_ENTRY_IDENT_SHIFT = 7,
+
+ SES_STATUS_KEY_PAD_ENTRY_FAIL_BYTE = 0,
+ SES_STATUS_KEY_PAD_ENTRY_FAIL_MASK = 0x40,
+ SES_STATUS_KEY_PAD_ENTRY_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_key_pad_entry, SES_STATUS_KEY_PAD_ENTRY, LCASE, UCASE)
+GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(fail, FAIL)
+#undef GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS
+
+/*------------------------- Enclosure Status Element -------------------------*/
+struct ses_status_enclosure {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_enclosure_field_data {
+ SES_STATUS_ENCLOSURE_IDENT_BYTE = 0,
+ SES_STATUS_ENCLOSURE_IDENT_MASK = 0x80,
+ SES_STATUS_ENCLOSURE_IDENT_SHIFT = 7,
+
+ SES_STATUS_ENCLOSURE_TIME_UNTIL_POWER_CYCLE_BYTE = 1,
+ SES_STATUS_ENCLOSURE_TIME_UNTIL_POWER_CYCLE_MASK = 0xFC,
+ SES_STATUS_ENCLOSURE_TIME_UNTIL_POWER_CYCLE_SHIFT = 2,
+
+ SES_STATUS_ENCLOSURE_FAIL_BYTE = 1,
+ SES_STATUS_ENCLOSURE_FAIL_MASK = 0x02,
+ SES_STATUS_ENCLOSURE_FAIL_SHIFT = 1,
+
+ SES_STATUS_ENCLOSURE_WARN_BYTE = 1,
+ SES_STATUS_ENCLOSURE_WARN_MASK = 0x01,
+ SES_STATUS_ENCLOSURE_WARN_SHIFT = 0,
+
+ SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_BYTE = 2,
+ SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_MASK = 0xFC,
+ SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_SHIFT = 2,
+ SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_MAX_AUTO = 60,
+ SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_MANUAL = 63,
+
+ SES_STATUS_ENCLOSURE_REQUESTED_FAIL_BYTE = 2,
+ SES_STATUS_ENCLOSURE_REQUESTED_FAIL_MASK = 0x02,
+ SES_STATUS_ENCLOSURE_REQUESTED_FAIL_SHIFT = 1,
+
+ SES_STATUS_ENCLOSURE_REQUESTED_WARN_BYTE = 2,
+ SES_STATUS_ENCLOSURE_REQUESTED_WARN_MASK = 0x01,
+ SES_STATUS_ENCLOSURE_REQUESTED_WARN_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_ENCLOSURE_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_enclosure, SES_STATUS_ENCLOSURE, LCASE, UCASE)
+GEN_SES_STATUS_ENCLOSURE_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_ENCLOSURE_ACCESSORS(time_until_power_cycle,
+ TIME_UNTIL_POWER_CYCLE)
+GEN_SES_STATUS_ENCLOSURE_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_ENCLOSURE_ACCESSORS(warn, WARN)
+GEN_SES_STATUS_ENCLOSURE_ACCESSORS(requested_power_off_duration,
+ REQUESTED_POWER_OFF_DURATION)
+GEN_SES_STATUS_ENCLOSURE_ACCESSORS(requested_fail, REQUESTED_FAIL)
+GEN_SES_STATUS_ENCLOSURE_ACCESSORS(requested_warn, REQUESTED_WARN)
+#undef GEN_SES_STATUS_ENCLOSURE_ACCESSORS
+
+/*------------------- SCSI Port/Transceiver Status Element -------------------*/
+struct ses_status_scsi_port_or_xcvr {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_scsi_port_or_xcvr_field_data {
+ SES_STATUS_SCSI_PORT_OR_XCVR_IDENT_BYTE = 0,
+ SES_STATUS_SCSI_PORT_OR_XCVR_IDENT_MASK = 0x80,
+ SES_STATUS_SCSI_PORT_OR_XCVR_IDENT_SHIFT = 7,
+
+ SES_STATUS_SCSI_PORT_OR_XCVR_FAIL_BYTE = 0,
+ SES_STATUS_SCSI_PORT_OR_XCVR_FAIL_MASK = 0x40,
+ SES_STATUS_SCSI_PORT_OR_XCVR_FAIL_SHIFT = 6,
+
+ SES_STATUS_SCSI_PORT_OR_XCVR_REPORT_BYTE = 1,
+ SES_STATUS_SCSI_PORT_OR_XCVR_REPORT_MASK = 0x01,
+ SES_STATUS_SCSI_PORT_OR_XCVR_REPORT_SHIFT = 0,
+
+ SES_STATUS_SCSI_PORT_OR_XCVR_DISABLED_BYTE = 2,
+ SES_STATUS_SCSI_PORT_OR_XCVR_DISABLED_MASK = 0x10,
+ SES_STATUS_SCSI_PORT_OR_XCVR_DISABLED_SHIFT = 4,
+
+ SES_STATUS_SCSI_PORT_OR_XCVR_LOL_BYTE = 2,
+ SES_STATUS_SCSI_PORT_OR_XCVR_LOL_MASK = 0x02,
+ SES_STATUS_SCSI_PORT_OR_XCVR_LOL_SHIFT = 1,
+
+ SES_STATUS_SCSI_PORT_OR_XCVR_XMIT_FAIL_BYTE = 2,
+ SES_STATUS_SCSI_PORT_OR_XCVR_XMIT_FAIL_MASK = 0x01,
+ SES_STATUS_SCSI_PORT_OR_XCVR_XMIT_FAIL_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_scsi_port_or_xcvr, SES_STATUS_SCSI_PORT_OR_XCVR,\
+ LCASE, UCASE)
+GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(report, REPORT)
+GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(disable, DISABLED)
+GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(lol, LOL)
+GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(xmit_fail, XMIT_FAIL)
+#undef GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS
+
+/*------------------------- Language Status Element --------------------------*/
+struct ses_status_language {
+ struct ses_status_common common;
+ uint8_t bytes[1];
+ uint8_t language_code[2];
+};
+
+enum ses_status_language_field_data {
+ SES_STATUS_LANGUAGE_IDENT_BYTE = 0,
+ SES_STATUS_LANGUAGE_IDENT_MASK = 0x80,
+ SES_STATUS_LANGUAGE_IDENT_SHIFT = 7
+};
+
+#define GEN_SES_STATUS_LANGUAGE_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_language, SES_STATUS_LANGUAGE, LCASE, UCASE)
+GEN_SES_STATUS_LANGUAGE_ACCESSORS(ident, IDENT)
+#undef GEN_SES_STATUS_LANGUAGE_ACCESSORS
+
+/*-------------------- Communication Port Status Element ---------------------*/
+struct ses_status_comm_port {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_comm_port_field_data {
+ SES_STATUS_COMM_PORT_IDENT_BYTE = 0,
+ SES_STATUS_COMM_PORT_IDENT_MASK = 0x80,
+ SES_STATUS_COMM_PORT_IDENT_SHIFT = 7,
+
+ SES_STATUS_COMM_PORT_FAIL_BYTE = 0,
+ SES_STATUS_COMM_PORT_FAIL_MASK = 0x40,
+ SES_STATUS_COMM_PORT_FAIL_SHIFT = 6,
+
+ SES_STATUS_COMM_PORT_DISABLED_BYTE = 2,
+ SES_STATUS_COMM_PORT_DISABLED_MASK = 0x01,
+ SES_STATUS_COMM_PORT_DISABLED_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_COMM_PORT_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_comm_port, SES_STATUS_COMM_PORT, LCASE, UCASE)
+GEN_SES_STATUS_COMM_PORT_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_COMM_PORT_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_COMM_PORT_ACCESSORS(disabled, DISABLED)
+#undef GEN_SES_STATUS_COMM_PORT_ACCESSORS
+
+/*---------------------- Voltage Sensor Status Element -----------------------*/
+struct ses_status_voltage_sensor {
+ struct ses_status_common common;
+ uint8_t bytes[1];
+ uint8_t voltage[2];
+};
+
+enum ses_status_voltage_sensor_field_data {
+ SES_STATUS_VOLTAGE_SENSOR_IDENT_BYTE = 0,
+ SES_STATUS_VOLTAGE_SENSOR_IDENT_MASK = 0x80,
+ SES_STATUS_VOLTAGE_SENSOR_IDENT_SHIFT = 7,
+
+ SES_STATUS_VOLTAGE_SENSOR_FAIL_BYTE = 0,
+ SES_STATUS_VOLTAGE_SENSOR_FAIL_MASK = 0x40,
+ SES_STATUS_VOLTAGE_SENSOR_FAIL_SHIFT = 6,
+
+ SES_STATUS_VOLTAGE_SENSOR_WARN_OVER_BYTE = 0,
+ SES_STATUS_VOLTAGE_SENSOR_WARN_OVER_MASK = 0x08,
+ SES_STATUS_VOLTAGE_SENSOR_WARN_OVER_SHIFT = 3,
+
+ SES_STATUS_VOLTAGE_SENSOR_WARN_UNDER_BYTE = 0,
+ SES_STATUS_VOLTAGE_SENSOR_WARN_UNDER_MASK = 0x04,
+ SES_STATUS_VOLTAGE_SENSOR_WARN_UNDER_SHIFT = 2,
+
+ SES_STATUS_VOLTAGE_SENSOR_CRIT_OVER_BYTE = 0,
+ SES_STATUS_VOLTAGE_SENSOR_CRIT_OVER_MASK = 0x02,
+ SES_STATUS_VOLTAGE_SENSOR_CRIT_OVER_SHIFT = 1,
+
+ SES_STATUS_VOLTAGE_SENSOR_CRIT_UNDER_BYTE = 0,
+ SES_STATUS_VOLTAGE_SENSOR_CRIT_UNDER_MASK = 0x01,
+ SES_STATUS_VOLTAGE_SENSOR_CRIT_UNDER_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_voltage_sensor, SES_STATUS_VOLTAGE_SENSOR, \
+ LCASE, UCASE)
+GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(warn_over, WARN_OVER)
+GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(warn_under, WARN_UNDER)
+GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(crit_over, CRIT_OVER)
+GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(crit_under, CRIT_UNDER)
+#undef GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS
+
+/*---------------------- Current Sensor Status Element -----------------------*/
+struct ses_status_current_sensor {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_current_sensor_field_data {
+ SES_STATUS_CURRENT_SENSOR_IDENT_BYTE = 0,
+ SES_STATUS_CURRENT_SENSOR_IDENT_MASK = 0x80,
+ SES_STATUS_CURRENT_SENSOR_IDENT_SHIFT = 7,
+
+ SES_STATUS_CURRENT_SENSOR_FAIL_BYTE = 0,
+ SES_STATUS_CURRENT_SENSOR_FAIL_MASK = 0x40,
+ SES_STATUS_CURRENT_SENSOR_FAIL_SHIFT = 6,
+
+ SES_STATUS_CURRENT_SENSOR_WARN_OVER_BYTE = 0,
+ SES_STATUS_CURRENT_SENSOR_WARN_OVER_MASK = 0x08,
+ SES_STATUS_CURRENT_SENSOR_WARN_OVER_SHIFT = 3,
+
+ SES_STATUS_CURRENT_SENSOR_CRIT_OVER_BYTE = 0,
+ SES_STATUS_CURRENT_SENSOR_CRIT_OVER_MASK = 0x02,
+ SES_STATUS_CURRENT_SENSOR_CRIT_OVER_SHIFT = 1
+};
+
+#define GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_current_sensor, SES_STATUS_CURRENT_SENSOR, \
+ LCASE, UCASE)
+GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(warn_over, WARN_OVER)
+GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(crit_over, CRIT_OVER)
+#undef GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS
+
+/*--------------------- SCSI Target Port Status Element ----------------------*/
+struct ses_status_target_port {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_scsi_target_port_field_data {
+ SES_STATUS_TARGET_PORT_IDENT_BYTE = 0,
+ SES_STATUS_TARGET_PORT_IDENT_MASK = 0x80,
+ SES_STATUS_TARGET_PORT_IDENT_SHIFT = 7,
+
+ SES_STATUS_TARGET_PORT_FAIL_BYTE = 0,
+ SES_STATUS_TARGET_PORT_FAIL_MASK = 0x40,
+ SES_STATUS_TARGET_PORT_FAIL_SHIFT = 6,
+
+ SES_STATUS_TARGET_PORT_REPORT_BYTE = 1,
+ SES_STATUS_TARGET_PORT_REPORT_MASK = 0x01,
+ SES_STATUS_TARGET_PORT_REPORT_SHIFT = 0,
+
+ SES_STATUS_TARGET_PORT_ENABLED_BYTE = 2,
+ SES_STATUS_TARGET_PORT_ENABLED_MASK = 0x01,
+ SES_STATUS_TARGET_PORT_ENABLED_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_TARGET_PORT_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_target_port, SES_STATUS_TARGET_PORT, LCASE, UCASE)
+GEN_SES_STATUS_TARGET_PORT_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_TARGET_PORT_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_TARGET_PORT_ACCESSORS(report, REPORT)
+GEN_SES_STATUS_TARGET_PORT_ACCESSORS(enabled, ENABLED)
+#undef GEN_SES_STATUS_TARGET_PORT_ACCESSORS
+
+/*-------------------- SCSI Initiator Port Status Element --------------------*/
+struct ses_status_initiator_port {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_scsi_initiator_port_field_data {
+ SES_STATUS_INITIATOR_PORT_IDENT_BYTE = 0,
+ SES_STATUS_INITIATOR_PORT_IDENT_MASK = 0x80,
+ SES_STATUS_INITIATOR_PORT_IDENT_SHIFT = 7,
+
+ SES_STATUS_INITIATOR_PORT_FAIL_BYTE = 0,
+ SES_STATUS_INITIATOR_PORT_FAIL_MASK = 0x40,
+ SES_STATUS_INITIATOR_PORT_FAIL_SHIFT = 6,
+
+ SES_STATUS_INITIATOR_PORT_REPORT_BYTE = 1,
+ SES_STATUS_INITIATOR_PORT_REPORT_MASK = 0x01,
+ SES_STATUS_INITIATOR_PORT_REPORT_SHIFT = 0,
+
+ SES_STATUS_INITIATOR_PORT_ENABLED_BYTE = 2,
+ SES_STATUS_INITIATOR_PORT_ENABLED_MASK = 0x01,
+ SES_STATUS_INITIATOR_PORT_ENABLED_SHIFT = 0
+};
+
+#define GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_initiator_port, SES_STATUS_INITIATOR_PORT, \
+ LCASE, UCASE)
+GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(fail, FAIL)
+GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(report, REPORT)
+GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(enabled, ENABLED)
+#undef GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS
+
+/*-------------------- Simple Subenclosure Status Element --------------------*/
+struct ses_status_simple_subses {
+ struct ses_status_common common;
+ uint8_t bytes[2];
+ uint8_t short_enclosure_status;
+};
+
+enum ses_status_simple_subses_field_data {
+ SES_STATUS_SIMPlE_SUBSES_IDENT_BYTE = 0,
+ SES_STATUS_SIMPlE_SUBSES_IDENT_MASK = 0x80,
+ SES_STATUS_SIMPlE_SUBSES_IDENT_SHIFT = 7,
+
+ SES_STATUS_SIMPlE_SUBSES_FAIL_BYTE = 0,
+ SES_STATUS_SIMPlE_SUBSES_FAIL_MASK = 0x40,
+ SES_STATUS_SIMPlE_SUBSES_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_simple_subses, SES_STATUS_SIMPlE_SUBSES, \
+ LCASE, UCASE)
+GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS(fail, FAIL)
+#undef GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS
+
+/*----------------------- SAS Expander Status Element ------------------------*/
+struct ses_status_sas_expander {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_sas_expander_field_data {
+ SES_STATUS_SAS_EXPANDER_IDENT_BYTE = 0,
+ SES_STATUS_SAS_EXPANDER_IDENT_MASK = 0x80,
+ SES_STATUS_SAS_EXPANDER_IDENT_SHIFT = 7,
+
+ SES_STATUS_SAS_EXPANDER_FAIL_BYTE = 0,
+ SES_STATUS_SAS_EXPANDER_FAIL_MASK = 0x40,
+ SES_STATUS_SAS_EXPANDER_FAIL_SHIFT = 6
+};
+
+#define GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_sas_expander, SES_STATUS_SAS_EXPANDER, LCASE, UCASE)
+GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS(fail, FAIL)
+#undef GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS
+
+/*----------------------- SAS Connector Status Element -----------------------*/
+struct ses_status_sas_connector {
+ struct ses_status_common common;
+ uint8_t bytes[3];
+};
+
+enum ses_status_sas_connector_field_data {
+ SES_STATUS_SAS_CONNECTOR_IDENT_BYTE = 0,
+ SES_STATUS_SAS_CONNECTOR_IDENT_MASK = 0x80,
+ SES_STATUS_SAS_CONNECTOR_IDENT_SHIFT = 7,
+
+ SES_STATUS_SAS_CONNECTOR_TYPE_BYTE = 0,
+ SES_STATUS_SAS_CONNECTOR_TYPE_MASK = 0x7F,
+ SES_STATUS_SAS_CONNECTOR_TYPE_SHIFT = 0,
+
+ SES_STATUS_SAS_CONNECTOR_PHYS_LINK_BYTE = 1,
+ SES_STATUS_SAS_CONNECTOR_PHYS_LINK_MASK = 0xFF,
+ SES_STATUS_SAS_CONNECTOR_PHYS_LINK_SHIFT = 0,
+ SES_STATUS_SAS_CONNECTOR_PHYS_LINK_ALL = 0xFF,
+
+ SES_STATUS_SAS_CONNECTOR_FAIL_BYTE = 2,
+ SES_STATUS_SAS_CONNECTOR_FAIL_MASK = 0x40,
+ SES_STATUS_SAS_CONNECTOR_FAIL_SHIFT = 6,
+};
+
+#define GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(LCASE, UCASE) \
+ GEN_GETTER(ses_status_sas_connector, SES_STATUS_SAS_CONNECTOR, \
+ LCASE, UCASE)
+GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(ident, IDENT)
+GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(type, TYPE)
+GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(phys_link, PHYS_LINK)
+GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(fail, FAIL)
+#undef GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS
+
+/*------------------------- Universal Status Element -------------------------*/
+union ses_status_element {
+ struct ses_status_common common;
+ struct ses_status_dev_slot dev_slot;
+ struct ses_status_array_dev_slot array_dev_slot;
+ struct ses_status_power_supply power_supply;
+ struct ses_status_cooling cooling;
+ struct ses_status_temp_sensor temp_sensor;
+ struct ses_status_door_lock door_lock;
+ struct ses_status_audible_alarm audible_alarm;
+ struct ses_status_ecc_electronics ecc_electronics;
+ struct ses_status_scc_electronics scc_electronics;
+ struct ses_status_nv_cache nv_cache;
+ struct ses_status_invalid_op_reason invalid_op_reason;
+ struct ses_status_ups ups;
+ struct ses_status_display display;
+ struct ses_status_key_pad_entry key_pad_entry;
+ struct ses_status_scsi_port_or_xcvr scsi_port_or_xcvr;
+ struct ses_status_language language;
+ struct ses_status_comm_port comm_port;
+ struct ses_status_voltage_sensor voltage_sensor;
+ struct ses_status_current_sensor current_sensor;
+ struct ses_status_target_port target_port;
+ struct ses_status_initiator_port initiator_port;
+ struct ses_status_simple_subses simple_subses;
+ struct ses_status_sas_expander sas_expander;
+ struct ses_status_sas_connector sas_connector;
+ uint8_t bytes[4];
+};
+
+/*===================== SCSI SES Status Diagnostic Page =====================*/
+struct ses_status_page {
+ struct ses_page_hdr hdr;
+ union ses_status_element elements[];
+};
+
+enum ses_status_page_field_data {
+ SES_STATUS_PAGE_INVOP_MASK = 0x10,
+ SES_STATUS_PAGE_INVOP_SHIFT = 4,
+
+ SES_STATUS_PAGE_INFO_MASK = 0x08,
+ SES_STATUS_PAGE_INFO_SHIFT = 3,
+
+ SES_STATUS_PAGE_NON_CRIT_MASK = 0x04,
+ SES_STATUS_PAGE_NON_CRIT_SHIFT = 2,
+
+ SES_STATUS_PAGE_CRIT_MASK = 0x02,
+ SES_STATUS_PAGE_CRIT_SHIFT = 1,
+
+ SES_STATUS_PAGE_UNRECOV_MASK = 0x01,
+ SES_STATUS_PAGE_UNRECOV_SHIFT = 0,
+
+ SES_STATUS_PAGE_CHANGED_MASK = SES_STATUS_PAGE_INVOP_MASK
+ | SES_STATUS_PAGE_INFO_MASK
+ | SES_STATUS_PAGE_NON_CRIT_MASK
+ | SES_STATUS_PAGE_CRIT_MASK
+ | SES_STATUS_PAGE_UNRECOV_MASK,
+ SES_STATUS_PAGE_CHANGED_SHIFT = 0,
+};
+
+#define GEN_SES_STATUS_PAGE_ACCESSORS(LCASE, UCASE) \
+ GEN_HDR_ACCESSORS(ses_status_page, SES_STATUS_PAGE, LCASE, UCASE)
+
+GEN_SES_STATUS_PAGE_ACCESSORS(invop, INVOP)
+GEN_SES_STATUS_PAGE_ACCESSORS(info, INFO)
+GEN_SES_STATUS_PAGE_ACCESSORS(non_crit, NON_CRIT)
+GEN_SES_STATUS_PAGE_ACCESSORS(crit, CRIT)
+GEN_SES_STATUS_PAGE_ACCESSORS(unrecov, UNRECOV)
+GEN_SES_STATUS_PAGE_ACCESSORS(changed, CHANGED)
+#undef GEN_SES_STATUS_PAGE_ACCESSORS
+
+/*================ SCSI SES Element Descriptor Diagnostic Page ===============*/
+struct ses_elem_descr {
+ uint8_t reserved[2];
+ uint8_t length[2];
+ char description[];
+};
+
+struct ses_elem_descr_page {
+ struct ses_page_hdr hdr;
+ struct ses_elem_descr descrs[];
+};
+
+/*============ SCSI SES Additional Element Status Diagnostic Page ============*/
+struct ses_addl_elem_status_page {
+ struct ses_page_hdr hdr;
+};
+
+/*====================== Legacy (Deprecated) Structures ======================*/
+struct ses_control_page_hdr {
+ uint8_t page_code;
+ uint8_t control_flags;
+ uint8_t length[2];
+ uint8_t gen_code[4];
+/* Followed by variable length array of descriptors. */
+};
+
+struct ses_status_page_hdr {
+ uint8_t page_code;
+ uint8_t status_flags;
+ uint8_t length[2];
+ uint8_t gen_code[4];
+/* Followed by variable length array of descriptors. */
+};
+
+/* ses_page_hdr.reserved values */
/*
- * Object Status
+ * Enclosure Status Diagnostic Page:
+ * uint8_t reserved : 3,
+ * invop : 1,
+ * info : 1,
+ * noncritical : 1,
+ * critical : 1,
+ * unrecov : 1;
*/
-typedef struct {
- unsigned int obj_id;
- unsigned char cstat[4];
-} ses_objstat;
+#define SES_ENCSTAT_UNRECOV 0x01
+#define SES_ENCSTAT_CRITICAL 0x02
+#define SES_ENCSTAT_NONCRITICAL 0x04
+#define SES_ENCSTAT_INFO 0x08
+#define SES_ENCSTAT_INVOP 0x10
+/* Status mask: All of the above OR'd together */
+#define SES_STATUS_MASK 0x1f
+#define SES_SET_STATUS_MASK 0xf
+/* Element Descriptor Diagnostic Page: unused */
+/* Additional Element Status Diagnostic Page: unused */
+
+
/* Summary SES Status Defines, Common Status Codes */
#define SES_OBJSTAT_UNSUPPORTED 0
@@ -140,6 +2164,7 @@ typedef struct {
#define SES_OBJSTAT_NOTINSTALLED 5
#define SES_OBJSTAT_UNKNOWN 6
#define SES_OBJSTAT_NOTAVAIL 7
+#define SES_OBJSTAT_NOACCESS 8
/*
* For control pages, cstat[0] is the same for the
@@ -181,3 +2206,236 @@ typedef union {
unsigned int obj_id;
char obj_text[1];
} ses_hlptxt;
+
+/*============================================================================*/
+struct ses_elm_desc_hdr {
+ uint8_t reserved[2];
+ uint8_t length[2];
+};
+
+/*
+ * SES v2 r20 6.1.13 - Element Additional Status diagnostic page
+ * Tables 26-28 (general), 29-32 (FC), 33-41 (SAS)
+ *
+ * Protocol identifier uses definitions in scsi_all.h;
+ * SPSP_PROTO_FC, SPSP_PROTO_SAS are the only ones used here.
+ */
+
+struct ses_elm_fc_eip_hdr {
+ uint8_t num_phys;
+ uint8_t reserved[2];
+ uint8_t dev_slot_num;
+ uint8_t node_name[8];
+};
+
+struct ses_elm_fc_noneip_hdr {
+ uint8_t num_phys;
+ uint8_t reserved;
+ uint8_t node_name[8];
+};
+
+struct ses_elm_fc_base_hdr {
+ uint8_t num_phys;
+};
+
+union ses_elm_fc_hdr {
+ struct ses_elm_fc_base_hdr base_hdr;
+ struct ses_elm_fc_eip_hdr eip_hdr;
+ struct ses_elm_fc_noneip_hdr noneip_hdr;
+};
+
+struct ses_elm_fc_port {
+ uint8_t port_loop_position;
+ uint8_t bypass_reason;
+#define SES_FC_PORT_BYPASS_UNBYPASSED 0x00
+
+#define SES_FC_PORT_BYPASS_LINKFAIL_RATE_TOO_HIGH 0x10
+#define SES_FC_PORT_BYPASS_SYNC_LOSS_RATE_TOO_HIGH 0x11
+#define SES_FC_PORT_BYPASS_SIGNAL_LOSS_RATE_TOO_HIGH 0x12
+#define SES_FC_PORT_BYPASS_SEQPROTO_ERR_RATE_TOO_HIGH 0x13
+#define SES_FC_PORT_BYPASS_INVAL_XMIT_RATE_TOO_HIGH 0x14
+#define SES_FC_PORT_BYPASS_CRC_ERR_RATE_TOO_HIGH 0x15
+
+#define SES_FC_PORT_BYPASS_ERR_RATE_RESERVED_BEGIN 0x16
+#define SES_FC_PORT_BYPASS_ERR_RATE_RESERVED_END 0x1F
+
+#define SES_FC_PORT_BYPASS_LINKFAIL_COUNT_TOO_HIGH 0x20
+#define SES_FC_PORT_BYPASS_SYNC_LOSS_COUNT_TOO_HIGH 0x21
+#define SES_FC_PORT_BYPASS_SIGNAL_LOSS_COUNT_TOO_HIGH 0x22
+#define SES_FC_PORT_BYPASS_SEQPROTO_ERR_COUNT_TOO_HIGH 0x23
+#define SES_FC_PORT_BYPASS_INVAL_XMIT_COUNT_TOO_HIGH 0x24
+#define SES_FC_PORT_BYPASS_CRC_ERR_COUNT_TOO_HIGH 0x25
+
+#define SES_FC_PORT_BYPASS_ERR_COUNT_RESERVED_BEGIN 0x26
+#define SES_FC_PORT_BYPASS_ERR_COUNT_RESERVED_END 0x2F
+
+#define SES_FC_PORT_BYPASS_RESERVED_BEGIN 0x30
+#define SES_FC_PORT_BYPASS_RESERVED_END 0xBF
+
+#define SES_FC_PORT_BYPASS_VENDOR_SPECIFIC_BEGIN 0xC0
+#define SES_FC_PORT_BYPASS_VENDOR_SPECIFIC_END 0xFF
+ uint8_t port_req_hard_addr;
+ uint8_t n_port_id[3];
+ uint8_t n_port_name[8];
+};
+
+struct ses_elm_sas_device_phy {
+ uint8_t byte0;
+ /*
+ * uint8_t reserved0 : 1,
+ * uint8_t device_type : 3,
+ * uint8_t reserved1 : 4;
+ */
+
+ uint8_t reserved0;
+
+ /* Bit positions for initiator and target port protocols */
+#define SES_SASOBJ_DEV_PHY_SMP 0x2
+#define SES_SASOBJ_DEV_PHY_STP 0x4
+#define SES_SASOBJ_DEV_PHY_SSP 0x8
+ /* Select all of the above protocols */
+#define SES_SASOBJ_DEV_PHY_PROTOMASK 0xe
+ uint8_t initiator_ports;
+ /*
+ * uint8_t reserved0 : 4,
+ * uint8_t ssp : 1,
+ * uint8_t stp : 1,
+ * uint8_t smp : 1,
+ * uint8_t reserved1 : 3;
+ */
+ uint8_t target_ports;
+ /*
+ * uint8_t sata_port_selector : 1,
+ * uint8_t reserved : 3,
+ * uint8_t ssp : 1,
+ * uint8_t stp : 1,
+ * uint8_t smp : 1,
+ * uint8_t sata_device : 1;
+ */
+ uint8_t parent_addr[8]; /* SAS address of parent */
+ uint8_t phy_addr[8]; /* SAS address of this phy */
+ uint8_t phy_id;
+ uint8_t reserved1[7];
+};
+#ifdef _KERNEL
+int ses_elm_sas_dev_phy_sata_dev(struct ses_elm_sas_device_phy *);
+int ses_elm_sas_dev_phy_sata_port(struct ses_elm_sas_device_phy *);
+int ses_elm_sas_dev_phy_dev_type(struct ses_elm_sas_device_phy *);
+#endif /* _KERNEL */
+
+struct ses_elm_sas_expander_phy {
+ uint8_t connector_index;
+ uint8_t other_index;
+};
+
+struct ses_elm_sas_port_phy {
+ uint8_t phy_id;
+ uint8_t reserved;
+ uint8_t connector_index;
+ uint8_t other_index;
+ uint8_t phy_addr[8];
+};
+
+struct ses_elm_sas_type0_base_hdr {
+ uint8_t num_phys;
+ uint8_t byte1;
+ /*
+ * uint8_t descriptor_type : 2,
+ * uint8_t reserved : 5,
+ * uint8_t not_all_phys : 1;
+ */
+#define SES_SASOBJ_TYPE0_NOT_ALL_PHYS(obj) \
+ ((obj)->byte1 & 0x1)
+};
+
+struct ses_elm_sas_type0_eip_hdr {
+ struct ses_elm_sas_type0_base_hdr base;
+ uint8_t reserved;
+ uint8_t dev_slot_num;
+};
+
+struct ses_elm_sas_type1_expander_hdr {
+ uint8_t num_phys;
+ uint8_t byte1;
+ /*
+ * uint8_t descriptor_type : 2,
+ * uint8_t reserved : 6;
+ */
+ uint8_t reserved[2];
+ uint8_t sas_addr[8];
+};
+
+struct ses_elm_sas_type1_nonexpander_hdr {
+ uint8_t num_phys;
+ uint8_t byte1;
+ /*
+ * uint8_t descriptor_type : 2,
+ * uint8_t reserved : 6;
+ */
+ uint8_t reserved[2];
+};
+
+/* NB: This is only usable for as long as the headers happen to match */
+struct ses_elm_sas_base_hdr {
+ uint8_t num_phys;
+ uint8_t byte1;
+ /*
+ * uint8_t descriptor_type : 2,
+ * uint8_t descr_specific : 6;
+ */
+#define SES_SASOBJ_TYPE_SLOT 0
+#define SES_SASOBJ_TYPE_OTHER 1
+};
+
+union ses_elm_sas_hdr {
+ struct ses_elm_sas_base_hdr base_hdr;
+ struct ses_elm_sas_type0_base_hdr type0_noneip;
+ struct ses_elm_sas_type0_eip_hdr type0_eip;
+ struct ses_elm_sas_type1_expander_hdr type1_exp;
+ struct ses_elm_sas_type1_nonexpander_hdr type1_nonexp;
+};
+int ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *);
+int ses_elm_sas_descr_type(union ses_elm_sas_hdr *);
+
+struct ses_elm_addlstatus_base_hdr {
+ uint8_t byte0;
+ /*
+ * uint8_t invalid : 1,
+ * uint8_t reserved : 2,
+ * uint8_t eip : 1,
+ * uint8_t proto_id : 4;
+ */
+ uint8_t length;
+};
+int ses_elm_addlstatus_proto(struct ses_elm_addlstatus_base_hdr *);
+int ses_elm_addlstatus_eip(struct ses_elm_addlstatus_base_hdr *);
+int ses_elm_addlstatus_invalid(struct ses_elm_addlstatus_base_hdr *);
+
+struct ses_elm_addlstatus_eip_hdr {
+ struct ses_elm_addlstatus_base_hdr base;
+ uint8_t reserved;
+ uint8_t element_index;
+ /* NB: This define (currently) applies to all eip=1 headers */
+#define SES_EIP_HDR_EXTRA_LEN 2
+};
+
+union ses_elm_addlstatus_descr_hdr {
+ struct ses_elm_addlstatus_base_hdr base;
+ struct ses_elm_addlstatus_eip_hdr eip;
+};
+
+union ses_elm_addlstatus_proto_hdr {
+ union ses_elm_fc_hdr fc;
+ union ses_elm_sas_hdr sas;
+};
+
+/*============================= Namespace Cleanup ============================*/
+#undef GEN_HDR_ACCESSORS
+#undef GEN_ACCESSORS
+#undef GEN_HDR_SETTER
+#undef GEN_HDR_GETTER
+#undef GEN_SETTER
+#undef GEN_GETTER
+#undef MK_ENUM
+
+#endif /* _SCSI_SES_H_ */
OpenPOWER on IntegriCloud