[Midnightbsd-cvs] src [8749] trunk: Revamp the CAM enclose services driver.

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sun Sep 25 22:34:48 EDT 2016


Revision: 8749
          http://svnweb.midnightbsd.org/src/?rev=8749
Author:   laffer1
Date:     2016-09-25 22:34:48 -0400 (Sun, 25 Sep 2016)
Log Message:
-----------
Revamp the CAM enclose services driver. This driver uses an in kernel daemon to track state changes.

Modified Paths:
--------------
    trunk/share/examples/ses/Makefile.inc
    trunk/share/examples/ses/sesd/sesd.0
    trunk/share/examples/ses/srcs/eltsub.c
    trunk/share/examples/ses/srcs/getencstat.c
    trunk/share/examples/ses/srcs/getnobj.c
    trunk/share/examples/ses/srcs/getobjmap.c
    trunk/share/examples/ses/srcs/getobjstat.c
    trunk/share/examples/ses/srcs/inienc.c
    trunk/share/examples/ses/srcs/sesd.c
    trunk/share/examples/ses/srcs/setencstat.c
    trunk/share/examples/ses/srcs/setobjstat.c
    trunk/sys/cam/scsi/scsi_pt.c
    trunk/sys/cam/scsi/scsi_ses.h
    trunk/sys/conf/files
    trunk/sys/fs/devfs/devfs_vnops.c
    trunk/sys/modules/cam/Makefile
    trunk/usr.bin/kdump/mkioctls

Added Paths:
-----------
    trunk/sys/cam/scsi/scsi_enc.c
    trunk/sys/cam/scsi/scsi_enc.h
    trunk/sys/cam/scsi/scsi_enc_internal.h
    trunk/sys/cam/scsi/scsi_enc_safte.c
    trunk/sys/cam/scsi/scsi_enc_ses.c

Removed Paths:
-------------
    trunk/sys/cam/scsi/scsi_ses.c

Modified: trunk/share/examples/ses/Makefile.inc
===================================================================
--- trunk/share/examples/ses/Makefile.inc	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/Makefile.inc	2016-09-26 02:34:48 UTC (rev 8749)
@@ -32,7 +32,6 @@
 # mjacob at feral.com
 #
 
-CFLAGS+= -I/usr/include/cam/scsi -DSESINC="<scsi_ses.h>"
 BINDIR?= /usr/sbin
 
 CLEANFILES+= ${MAN}

Modified: trunk/share/examples/ses/sesd/sesd.0
===================================================================
--- trunk/share/examples/ses/sesd/sesd.0	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/sesd/sesd.0	2016-09-26 02:34:48 UTC (rev 8749)
@@ -31,7 +31,7 @@
 .\"
 .\" $MidnightBSD$
 .\"
-.Dd February 21, 2000
+.Dd November 5, 2012
 .Dt SESD 8
 .Os
 .Sh NAME
@@ -39,6 +39,7 @@
 .Nd monitor SCSI Environmental Services Devices
 .Sh SYNOPSIS
 .Nm
+.Op Fl c
 .Op Fl d
 .Op Fl t Ar poll-interval
 .Ar device
@@ -57,7 +58,9 @@
 .Pp
 The following options may be used:
 .Bl -tag -width Ds
-.It Fl p Ar poll-interval
+.It Fl c
+Try to clear enclosure status after read.
+.It Fl t Ar poll-interval
 Change the interval of polling from the default 30 seconds to the number
 of seconds specified.
 .It Fl d

Modified: trunk/share/examples/ses/srcs/eltsub.c
===================================================================
--- trunk/share/examples/ses/srcs/eltsub.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/srcs/eltsub.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -33,10 +33,13 @@
  */
 
 #include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <sys/ioctl.h>
-#include SESINC
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_enc.h>
 
 #include "eltsub.h"
 
@@ -46,79 +49,82 @@
 	static char rbuf[132];
 
 	switch (type) {
-	case SESTYP_UNSPECIFIED:
+	case ELMTYP_UNSPECIFIED:
 		sprintf(rbuf, "Unspecified");
 		break;
-	case SESTYP_DEVICE:
-		sprintf(rbuf, "Device");
+	case ELMTYP_DEVICE:
+		sprintf(rbuf, "Device Slot");
 		break;
-	case SESTYP_POWER:
-		sprintf(rbuf, "Power supply");
+	case ELMTYP_POWER:
+		sprintf(rbuf, "Power Supply");
 		break;
-	case SESTYP_FAN:
-		sprintf(rbuf, "Cooling element");
+	case ELMTYP_FAN:
+		sprintf(rbuf, "Cooling");
 		break;
-	case SESTYP_THERM:
-		sprintf(rbuf, "Temperature sensors");
+	case ELMTYP_THERM:
+		sprintf(rbuf, "Temperature Sensors");
 		break;
-	case SESTYP_DOORLOCK:
+	case ELMTYP_DOORLOCK:
 		sprintf(rbuf, "Door Lock");
 		break;
-	case SESTYP_ALARM:
+	case ELMTYP_ALARM:
 		sprintf(rbuf, "Audible alarm");
 		break;
-	case SESTYP_ESCC:
-		sprintf(rbuf, "Enclosure services controller electronics");
+	case ELMTYP_ESCC:
+		sprintf(rbuf, "Enclosure Services Controller Electronics");
 		break;
-	case SESTYP_SCC:
-		sprintf(rbuf, "SCC controller electronics");
+	case ELMTYP_SCC:
+		sprintf(rbuf, "SCC Controller Electronics");
 		break;
-	case SESTYP_NVRAM:
-		sprintf(rbuf, "Nonvolatile cache");
+	case ELMTYP_NVRAM:
+		sprintf(rbuf, "Nonvolatile Cache");
 		break;
-	case SESTYP_UPS:
-		sprintf(rbuf, "Uninterruptible power supply");
+	case ELMTYP_INV_OP_REASON:
+		sprintf(rbuf, "Invalid Operation Reason");
 		break;
-	case SESTYP_DISPLAY:
+	case ELMTYP_UPS:
+		sprintf(rbuf, "Uninterruptible Power Supply");
+		break;
+	case ELMTYP_DISPLAY:
 		sprintf(rbuf, "Display");
 		break;
-	case SESTYP_KEYPAD:
-		sprintf(rbuf, "Key pad entry device");
+	case ELMTYP_KEYPAD:
+		sprintf(rbuf, "Key Pad Entry");
 		break;
-	case SESTYP_ENCLOSURE:
+	case ELMTYP_ENCLOSURE:
 		sprintf(rbuf, "Enclosure");
 		break;
-	case SESTYP_SCSIXVR:
-		sprintf(rbuf, "SCSI port/transceiver");
+	case ELMTYP_SCSIXVR:
+		sprintf(rbuf, "SCSI Port/Transceiver");
 		break;
-	case SESTYP_LANGUAGE:
+	case ELMTYP_LANGUAGE:
 		sprintf(rbuf, "Language");
 		break;
-	case SESTYP_COMPORT:
+	case ELMTYP_COMPORT:
 		sprintf(rbuf, "Communication Port");
 		break;
-	case SESTYP_VOM:
+	case ELMTYP_VOM:
 		sprintf(rbuf, "Voltage Sensor");
 		break;
-	case SESTYP_AMMETER:
+	case ELMTYP_AMMETER:
 		sprintf(rbuf, "Current Sensor");
 		break;
-	case SESTYP_SCSI_TGT:
-		sprintf(rbuf, "SCSI target port");
+	case ELMTYP_SCSI_TGT:
+		sprintf(rbuf, "SCSI Target Port");
 		break;
-	case SESTYP_SCSI_INI:
-		sprintf(rbuf, "SCSI initiator port");
+	case ELMTYP_SCSI_INI:
+		sprintf(rbuf, "SCSI Initiator Port");
 		break;
-	case SESTYP_SUBENC:
-		sprintf(rbuf, "Simple sub-enclosure");
+	case ELMTYP_SUBENC:
+		sprintf(rbuf, "Simple Subenclosure");
 		break;
-	case SESTYP_ARRAY:
-		sprintf(rbuf, "Array device");
+	case ELMTYP_ARRAY_DEV:
+		sprintf(rbuf, "Array Device Slot");
 		break;
-	case SESTYP_SASEXPANDER:
+	case ELMTYP_SAS_EXP:
 		sprintf(rbuf, "SAS Expander");
 		break;
-	case SESTYP_SASCONNECTOR:
+	case ELMTYP_SAS_CONN:
 		sprintf(rbuf, "SAS Connector");
 		break;
 	default:
@@ -134,31 +140,34 @@
 	static char rbuf[32];
 	switch (code & 0xf) {
 	case SES_OBJSTAT_UNSUPPORTED:
-		sprintf(rbuf, "status not supported");
+		sprintf(rbuf, "Unsupported");
 		break;
 	case SES_OBJSTAT_OK:
-		sprintf(rbuf, "ok");
+		sprintf(rbuf, "OK");
 		break;
 	case SES_OBJSTAT_CRIT:
-		sprintf(rbuf, "critical");
+		sprintf(rbuf, "Critical");
 		break;
 	case SES_OBJSTAT_NONCRIT:
-		sprintf(rbuf, "non-critical");
+		sprintf(rbuf, "Noncritical");
 		break;
 	case SES_OBJSTAT_UNRECOV:
-		sprintf(rbuf, "unrecoverable");
+		sprintf(rbuf, "Unrecoverable");
 		break;
 	case SES_OBJSTAT_NOTINSTALLED:
-		sprintf(rbuf, "not installed");
+		sprintf(rbuf, "Not Installed");
 		break;
 	case SES_OBJSTAT_UNKNOWN:
-		sprintf(rbuf, "unknown status");
+		sprintf(rbuf, "Unknown");
 		break;
 	case SES_OBJSTAT_NOTAVAIL:
-		sprintf(rbuf, "status not available");
+		sprintf(rbuf, "Not Available");
 		break;
+	case SES_OBJSTAT_NOACCESS:
+		sprintf(rbuf, "No Access Allowed");
+		break;
 	default:
-		sprintf(rbuf, "unknown status code %x", code & 0xf);
+		sprintf(rbuf, "<Status 0x%x>", code & 0xf);
 		break;
 	}
 	return (rbuf);
@@ -171,7 +180,11 @@
 	static char ebuf[256], *scode;
 
 	scode = scode2ascii(cstat[0]);
-	sprintf(ebuf, "Status=%s (bytes=0x%02x 0x%02x 0x%02x 0x%02x)",
-	    scode, cstat[0], cstat[1], cstat[2], cstat[3]);
+	sprintf(ebuf, "status: %s%s%s%s (0x%02x 0x%02x 0x%02x 0x%02x)",
+	    scode,
+	    (cstat[0] & 0x40) ? ", Prd.Fail" : "",
+	    (cstat[0] & 0x20) ? ", Disabled" : "",
+	    (cstat[0] & 0x10) ? ", Swapped" : "",
+	    cstat[0], cstat[1], cstat[2], cstat[3]);
 	return (ebuf);
 }

Modified: trunk/share/examples/ses/srcs/getencstat.c
===================================================================
--- trunk/share/examples/ses/srcs/getencstat.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/srcs/getencstat.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -33,12 +33,15 @@
  */
 
 #include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <fcntl.h>
-#include SESINC
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_enc.h>
 
 #include "eltsub.h"
 
@@ -45,8 +48,10 @@
 int
 main(int a, char **v)
 {
-	ses_object *objp;
-	ses_objstat ob;
+	encioc_element_t *objp;
+	encioc_elm_status_t ob;
+	encioc_elm_desc_t objd;
+	encioc_elm_devnames_t objdn;
 	int fd, nobj, f, i, verbose, quiet, errors;
 	u_char estat;
 
@@ -73,13 +78,13 @@
 			perror(*v);
 			continue;
 		}
-		if (ioctl(fd, SESIOC_GETNOBJ, (caddr_t) &nobj) < 0) {
-			perror("SESIOC_GETNOBJ");
+		if (ioctl(fd, ENCIOC_GETNELM, (caddr_t) &nobj) < 0) {
+			perror("ENCIOC_GETNELM");
 			(void) close(fd);
 			continue;
 		}
-		if (ioctl(fd, SESIOC_GETENCSTAT, (caddr_t) &estat) < 0) {
-			perror("SESIOC_GETENCSTAT");
+		if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &estat) < 0) {
+			perror("ENCIOC_GETENCSTAT");
 			(void) close(fd);
 			continue;
 		}
@@ -113,38 +118,64 @@
 			}
 		}
 		fprintf(stdout, ">\n");
-		objp = calloc(nobj, sizeof (ses_object));
+		objp = calloc(nobj, sizeof (encioc_element_t));
 		if (objp == NULL) {
 			perror("calloc");
 			(void) close(fd);
 			continue;
 		}
-                if (ioctl(fd, SESIOC_GETOBJMAP, (caddr_t) objp) < 0) {
-                        perror("SESIOC_GETOBJMAP");
+                if (ioctl(fd, ENCIOC_GETELMMAP, (caddr_t) objp) < 0) {
+                        perror("ENCIOC_GETELMMAP");
                         (void) close(fd);
                         continue;
                 }
 		for (i = 0; i < nobj; i++) {
-			ob.obj_id = objp[i].obj_id;
-			if (ioctl(fd, SESIOC_GETOBJSTAT, (caddr_t) &ob) < 0) {
-				perror("SESIOC_GETOBJSTAT");
+			ob.elm_idx = objp[i].elm_idx;
+			if (ioctl(fd, ENCIOC_GETELMSTAT, (caddr_t) &ob) < 0) {
+				perror("ENCIOC_GETELMSTAT");
 				(void) close(fd);
 				break;
 			}
-			if ((ob.cstat[0] & 0xf) == SES_OBJSTAT_OK) {
-				if (verbose) {
-					fprintf(stdout,
-					    "Element 0x%x: %s OK (%s)\n",
-					    ob.obj_id,
-					    geteltnm(objp[i].object_type),
-					    stat2ascii(objp[i].object_type,
-					    ob.cstat));
-				}
+			bzero(&objd, sizeof(objd));
+			objd.elm_idx = objp[i].elm_idx;
+			objd.elm_desc_len = UINT16_MAX;
+			objd.elm_desc_str = calloc(UINT16_MAX, sizeof(char));
+			if (objd.elm_desc_str == NULL) {
+				perror("calloc");
+				(void) close(fd);
 				continue;
 			}
-			fprintf(stdout, "Element 0x%x: %s, %s\n",
-			    ob.obj_id, geteltnm(objp[i].object_type),
-			    stat2ascii(objp[i].object_type, ob.cstat));
+			if (ioctl(fd, ENCIOC_GETELMDESC, (caddr_t)&objd) < 0) {
+				perror("ENCIOC_GETELMDESC");
+				(void) close(fd);
+				break;
+			}
+			bzero(&objdn, sizeof(objdn));
+			objdn.elm_idx = objp[i].elm_idx;
+			objdn.elm_names_size = 128;
+			objdn.elm_devnames = calloc(128, sizeof(char));
+			if (objdn.elm_devnames == NULL) {
+				perror("calloc");
+				(void) close(fd);
+				break;
+			}
+			/*
+			 * This ioctl isn't critical and has a good chance
+			 * of returning -1.
+			 */
+			(void)ioctl(fd, ENCIOC_GETELMDEVNAMES, (caddr_t)&objdn);
+			fprintf(stdout, "Element 0x%x: %s", ob.elm_idx,
+			    geteltnm(objp[i].elm_type));
+			fprintf(stdout, ", %s",
+			    stat2ascii(objp[i].elm_type, ob.cstat));
+			if (objd.elm_desc_len > 0)
+				fprintf(stdout, ", descriptor: '%s'",
+				    objd.elm_desc_str);
+			if (objdn.elm_names_len > 0)
+				fprintf(stdout, ", dev: '%s'",
+				    objdn.elm_devnames);
+			fprintf(stdout, "\n");
+			free(objdn.elm_devnames);
 		}
 		free(objp);
 		(void) close(fd);

Modified: trunk/share/examples/ses/srcs/getnobj.c
===================================================================
--- trunk/share/examples/ses/srcs/getnobj.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/srcs/getnobj.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -33,12 +33,15 @@
  */
 
 #include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-#include SESINC
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_ses.h>
 
 int
 main(int argc, char **argv)

Modified: trunk/share/examples/ses/srcs/getobjmap.c
===================================================================
--- trunk/share/examples/ses/srcs/getobjmap.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/srcs/getobjmap.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -33,11 +33,14 @@
  */
 
 #include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-#include SESINC
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_ses.h>
 
 #include "eltsub.h"
 

Modified: trunk/share/examples/ses/srcs/getobjstat.c
===================================================================
--- trunk/share/examples/ses/srcs/getobjstat.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/srcs/getobjstat.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -32,11 +32,14 @@
  * mjacob at feral.com
  */
 #include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-#include SESINC
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_ses.h>
 
 int
 main(int a, char **v)

Modified: trunk/share/examples/ses/srcs/inienc.c
===================================================================
--- trunk/share/examples/ses/srcs/inienc.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/srcs/inienc.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -33,11 +33,14 @@
  */
 
 #include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-#include SESINC
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_ses.h>
 
 int
 main(int a, char **v)

Modified: trunk/share/examples/ses/srcs/sesd.c
===================================================================
--- trunk/share/examples/ses/srcs/sesd.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/srcs/sesd.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -32,6 +32,8 @@
  * mjacob at feral.com
  */
 #include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <fcntl.h>
@@ -38,8 +40,10 @@
 #include <errno.h>
 #include <string.h>
 #include <syslog.h>
+#include <unistd.h>
 #include <sys/ioctl.h>
-#include SESINC
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_enc.h>
 
 #define	ALLSTAT (SES_ENCSTAT_UNRECOV | SES_ENCSTAT_CRITICAL | \
 	SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO)
@@ -52,9 +56,9 @@
 main(int a, char **v)
 {
 	static const char *usage =
-	    "usage: %s [ -d ] [ -t pollinterval ] device [ device ]\n";
-	int fd, polltime, dev, devbase, nodaemon;
-	ses_encstat stat, *carray;
+	    "usage: %s [ -c ] [ -d ] [ -t pollinterval ] device [ device ]\n";
+	int fd, polltime, dev, nodaemon, clear, c;
+	encioc_enc_status_t stat, nstat, *carray;
 
 	if (a < 2) {
 		fprintf(stderr, usage, *v);
@@ -61,41 +65,45 @@
 		return (1);
 	}
 
-	devbase = 1;
-
-	if (strcmp(v[1], "-d") == 0) {
-		nodaemon = 1;
-		devbase++;
-	} else {
-		nodaemon = 0;
+	nodaemon = 0;
+	polltime = 30;
+	clear = 0;
+	while ((c = getopt(a, v, "cdt:")) != -1) {
+		switch (c) {
+		case 'c':
+			clear = 1;
+			break;
+		case 'd':
+			nodaemon = 1;
+			break;
+		case 't':
+			polltime = atoi(optarg);
+			break;
+		default:
+			fprintf(stderr, usage, *v);
+			return (1);
+		}
 	}
 
-	if (a > 2 && strcmp(v[2], "-t") == 0) {
-		devbase += 2;
-		polltime = atoi(v[3]);
-	} else {
-		polltime = 30;
-	}
-
 	carray = malloc(a);
 	if (carray == NULL) {
 		perror("malloc");
 		return (1);
 	}
-	for (dev = devbase; dev < a; dev++)
-		carray[dev] = (ses_encstat) -1;
+	for (dev = optind; dev < a; dev++)
+		carray[dev] = (encioc_enc_status_t) -1;
 
 	/*
 	 * Check to make sure we can open all devices
 	 */
-	for (dev = devbase; dev < a; dev++) {
+	for (dev = optind; dev < a; dev++) {
 		fd = open(v[dev], O_RDWR);
 		if (fd < 0) {
 			perror(v[dev]);
 			return (1);
 		}
-		if (ioctl(fd, SESIOC_INIT, NULL) < 0) {
-			fprintf(stderr, "%s: SESIOC_INIT fails- %s\n",
+		if (ioctl(fd, ENCIOC_INIT, NULL) < 0) {
+			fprintf(stderr, "%s: ENCIOC_INIT fails- %s\n",
 			    v[dev], strerror(errno));
 			return (1);
 		}
@@ -112,7 +120,7 @@
 	}
 
 	for (;;) {
-		for (dev = devbase; dev < a; dev++) {
+		for (dev = optind; dev < a; dev++) {
 			fd = open(v[dev], O_RDWR);
 			if (fd < 0) {
 				syslog(LOG_ERR, "%s: %m", v[dev]);
@@ -122,12 +130,20 @@
 			/*
 			 * Get the actual current enclosure status.
 			 */
-			if (ioctl(fd, SESIOC_GETENCSTAT, (caddr_t) &stat) < 0) {
+			if (ioctl(fd, ENCIOC_GETENCSTAT, (caddr_t) &stat) < 0) {
 				syslog(LOG_ERR,
-				    "%s: SESIOC_GETENCSTAT- %m", v[dev]);
+				    "%s: ENCIOC_GETENCSTAT- %m", v[dev]);
 				(void) close(fd);
 				continue;
 			}
+			if (stat != 0 && clear) {
+				nstat = 0;
+				if (ioctl(fd, ENCIOC_SETENCSTAT,
+				    (caddr_t) &nstat) < 0) {
+					syslog(LOG_ERR,
+					    "%s: ENCIOC_SETENCSTAT- %m", v[dev]);
+				}
+			}
 			(void) close(fd);
 
 			if (stat == carray[dev])
@@ -139,9 +155,8 @@
 				    "%s: Enclosure Status OK", v[dev]);
 			}
 			if (stat & SES_ENCSTAT_INFO) {
-				syslog(LOG_INFO,
-				    "%s: Enclosure Status Has Information",
-				    v[dev]);
+				syslog(LOG_NOTICE,
+				    "%s: Enclosure Has Information", v[dev]);
 			}
 			if (stat & SES_ENCSTAT_NONCRITICAL) {
 				syslog(LOG_WARNING,

Modified: trunk/share/examples/ses/srcs/setencstat.c
===================================================================
--- trunk/share/examples/ses/srcs/setencstat.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/srcs/setencstat.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -33,11 +33,14 @@
  */
 
 #include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-#include SESINC
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_enc.h>
 
 int
 main(int a, char **v)
@@ -44,7 +47,7 @@
 {
 	int fd;
 	long val;
-	ses_encstat stat;
+	encioc_enc_status_t stat;
 
 	if (a != 3) {
 		fprintf(stderr, "usage: %s device enclosure_status\n", *v);
@@ -57,9 +60,9 @@
 	}
 	
 	val =  strtol(v[2], NULL, 0);
-	stat = (ses_encstat) val;
-	if (ioctl(fd, SESIOC_SETENCSTAT, (caddr_t) &stat) < 0) {
-		perror("SESIOC_SETENCSTAT");
+	stat = (encioc_enc_status_t)val;
+	if (ioctl(fd, ENCIOC_SETENCSTAT, (caddr_t) &stat) < 0) {
+		perror("ENCIOC_SETENCSTAT");
 	}
 	(void) close(fd);
 	return (0);

Modified: trunk/share/examples/ses/srcs/setobjstat.c
===================================================================
--- trunk/share/examples/ses/srcs/setobjstat.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/share/examples/ses/srcs/setobjstat.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -33,11 +33,14 @@
  */
 
 #include <unistd.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
-#include SESINC
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_enc.h>
 
 int
 main(int a, char **v)
@@ -44,7 +47,7 @@
 {
 	int fd;
 	int i;
-	ses_objstat obj;
+	encioc_elm_status_t obj;
 	long cvt;
 	char *x;
 
@@ -64,7 +67,7 @@
 	if (x == v[2]) {
 		goto usage;
 	}
-	obj.obj_id = cvt;
+	obj.elm_idx = cvt;
 	for (i = 0; i < 4; i++) {
 		x = v[3 + i];
 		cvt = strtol(v[3 + i],  &x, 0);
@@ -73,8 +76,8 @@
 		}
 		obj.cstat[i] = cvt;
 	}
-	if (ioctl(fd, SESIOC_SETOBJSTAT, (caddr_t) &obj) < 0) {
-		perror("SESIOC_SETOBJSTAT");
+	if (ioctl(fd, ENCIOC_SETELMSTAT, (caddr_t) &obj) < 0) {
+		perror("ENCIOC_SETELMSTAT");
 	}
 	(void) close(fd);
 	return (0);

Added: trunk/sys/cam/scsi/scsi_enc.c
===================================================================
--- trunk/sys/cam/scsi/scsi_enc.c	                        (rev 0)
+++ trunk/sys/cam/scsi/scsi_enc.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -0,0 +1,990 @@
+/*-
+ * 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>
+
+MALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers");
+
+/* Enclosure type independent driver */
+
+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 the ses daemon to terminate. */
+		wakeup(enc->enc_daemon);
+	}
+	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, CAM_PRIORITY_NORMAL);
+	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 ENCLOSURE device.
+ */
+
+#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 ((iqd[0] & 0x1f) == T_ENCLOSURE) {
+		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);
+	cam_periph_release(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);
+
+	if (cam_periph_acquire(enc->periph) != CAM_REQ_CMP)
+		return (ENXIO);
+
+	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);
+	} else
+		cam_periph_release(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 (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_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 starting 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_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);
+}
+


Property changes on: trunk/sys/cam/scsi/scsi_enc.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/cam/scsi/scsi_enc.h
===================================================================
--- trunk/sys/cam/scsi/scsi_enc.h	                        (rev 0)
+++ trunk/sys/cam/scsi/scsi_enc.h	2016-09-26 02:34:48 UTC (rev 8749)
@@ -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_ */


Property changes on: trunk/sys/cam/scsi/scsi_enc.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/cam/scsi/scsi_enc_internal.h
===================================================================
--- trunk/sys/cam/scsi/scsi_enc_internal.h	                        (rev 0)
+++ trunk/sys/cam/scsi/scsi_enc_internal.h	2016-09-26 02:34:48 UTC (rev 8749)
@@ -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__ */


Property changes on: trunk/sys/cam/scsi/scsi_enc_internal.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/cam/scsi/scsi_enc_safte.c
===================================================================
--- trunk/sys/cam/scsi/scsi_enc_safte.c	                        (rev 0)
+++ trunk/sys/cam/scsi/scsi_enc_safte.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -0,0 +1,1135 @@
+/*-
+ * 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>
+
+/*
+ * 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_VLOG(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_VLOG(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_VLOG(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_VLOG(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_VLOG(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);
+}
+


Property changes on: trunk/sys/cam/scsi/scsi_enc_safte.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/sys/cam/scsi/scsi_enc_ses.c
===================================================================
--- trunk/sys/cam/scsi/scsi_enc_ses.c	                        (rev 0)
+++ trunk/sys/cam/scsi/scsi_enc_ses.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -0,0 +1,2831 @@
+/*-
+ * 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/ctype.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>
+
+/* 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
+#define	SES_FLAG_DESC		0x04
+
+	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;
+	struct cam_sim		    *sim;
+
+	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_unlocked(&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);
+
+	sim = xpt_path_sim(cdm.ccb_h.path);
+	CAM_SIM_LOCK(sim);
+	xpt_free_path(cdm.ccb_h.path);
+	CAM_SIM_UNLOCK(sim);
+}
+
+/**
+ * \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);
+	cam_periph_lock(enc->periph);
+	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++;
+	}
+	cam_periph_unlock(enc->periph);
+	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 i, ret;
+	struct sbuf sb;
+	uint8_t *devid, *elmaddr;
+	ses_element_t *elmpriv;
+	const char *c;
+
+	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;
+	}
+	cam_periph_lock(enc->periph);
+	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);
+	cam_periph_unlock(enc->periph);
+	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 at 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@");
+		for (i = 0, c = elmpriv->descr; i < elmpriv->descr_len;
+		    i++, c++) {
+			if (!isprint(*c) || isspace(*c) || *c == '/')
+				sbuf_putc(&sb, '_');
+			else
+				sbuf_putc(&sb, *c);
+		}
+	}
+	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_VLOG(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_VLOG(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_VLOG(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_VLOG(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] == SesElementDescriptor)
+			ses->ses_flags |= SES_FLAG_DESC;
+		else if (page->params[i] == SesAddlElementStatus)
+			ses->ses_flags |= SES_FLAG_ADDLSTATUS;
+	}
+
+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_VLOG(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_VLOG(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_VLOG(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_VLOG(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_VLOG(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_cache_free(enc, enc_cache);
+	else {
+		enc_update_request(enc, SES_UPDATE_GETSTATUS);
+		if (ses->ses_flags & SES_FLAG_DESC)
+			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_VLOG(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_VLOG(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_VLOG(enc, "Status page, length insufficient for "
+			"expected number of objects\n");
+	} else {
+		if (cur_stat <= last_stat)
+			ENC_VLOG(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_VLOG(enc, "Runt Additional Element Status Page\n");
+		goto out;
+	}
+	if (length > xfer_len) {
+		ENC_VLOG(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_VLOG(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_VLOG(enc, "Element %d Beyond End "
+			    "of Additional Element Status Descriptors\n",
+			    iter.global_element_index);
+			break;
+		}
+
+		/* 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_VLOG(enc, "Element %d: Unknown Additional Element "
+			    "Protocol 0x%x\n", iter.global_element_index,
+			    ses_elm_addlstatus_proto(elmpriv->addl.hdr));
+			break;
+		}
+
+		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_VLOG(enc, "Runt Element Descriptor Page\n");
+		goto out;
+	}
+	if (plength > xfer_len) {
+		ENC_VLOG(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_VLOG(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_VLOG(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_VLOG(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_VLOG(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_VLOG(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_VLOG(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_VLOG(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_VLOG(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_VLOG(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);
+}
+


Property changes on: trunk/sys/cam/scsi/scsi_enc_ses.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+MidnightBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/sys/cam/scsi/scsi_pt.c
===================================================================
--- trunk/sys/cam/scsi/scsi_pt.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/sys/cam/scsi/scsi_pt.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -255,11 +255,6 @@
 	struct ccb_pathinq cpi;
 
 	cgd = (struct ccb_getdev *)arg;
-	if (periph == NULL) {
-		printf("ptregister: periph was NULL!!\n");
-		return(CAM_REQ_CMP_ERR);
-	}
-
 	if (cgd == NULL) {
 		printf("ptregister: no getdev CCB, can't register device\n");
 		return(CAM_REQ_CMP_ERR);

Deleted: trunk/sys/cam/scsi/scsi_ses.c
===================================================================
--- trunk/sys/cam/scsi/scsi_ses.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/sys/cam/scsi/scsi_ses.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -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>
-__MBSDID("$MidnightBSD$");
-
-#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>
-
-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);
-}

Modified: trunk/sys/cam/scsi/scsi_ses.h
===================================================================
--- trunk/sys/cam/scsi/scsi_ses.h	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/sys/cam/scsi/scsi_ses.h	2016-09-26 02:34:48 UTC (rev 8749)
@@ -29,108 +29,2132 @@
  *
  */
 
-#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;
+/*========================== Field Extraction Macros =========================*/
+#define MK_ENUM(S, F, SUFFIX) S ## _ ## F ## SUFFIX
 
-/* 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
+#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));					    \
+}
 
-/*
- * 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_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);
+}
+
+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
 #define	SES_OBJSTAT_OK			1
@@ -140,6 +2164,7 @@
 #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 @@
 	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_ */

Modified: trunk/sys/conf/files
===================================================================
--- trunk/sys/conf/files	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/sys/conf/files	2016-09-26 02:34:48 UTC (rev 8749)
@@ -134,7 +134,9 @@
 cam/scsi/scsi_pass.c		optional pass
 cam/scsi/scsi_pt.c		optional pt
 cam/scsi/scsi_sa.c		optional sa
-cam/scsi/scsi_ses.c		optional ses
+cam/scsi/scsi_enc.c		optional ses
+cam/scsi/scsi_enc_ses.c		optional ses
+cam/scsi/scsi_enc_safte.c	optional ses
 cam/scsi/scsi_sg.c		optional sg
 cam/scsi/scsi_targ_bh.c		optional targbh
 cam/scsi/scsi_target.c		optional targ

Modified: trunk/sys/fs/devfs/devfs_vnops.c
===================================================================
--- trunk/sys/fs/devfs/devfs_vnops.c	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/sys/fs/devfs/devfs_vnops.c	2016-09-26 02:34:48 UTC (rev 8749)
@@ -1276,8 +1276,53 @@
 devfs_readlink(struct vop_readlink_args *ap)
 {
 	struct devfs_dirent *de;
+	struct cdev_priv *cdp;
 
 	de = ap->a_vp->v_data;
+	cdp = de->de_cdp;
+
+	if (cdp != NULL && (cdp->cdp_c.si_flags & SI_ALIAS) != 0) {
+		struct devfs_mount *dmp;
+		struct prison *pr;
+		char *mp;
+		int mp_len;
+		int pr_path_len;
+		int err;
+
+		/*
+		 * For device aliases, construct an absolute symlink (to
+		 * shorten its length and avoid the ugliness of a relative
+		 * link) by prepending the fully qualified path to the root
+		 * of this devfs.  For a non-jailed process, the devfs root
+		 * is our mount point.  For a jailed process, we must remove
+		 * any jail prefix in our mount point so that our response
+		 * matches the user process's world view.
+		 */
+		dmp = VFSTODEVFS(ap->a_vp->v_mount);
+		mp = dmp->dm_mount->mnt_stat.f_mntonname;
+		mp_len = strlen(mp);
+
+		pr = ap->a_cred->cr_prison;
+		pr_path_len = strlen(pr->pr_path);
+
+		if (strncmp(pr->pr_path, mp, pr_path_len) == 0
+		 && mp[pr_path_len] == '/') {
+			mp += pr_path_len;
+			mp_len -= pr_path_len;
+		}
+
+		err = uiomove(mp, mp_len, ap->a_uio);
+		if (err != 0)
+			return (err);
+
+		/*
+		 * Devfs cannot be the root file system, so its
+		 * mount point must always be terminated by a '/'.
+		 */
+		err = uiomove("/", 1, ap->a_uio);
+		if (err != 0)
+			return (err);
+	}
 	return (uiomove(de->de_symlink, strlen(de->de_symlink), ap->a_uio));
 }
 

Modified: trunk/sys/modules/cam/Makefile
===================================================================
--- trunk/sys/modules/cam/Makefile	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/sys/modules/cam/Makefile	2016-09-26 02:34:48 UTC (rev 8749)
@@ -14,7 +14,6 @@
 SRCS+=	opt_cd.h
 SRCS+=	opt_pt.h
 SRCS+=	opt_sa.h
-SRCS+=	opt_ses.h
 SRCS+=	device_if.h bus_if.h vnode_if.h
 SRCS+=	cam.c
 .if exists($S/${MACHINE}/${MACHINE}/cam_machdep.c)
@@ -26,7 +25,9 @@
 SRCS+=	scsi_pass.c
 SRCS+=	scsi_pt.c
 SRCS+=	scsi_sa.c
-SRCS+=	scsi_ses.c
+SRCS+=	scsi_enc.c
+SRCS+=	scsi_enc_ses.c
+SRCS+=	scsi_enc_safte.c
 SRCS+=	scsi_sg.c
 SRCS+=	scsi_targ_bh.c scsi_target.c
 SRCS+=	scsi_xpt.c

Modified: trunk/usr.bin/kdump/mkioctls
===================================================================
--- trunk/usr.bin/kdump/mkioctls	2016-09-26 02:29:23 UTC (rev 8748)
+++ trunk/usr.bin/kdump/mkioctls	2016-09-26 02:34:48 UTC (rev 8749)
@@ -54,6 +54,8 @@
 	print "#include <netinet6/ip6_mroute.h>"
 	print "#include <stdio.h>"
 	print "#include <cam/cam.h>"
+	print "#include <stddef.h>"
+	print "#include <stdint.h>"
 	print ""
 	print "const char *ioctlname(u_long val);"
 	print ""



More information about the Midnightbsd-cvs mailing list