From 30e1f54418d6ce9547a957402c981ff909fbee8c Mon Sep 17 00:00:00 2001 From: mav Date: Wed, 6 Feb 2013 22:07:38 +0000 Subject: [PATCH] MFC r235911, r235980, r238739, r238740, r238894, r239213, r241488, r241952, r242173, r242621, r242634, r242638, r242647, r242720, r244418, r244508, r245891: Revamp the CAM enclosure services driver. This updated driver uses an in-kernel daemon to track state changes and publishes physical path location information for disk elements into the CAM device database. Sponsored by: Spectra Logic Corporation Sponsored by: iXsystems, Inc. git-svn-id: svn://svn.freebsd.org/base/stable/9@246437 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- share/examples/ses/Makefile.inc | 1 - share/examples/ses/sesd/sesd.0 | 7 +- share/examples/ses/srcs/eltsub.c | 115 +- share/examples/ses/srcs/getencstat.c | 81 +- share/examples/ses/srcs/getnobj.c | 5 +- share/examples/ses/srcs/getobjmap.c | 5 +- share/examples/ses/srcs/getobjstat.c | 5 +- share/examples/ses/srcs/inienc.c | 5 +- share/examples/ses/srcs/sesd.c | 73 +- share/examples/ses/srcs/setencstat.c | 13 +- share/examples/ses/srcs/setobjstat.c | 13 +- sys/cam/scsi/scsi_enc.c | 990 +++++++++ sys/cam/scsi/scsi_enc.h | 219 ++ sys/cam/scsi/scsi_enc_internal.h | 230 +++ sys/cam/scsi/scsi_enc_safte.c | 1135 +++++++++++ sys/cam/scsi/scsi_enc_ses.c | 2831 ++++++++++++++++++++++++++ sys/cam/scsi/scsi_pt.c | 5 - sys/cam/scsi/scsi_ses.c | 2533 ----------------------- sys/cam/scsi/scsi_ses.h | 2448 +++++++++++++++++++++- sys/conf/files | 4 +- sys/fs/devfs/devfs_vnops.c | 45 + sys/modules/cam/Makefile | 5 +- usr.bin/kdump/mkioctls | 2 + 23 files changed, 8012 insertions(+), 2758 deletions(-) create mode 100644 sys/cam/scsi/scsi_enc.c create mode 100644 sys/cam/scsi/scsi_enc.h create mode 100644 sys/cam/scsi/scsi_enc_internal.h create mode 100644 sys/cam/scsi/scsi_enc_safte.c create mode 100644 sys/cam/scsi/scsi_enc_ses.c delete mode 100644 sys/cam/scsi/scsi_ses.c diff --git a/share/examples/ses/Makefile.inc b/share/examples/ses/Makefile.inc index 5ff1cd430..2b1e85874 100644 --- a/share/examples/ses/Makefile.inc +++ b/share/examples/ses/Makefile.inc @@ -32,7 +32,6 @@ # mjacob@feral.com # -CFLAGS+= -I/usr/include/cam/scsi -DSESINC="" BINDIR?= /usr/sbin CLEANFILES+= ${MAN} diff --git a/share/examples/ses/sesd/sesd.0 b/share/examples/ses/sesd/sesd.0 index 29c0084da..7a548c72d 100644 --- a/share/examples/ses/sesd/sesd.0 +++ b/share/examples/ses/sesd/sesd.0 @@ -31,7 +31,7 @@ .\" .\" $FreeBSD$ .\" -.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 @@ poll each device for a change in state. .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 diff --git a/share/examples/ses/srcs/eltsub.c b/share/examples/ses/srcs/eltsub.c index 8500fff04..c664895b0 100644 --- a/share/examples/ses/srcs/eltsub.c +++ b/share/examples/ses/srcs/eltsub.c @@ -33,10 +33,13 @@ */ #include +#include +#include #include #include #include -#include SESINC +#include +#include #include "eltsub.h" @@ -46,79 +49,82 @@ geteltnm(int type) 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 @@ scode2ascii(u_char code) 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, "", code & 0xf); break; } return (rbuf); @@ -171,7 +180,11 @@ stat2ascii(int eletype __unused, u_char *cstat) 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); } diff --git a/share/examples/ses/srcs/getencstat.c b/share/examples/ses/srcs/getencstat.c index 3514fe4e7..9048f7355 100644 --- a/share/examples/ses/srcs/getencstat.c +++ b/share/examples/ses/srcs/getencstat.c @@ -33,20 +33,25 @@ */ #include +#include +#include #include #include #include #include #include -#include SESINC +#include +#include #include "eltsub.h" 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 @@ main(int a, char **v) 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 @@ main(int a, char **v) } } 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); diff --git a/share/examples/ses/srcs/getnobj.c b/share/examples/ses/srcs/getnobj.c index 17a26c62b..92d3458de 100644 --- a/share/examples/ses/srcs/getnobj.c +++ b/share/examples/ses/srcs/getnobj.c @@ -33,12 +33,15 @@ */ #include +#include +#include #include #include #include #include #include -#include SESINC +#include +#include int main(int argc, char **argv) diff --git a/share/examples/ses/srcs/getobjmap.c b/share/examples/ses/srcs/getobjmap.c index 9798b4c87..fbcc12e63 100644 --- a/share/examples/ses/srcs/getobjmap.c +++ b/share/examples/ses/srcs/getobjmap.c @@ -33,11 +33,14 @@ */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include #include "eltsub.h" diff --git a/share/examples/ses/srcs/getobjstat.c b/share/examples/ses/srcs/getobjstat.c index 99fb18542..d49f6f97d 100644 --- a/share/examples/ses/srcs/getobjstat.c +++ b/share/examples/ses/srcs/getobjstat.c @@ -32,11 +32,14 @@ * mjacob@feral.com */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include int main(int a, char **v) diff --git a/share/examples/ses/srcs/inienc.c b/share/examples/ses/srcs/inienc.c index 7d6cc220b..f418787b3 100644 --- a/share/examples/ses/srcs/inienc.c +++ b/share/examples/ses/srcs/inienc.c @@ -33,11 +33,14 @@ */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include int main(int a, char **v) diff --git a/share/examples/ses/srcs/sesd.c b/share/examples/ses/srcs/sesd.c index 0793077d2..acf9508fe 100644 --- a/share/examples/ses/srcs/sesd.c +++ b/share/examples/ses/srcs/sesd.c @@ -32,14 +32,18 @@ * mjacob@feral.com */ #include +#include +#include #include #include #include #include #include #include +#include #include -#include SESINC +#include +#include #define ALLSTAT (SES_ENCSTAT_UNRECOV | SES_ENCSTAT_CRITICAL | \ SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) @@ -52,29 +56,33 @@ int 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); return (1); } - devbase = 1; - - if (strcmp(v[1], "-d") == 0) { - nodaemon = 1; - devbase++; - } else { - nodaemon = 0; - } - - if (a > 2 && strcmp(v[2], "-t") == 0) { - devbase += 2; - polltime = atoi(v[3]); - } else { - polltime = 30; + 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); + } } carray = malloc(a); @@ -82,20 +90,20 @@ main(int a, char **v) 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 @@ main(int a, char **v) } 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 @@ main(int a, char **v) /* * 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 @@ main(int a, char **v) "%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, diff --git a/share/examples/ses/srcs/setencstat.c b/share/examples/ses/srcs/setencstat.c index 127f68fa4..87a7fa263 100644 --- a/share/examples/ses/srcs/setencstat.c +++ b/share/examples/ses/srcs/setencstat.c @@ -33,18 +33,21 @@ */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include int main(int a, char **v) { 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 @@ main(int a, char **v) } 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); diff --git a/share/examples/ses/srcs/setobjstat.c b/share/examples/ses/srcs/setobjstat.c index 08fdb7b48..26a5dd19e 100644 --- a/share/examples/ses/srcs/setobjstat.c +++ b/share/examples/ses/srcs/setobjstat.c @@ -33,18 +33,21 @@ */ #include +#include +#include #include #include #include #include -#include SESINC +#include +#include int main(int a, char **v) { int fd; int i; - ses_objstat obj; + encioc_elm_status_t obj; long cvt; char *x; @@ -64,7 +67,7 @@ usage: 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 @@ usage: } 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); diff --git a/sys/cam/scsi/scsi_enc.c b/sys/cam/scsi/scsi_enc.c new file mode 100644 index 000000000..526542ff5 --- /dev/null +++ b/sys/cam/scsi/scsi_enc.c @@ -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 +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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); +} + diff --git a/sys/cam/scsi/scsi_enc.h b/sys/cam/scsi/scsi_enc.h new file mode 100644 index 000000000..a5345f239 --- /dev/null +++ b/sys/cam/scsi/scsi_enc.h @@ -0,0 +1,219 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2000 by Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * the GNU Public License ("GPL"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#ifndef _SCSI_ENC_H_ +#define _SCSI_ENC_H_ + +#include + +#define ENCIOC ('s' - 040) +#define ENCIOC_GETNELM _IO(ENCIOC, 1) +#define ENCIOC_GETELMMAP _IO(ENCIOC, 2) +#define ENCIOC_GETENCSTAT _IO(ENCIOC, 3) +#define ENCIOC_SETENCSTAT _IO(ENCIOC, 4) +#define ENCIOC_GETELMSTAT _IO(ENCIOC, 5) +#define ENCIOC_SETELMSTAT _IO(ENCIOC, 6) +#define ENCIOC_GETTEXT _IO(ENCIOC, 7) +#define ENCIOC_INIT _IO(ENCIOC, 8) +#define ENCIOC_GETELMDESC _IO(ENCIOC, 9) +#define ENCIOC_GETELMDEVNAMES _IO(ENCIOC, 10) +#define ENCIOC_GETSTRING _IO(ENCIOC, 11) +#define ENCIOC_SETSTRING _IO(ENCIOC, 12) + +/* + * Platform Independent Definitions for enclosure devices. + */ +/* + * SCSI Based Environmental Services Application Defines + * + * Based almost entirely on SCSI-3 ENC Revision 8A specification, + * but slightly abstracted as the underlying device may in fact + * be a SAF-TE or vendor unique device. + */ +/* + * ENC Driver Operations: + * (The defines themselves are platform and access method specific) + * + * ENCIOC_GETNELM + * ENCIOC_GETELMMAP + * ENCIOC_GETENCSTAT + * ENCIOC_SETENCSTAT + * ENCIOC_GETELMSTAT + * ENCIOC_SETELMSTAT + * ENCIOC_INIT + * + * + * An application finds out how many elements an enclosure instance + * is managing by performing a ENCIOC_GETNELM operation. It then + * performs a ENCIOC_GETELMMAP to get the map that contains the + * elment identifiers for all elements (see encioc_element_t below). + * This information is static. + * + * The application may perform ENCIOC_GETELMSTAT operations to retrieve + * status on an element (see the enc_elm_status_t structure below), + * ENCIOC_SETELMSTAT operations to set status for an element. + * + * Similarly, overall enclosure status me be fetched or set via + * ENCIOC_GETENCSTAT or ENCIOC_SETENCSTAT operations (see encioc_enc_status_t + * below). + * + * Readers should note that there is nothing that requires either a set + * or a clear operation to actually latch and do anything in the target. + * + * A ENCIOC_INIT operation causes the enclosure to be initialized. + */ + +/* Element Types */ +typedef enum { + ELMTYP_UNSPECIFIED = 0x00, + ELMTYP_DEVICE = 0x01, + ELMTYP_POWER = 0x02, + ELMTYP_FAN = 0x03, + ELMTYP_THERM = 0x04, + ELMTYP_DOORLOCK = 0x05, + ELMTYP_ALARM = 0x06, + ELMTYP_ESCC = 0x07, /* Enclosure SCC */ + ELMTYP_SCC = 0x08, /* SCC */ + ELMTYP_NVRAM = 0x09, + ELMTYP_INV_OP_REASON = 0x0a, + ELMTYP_UPS = 0x0b, + ELMTYP_DISPLAY = 0x0c, + ELMTYP_KEYPAD = 0x0d, + ELMTYP_ENCLOSURE = 0x0e, + ELMTYP_SCSIXVR = 0x0f, + ELMTYP_LANGUAGE = 0x10, + ELMTYP_COMPORT = 0x11, + ELMTYP_VOM = 0x12, + ELMTYP_AMMETER = 0x13, + ELMTYP_SCSI_TGT = 0x14, + ELMTYP_SCSI_INI = 0x15, + ELMTYP_SUBENC = 0x16, + ELMTYP_ARRAY_DEV = 0x17, + ELMTYP_SAS_EXP = 0x18, /* SAS expander */ + ELMTYP_SAS_CONN = 0x19 /* SAS connector */ +} elm_type_t; + +typedef struct encioc_element { + /* Element Index */ + unsigned int elm_idx; + + /* ID of SubEnclosure containing Element*/ + unsigned int elm_subenc_id; + + /* Element Type */ + elm_type_t elm_type; +} encioc_element_t; + +/* + * Overall Enclosure Status + */ +typedef unsigned char encioc_enc_status_t; + +/* + * Element Status + */ +typedef struct encioc_elm_status { + unsigned int elm_idx; + unsigned char cstat[4]; +} encioc_elm_status_t; + +/* + * ENC String structure, for StringIn and StringOut commands; use this with + * the ENCIOC_GETSTRING and ENCIOC_SETSTRING ioctls. + */ +typedef struct encioc_string { + size_t bufsiz; /* IN/OUT: length of string provided/returned */ +#define ENC_STRING_MAX 0xffff + uint8_t *buf; /* IN/OUT: string */ +} encioc_string_t; + +/*============================================================================*/ + +/* + * SES v2 r20 6.1.10 (pg 39) - Element Descriptor diagnostic page + * Tables 21, 22, and 23 + */ +typedef struct encioc_elm_desc { + unsigned int elm_idx; /* IN: elment requested */ + uint16_t elm_desc_len; /* IN: buffer size; OUT: bytes written */ + char *elm_desc_str; /* IN/OUT: buffer for descriptor data */ +} encioc_elm_desc_t; + +/* + * ENCIOC_GETELMDEVNAMES: + * ioctl structure to get an element's device names, if available + */ +typedef struct encioc_elm_devnames { + unsigned int elm_idx; /* IN: element index */ + size_t elm_names_size;/* IN: size of elm_devnames */ + size_t elm_names_len; /* OUT: actual size returned */ + /* + * IN/OUT: comma separated list of peripheral driver + * instances servicing this element. + */ + char *elm_devnames; +} encioc_elm_devnames_t; + +/* ioctl structure for requesting FC info for a port */ +typedef struct encioc_elm_fc_port { + unsigned int elm_idx; + unsigned int port_idx; + struct ses_elm_fc_port port_data; +} encioc_elm_fc_port_t; + +/* ioctl structure for requesting SAS info for element phys */ +typedef struct encioc_elm_sas_device_phy { + unsigned int elm_idx; + unsigned int phy_idx; + struct ses_elm_sas_device_phy phy_data; +} enioc_elm_sas_phy_t; + +/* ioctl structure for requesting SAS info for an expander phy */ +typedef struct encioc_elm_sas_expander_phy { + unsigned int elm_idx; + unsigned int phy_idx; + struct ses_elm_sas_expander_phy phy_data; +} encioc_elm_sas_expander_phy_t; + +/* ioctl structure for requesting SAS info for a port phy */ +typedef struct encioc_elm_sas_port_phy { + unsigned int elm_idx; + unsigned int phy_idx; + struct ses_elm_sas_port_phy phy_data; +} enioc_elm_sas_port_phy_t; + +/* ioctl structure for requesting additional status for an element */ +typedef struct encioc_addl_status { + unsigned int elm_idx; + union ses_elm_addlstatus_descr_hdr addl_hdr; + union ses_elm_addlstatus_proto_hdr proto_hdr; +} enioc_addl_status_t; + +#endif /* _SCSI_ENC_H_ */ diff --git a/sys/cam/scsi/scsi_enc_internal.h b/sys/cam/scsi/scsi_enc_internal.h new file mode 100644 index 000000000..865b8a143 --- /dev/null +++ b/sys/cam/scsi/scsi_enc_internal.h @@ -0,0 +1,230 @@ +/*- + * Copyright (c) 2000 Matthew Jacob + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * This file contains definitions only intended for use within + * sys/cam/scsi/scsi_enc*.c, and not in other kernel components. + */ + +#ifndef __SCSI_ENC_INTERNAL_H__ +#define __SCSI_ENC_INTERNAL_H__ + +typedef struct enc_element { + uint32_t + enctype : 8, /* enclosure type */ + subenclosure : 8, /* subenclosure id */ + svalid : 1, /* enclosure information valid */ + overall_status_elem: 1,/* + * This object represents generic + * status about all objects of this + * type. + */ + priv : 14; /* private data, per object */ + uint8_t encstat[4]; /* state && stats */ + uint8_t *physical_path; /* Device physical path data. */ + u_int physical_path_len; /* Length of device path data. */ + void *elm_private; /* per-type object data */ +} enc_element_t; + +typedef enum { + ENC_NONE, + ENC_SES_SCSI2, + ENC_SES, + ENC_SES_PASSTHROUGH, + ENC_SEN, + ENC_SAFT, + ENC_SEMB_SES, + ENC_SEMB_SAFT +} enctyp; + +/* Platform Independent Driver Internal Definitions for enclosure devices. */ +typedef struct enc_softc enc_softc_t; + +struct enc_fsm_state; +typedef int fsm_fill_handler_t(enc_softc_t *ssc, + struct enc_fsm_state *state, + union ccb *ccb, + uint8_t *buf); +typedef int fsm_error_handler_t(union ccb *ccb, uint32_t cflags, + uint32_t sflags); +typedef int fsm_done_handler_t(enc_softc_t *ssc, + struct enc_fsm_state *state, union ccb *ccb, + uint8_t **bufp, int error, int xfer_len); + +struct enc_fsm_state { + const char *name; + int page_code; + size_t buf_size; + uint32_t timeout; + fsm_fill_handler_t *fill; + fsm_done_handler_t *done; + fsm_error_handler_t *error; +}; + +typedef int (enc_softc_init_t)(enc_softc_t *); +typedef void (enc_softc_invalidate_t)(enc_softc_t *); +typedef void (enc_softc_cleanup_t)(enc_softc_t *); +typedef int (enc_init_enc_t)(enc_softc_t *); +typedef int (enc_get_enc_status_t)(enc_softc_t *, int); +typedef int (enc_set_enc_status_t)(enc_softc_t *, encioc_enc_status_t, int); +typedef int (enc_get_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int); +typedef int (enc_set_elm_status_t)(enc_softc_t *, encioc_elm_status_t *, int); +typedef int (enc_get_elm_desc_t)(enc_softc_t *, encioc_elm_desc_t *); +typedef int (enc_get_elm_devnames_t)(enc_softc_t *, encioc_elm_devnames_t *); +typedef int (enc_handle_string_t)(enc_softc_t *, encioc_string_t *, int); +typedef void (enc_device_found_t)(enc_softc_t *); +typedef void (enc_poll_status_t)(enc_softc_t *); + +struct enc_vec { + enc_softc_invalidate_t *softc_invalidate; + enc_softc_cleanup_t *softc_cleanup; + enc_init_enc_t *init_enc; + enc_get_enc_status_t *get_enc_status; + enc_set_enc_status_t *set_enc_status; + enc_get_elm_status_t *get_elm_status; + enc_set_elm_status_t *set_elm_status; + enc_get_elm_desc_t *get_elm_desc; + enc_get_elm_devnames_t *get_elm_devnames; + enc_handle_string_t *handle_string; + enc_device_found_t *device_found; + enc_poll_status_t *poll_status; +}; + +typedef struct enc_cache { + enc_element_t *elm_map; /* objects */ + int nelms; /* number of objects */ + encioc_enc_status_t enc_status; /* overall status */ + void *private; /* per-type private data */ +} enc_cache_t; + +/* Enclosure instance toplevel structure */ +struct enc_softc { + enctyp enc_type; /* type of enclosure */ + struct enc_vec enc_vec; /* vector to handlers */ + void *enc_private; /* per-type private data */ + + /** + * "Published" configuration and state data available to + * external consumers. + */ + enc_cache_t enc_cache; + + /** + * Configuration and state data being actively updated + * by the enclosure daemon. + */ + enc_cache_t enc_daemon_cache; + + struct sx enc_cache_lock; + uint8_t enc_flags; +#define ENC_FLAG_INVALID 0x01 +#define ENC_FLAG_INITIALIZED 0x02 +#define ENC_FLAG_SHUTDOWN 0x04 + union ccb saved_ccb; + struct cdev *enc_dev; + struct cam_periph *periph; + + /* Bitmap of pending operations. */ + uint32_t pending_actions; + + /* The action on which the state machine is currently working. */ + uint32_t current_action; +#define ENC_UPDATE_NONE 0x00 +#define ENC_UPDATE_INVALID 0xff + + /* Callout for auto-updating enclosure status */ + struct callout status_updater; + + struct proc *enc_daemon; + + struct enc_fsm_state *enc_fsm_states; + + struct intr_config_hook enc_boot_hold_ch; +}; + +static inline enc_cache_t * +enc_other_cache(enc_softc_t *enc, enc_cache_t *primary) +{ + return (primary == &enc->enc_cache + ? &enc->enc_daemon_cache : &enc->enc_cache); +} + +/* SES Management mode page - SES2r20 Table 59 */ +struct ses_mgmt_mode_page { + struct scsi_mode_header_6 header; + struct scsi_mode_blk_desc blk_desc; + uint8_t byte0; /* ps : 1, spf : 1, page_code : 6 */ +#define SES_MGMT_MODE_PAGE_CODE 0x14 + uint8_t length; +#define SES_MGMT_MODE_PAGE_LEN 6 + uint8_t reserved[3]; + uint8_t byte5; /* reserved : 7, enbltc : 1 */ +#define SES_MGMT_TIMED_COMP_EN 0x1 + uint8_t max_comp_time[2]; +}; + +/* Enclosure core interface for sub-drivers */ +int enc_runcmd(struct enc_softc *, char *, int, char *, int *); +void enc_log(struct enc_softc *, const char *, ...); +void enc_done(struct cam_periph *, union ccb *); +int enc_error(union ccb *, uint32_t, uint32_t); +void enc_update_request(enc_softc_t *, uint32_t); + +/* SES Native interface */ +enc_softc_init_t ses_softc_init; + +/* SAF-TE interface */ +enc_softc_init_t safte_softc_init; + +/* Helper macros */ +MALLOC_DECLARE(M_SCSIENC); +#define ENC_CFLAGS CAM_RETRY_SELTO +#define ENC_FLAGS SF_NO_PRINT | SF_RETRY_UA +#define STRNCMP strncmp +#define PRINTF printf +#define ENC_LOG enc_log +#if defined(DEBUG) || defined(ENC_DEBUG) +#define ENC_DLOG enc_log +#else +#define ENC_DLOG if (0) enc_log +#endif +#define ENC_VLOG if (bootverbose) enc_log +#define ENC_MALLOC(amt) malloc(amt, M_SCSIENC, M_NOWAIT) +#define ENC_MALLOCZ(amt) malloc(amt, M_SCSIENC, M_ZERO|M_NOWAIT) +/* Cast away const avoiding GCC warnings. */ +#define ENC_FREE(ptr) free((void *)((uintptr_t)ptr), M_SCSIENC) +#define ENC_FREE_AND_NULL(ptr) do { \ + if (ptr != NULL) { \ + ENC_FREE(ptr); \ + ptr = NULL; \ + } \ +} while(0) +#define MEMZERO bzero +#define MEMCPY(dest, src, amt) bcopy(src, dest, amt) + +#endif /* __SCSI_ENC_INTERNAL_H__ */ diff --git a/sys/cam/scsi/scsi_enc_safte.c b/sys/cam/scsi/scsi_enc_safte.c new file mode 100644 index 000000000..3c98ab89d --- /dev/null +++ b/sys/cam/scsi/scsi_enc_safte.c @@ -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 +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* + * 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); +} + diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c new file mode 100644 index 000000000..92d486980 --- /dev/null +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -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 +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +/* 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@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); +} + diff --git a/sys/cam/scsi/scsi_pt.c b/sys/cam/scsi/scsi_pt.c index 26f4d19dd..826afb340 100644 --- a/sys/cam/scsi/scsi_pt.c +++ b/sys/cam/scsi/scsi_pt.c @@ -255,11 +255,6 @@ ptctor(struct cam_periph *periph, void *arg) 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); diff --git a/sys/cam/scsi/scsi_ses.c b/sys/cam/scsi/scsi_ses.c deleted file mode 100644 index b3bca25b4..000000000 --- a/sys/cam/scsi/scsi_ses.c +++ /dev/null @@ -1,2533 +0,0 @@ -/*- - * Copyright (c) 2000 Matthew Jacob - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification, immediately at the beginning of the file. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -MALLOC_DEFINE(M_SCSISES, "SCSI SES", "SCSI SES buffers"); - -/* - * Platform Independent Driver Internal Definitions for SES devices. - */ -typedef enum { - SES_NONE, - SES_SES_SCSI2, - SES_SES, - SES_SES_PASSTHROUGH, - SES_SEN, - SES_SAFT -} enctyp; - -struct ses_softc; -typedef struct ses_softc ses_softc_t; -typedef struct { - int (*softc_init)(ses_softc_t *, int); - int (*init_enc)(ses_softc_t *); - int (*get_encstat)(ses_softc_t *, int); - int (*set_encstat)(ses_softc_t *, ses_encstat, int); - int (*get_objstat)(ses_softc_t *, ses_objstat *, int); - int (*set_objstat)(ses_softc_t *, ses_objstat *, int); -} encvec; - -#define ENCI_SVALID 0x80 - -typedef struct { - uint32_t - enctype : 8, /* enclosure type */ - subenclosure : 8, /* subenclosure id */ - svalid : 1, /* enclosure information valid */ - priv : 15; /* private data, per object */ - uint8_t encstat[4]; /* state && stats */ -} encobj; - -#define SEN_ID "UNISYS SUN_SEN" -#define SEN_ID_LEN 24 - - -static enctyp ses_type(void *, int); - - -/* Forward reference to Enclosure Functions */ -static int ses_softc_init(ses_softc_t *, int); -static int ses_init_enc(ses_softc_t *); -static int ses_get_encstat(ses_softc_t *, int); -static int ses_set_encstat(ses_softc_t *, uint8_t, int); -static int ses_get_objstat(ses_softc_t *, ses_objstat *, int); -static int ses_set_objstat(ses_softc_t *, ses_objstat *, int); - -static int safte_softc_init(ses_softc_t *, int); -static int safte_init_enc(ses_softc_t *); -static int safte_get_encstat(ses_softc_t *, int); -static int safte_set_encstat(ses_softc_t *, uint8_t, int); -static int safte_get_objstat(ses_softc_t *, ses_objstat *, int); -static int safte_set_objstat(ses_softc_t *, ses_objstat *, int); - -/* - * Platform implementation defines/functions for SES internal kernel stuff - */ - -#define STRNCMP strncmp -#define PRINTF printf -#define SES_LOG ses_log -#ifdef DEBUG -#define SES_DLOG ses_log -#else -#define SES_DLOG if (0) ses_log -#endif -#define SES_VLOG if (bootverbose) ses_log -#define SES_MALLOC(amt) malloc(amt, M_SCSISES, M_NOWAIT) -#define SES_FREE(ptr, amt) free(ptr, M_SCSISES) -#define MEMZERO bzero -#define MEMCPY(dest, src, amt) bcopy(src, dest, amt) - -static int ses_runcmd(struct ses_softc *, char *, int, char *, int *); -static void ses_log(struct ses_softc *, const char *, ...); - -/* - * Gerenal FreeBSD kernel stuff. - */ - - -#define ccb_state ppriv_field0 -#define ccb_bp ppriv_ptr1 - -struct ses_softc { - enctyp ses_type; /* type of enclosure */ - encvec ses_vec; /* vector to handlers */ - void * ses_private; /* per-type private data */ - encobj * ses_objmap; /* objects */ - uint32_t ses_nobjects; /* number of objects */ - ses_encstat ses_encstat; /* overall status */ - uint8_t ses_flags; - union ccb ses_saved_ccb; - struct cdev *ses_dev; - struct cam_periph *periph; -}; -#define SES_FLAG_INVALID 0x01 -#define SES_FLAG_OPEN 0x02 -#define SES_FLAG_INITIALIZED 0x04 - -static d_open_t sesopen; -static d_close_t sesclose; -static d_ioctl_t sesioctl; -static periph_init_t sesinit; -static periph_ctor_t sesregister; -static periph_oninv_t sesoninvalidate; -static periph_dtor_t sescleanup; -static periph_start_t sesstart; - -static void sesasync(void *, uint32_t, struct cam_path *, void *); -static void sesdone(struct cam_periph *, union ccb *); -static int seserror(union ccb *, uint32_t, uint32_t); - -static struct periph_driver sesdriver = { - sesinit, "ses", - TAILQ_HEAD_INITIALIZER(sesdriver.units), /* generation */ 0 -}; - -PERIPHDRIVER_DECLARE(ses, sesdriver); - -static struct cdevsw ses_cdevsw = { - .d_version = D_VERSION, - .d_open = sesopen, - .d_close = sesclose, - .d_ioctl = sesioctl, - .d_name = "ses", - .d_flags = 0, -}; - -static void -sesinit(void) -{ - cam_status status; - - /* - * Install a global async callback. This callback will - * receive async callbacks like "new device found". - */ - status = xpt_register_async(AC_FOUND_DEVICE, sesasync, NULL, NULL); - - if (status != CAM_REQ_CMP) { - printf("ses: Failed to attach master async callback " - "due to status 0x%x!\n", status); - } -} - -static void -sesoninvalidate(struct cam_periph *periph) -{ - struct ses_softc *softc; - - softc = (struct ses_softc *)periph->softc; - - /* - * Unregister any async callbacks. - */ - xpt_register_async(0, sesasync, periph, periph->path); - - softc->ses_flags |= SES_FLAG_INVALID; - - xpt_print(periph->path, "lost device\n"); -} - -static void -sescleanup(struct cam_periph *periph) -{ - struct ses_softc *softc; - - softc = (struct ses_softc *)periph->softc; - - xpt_print(periph->path, "removing device entry\n"); - cam_periph_unlock(periph); - destroy_dev(softc->ses_dev); - cam_periph_lock(periph); - free(softc, M_SCSISES); -} - -static void -sesasync(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) -{ - struct cam_periph *periph; - - periph = (struct cam_periph *)callback_arg; - - switch(code) { - case AC_FOUND_DEVICE: - { - cam_status status; - struct ccb_getdev *cgd; - int inq_len; - - cgd = (struct ccb_getdev *)arg; - if (arg == NULL) { - break; - } - - if (cgd->protocol != PROTO_SCSI) - break; - - inq_len = cgd->inq_data.additional_length + 4; - - /* - * PROBLEM: WE NEED TO LOOK AT BYTES 48-53 TO SEE IF THIS IS - * PROBLEM: IS A SAF-TE DEVICE. - */ - switch (ses_type(&cgd->inq_data, inq_len)) { - case SES_SES: - case SES_SES_SCSI2: - case SES_SES_PASSTHROUGH: - case SES_SEN: - case SES_SAFT: - break; - default: - return; - } - - status = cam_periph_alloc(sesregister, sesoninvalidate, - sescleanup, sesstart, "ses", CAM_PERIPH_BIO, - cgd->ccb_h.path, sesasync, AC_FOUND_DEVICE, cgd); - - if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { - printf("sesasync: Unable to probe new device due to " - "status 0x%x\n", status); - } - break; - } - default: - cam_periph_async(periph, code, path, arg); - break; - } -} - -static cam_status -sesregister(struct cam_periph *periph, void *arg) -{ - struct ses_softc *softc; - struct ccb_getdev *cgd; - char *tname; - - cgd = (struct ccb_getdev *)arg; - if (periph == NULL) { - printf("sesregister: periph was NULL!!\n"); - return (CAM_REQ_CMP_ERR); - } - - if (cgd == NULL) { - printf("sesregister: no getdev CCB, can't register device\n"); - return (CAM_REQ_CMP_ERR); - } - - softc = SES_MALLOC(sizeof (struct ses_softc)); - if (softc == NULL) { - printf("sesregister: Unable to probe new device. " - "Unable to allocate softc\n"); - return (CAM_REQ_CMP_ERR); - } - bzero(softc, sizeof (struct ses_softc)); - periph->softc = softc; - softc->periph = periph; - - softc->ses_type = ses_type(&cgd->inq_data, sizeof (cgd->inq_data)); - - switch (softc->ses_type) { - case SES_SES: - case SES_SES_SCSI2: - case SES_SES_PASSTHROUGH: - softc->ses_vec.softc_init = ses_softc_init; - softc->ses_vec.init_enc = ses_init_enc; - softc->ses_vec.get_encstat = ses_get_encstat; - softc->ses_vec.set_encstat = ses_set_encstat; - softc->ses_vec.get_objstat = ses_get_objstat; - softc->ses_vec.set_objstat = ses_set_objstat; - break; - case SES_SAFT: - softc->ses_vec.softc_init = safte_softc_init; - softc->ses_vec.init_enc = safte_init_enc; - softc->ses_vec.get_encstat = safte_get_encstat; - softc->ses_vec.set_encstat = safte_set_encstat; - softc->ses_vec.get_objstat = safte_get_objstat; - softc->ses_vec.set_objstat = safte_set_objstat; - break; - case SES_SEN: - break; - case SES_NONE: - default: - free(softc, M_SCSISES); - return (CAM_REQ_CMP_ERR); - } - - cam_periph_unlock(periph); - softc->ses_dev = make_dev(&ses_cdevsw, periph->unit_number, - UID_ROOT, GID_OPERATOR, 0600, "%s%d", - periph->periph_name, periph->unit_number); - cam_periph_lock(periph); - softc->ses_dev->si_drv1 = periph; - - /* - * Add an async callback so that we get - * notified if this device goes away. - */ - xpt_register_async(AC_LOST_DEVICE, sesasync, periph, periph->path); - - switch (softc->ses_type) { - default: - case SES_NONE: - tname = "No SES device"; - break; - case SES_SES_SCSI2: - tname = "SCSI-2 SES Device"; - break; - case SES_SES: - tname = "SCSI-3 SES Device"; - break; - case SES_SES_PASSTHROUGH: - tname = "SES Passthrough Device"; - break; - case SES_SEN: - tname = "UNISYS SEN Device (NOT HANDLED YET)"; - break; - case SES_SAFT: - tname = "SAF-TE Compliant Device"; - break; - } - xpt_announce_periph(periph, tname); - return (CAM_REQ_CMP); -} - -static int -sesopen(struct cdev *dev, int flags, int fmt, struct thread *td) -{ - struct cam_periph *periph; - struct ses_softc *softc; - int error = 0; - - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) { - return (ENXIO); - } - - if (cam_periph_acquire(periph) != CAM_REQ_CMP) { - cam_periph_unlock(periph); - return (ENXIO); - } - - cam_periph_lock(periph); - - softc = (struct ses_softc *)periph->softc; - - if (softc->ses_flags & SES_FLAG_INVALID) { - error = ENXIO; - goto out; - } - if (softc->ses_flags & SES_FLAG_OPEN) { - error = EBUSY; - goto out; - } - if (softc->ses_vec.softc_init == NULL) { - error = ENXIO; - goto out; - } - - softc->ses_flags |= SES_FLAG_OPEN; - if ((softc->ses_flags & SES_FLAG_INITIALIZED) == 0) { - error = (*softc->ses_vec.softc_init)(softc, 1); - if (error) - softc->ses_flags &= ~SES_FLAG_OPEN; - else - softc->ses_flags |= SES_FLAG_INITIALIZED; - } - -out: - cam_periph_unlock(periph); - if (error) { - cam_periph_release(periph); - } - return (error); -} - -static int -sesclose(struct cdev *dev, int flag, int fmt, struct thread *td) -{ - struct cam_periph *periph; - struct ses_softc *softc; - int error; - - error = 0; - - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) - return (ENXIO); - - cam_periph_lock(periph); - - softc = (struct ses_softc *)periph->softc; - softc->ses_flags &= ~SES_FLAG_OPEN; - - cam_periph_unlock(periph); - cam_periph_release(periph); - - return (0); -} - -static void -sesstart(struct cam_periph *p, union ccb *sccb) -{ - if (p->immediate_priority <= p->pinfo.priority) { - SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle); - p->immediate_priority = CAM_PRIORITY_NONE; - wakeup(&p->ccb_list); - } -} - -static void -sesdone(struct cam_periph *periph, union ccb *dccb) -{ - wakeup(&dccb->ccb_h.cbfcnp); -} - -static int -seserror(union ccb *ccb, uint32_t cflags, uint32_t sflags) -{ - struct ses_softc *softc; - struct cam_periph *periph; - - periph = xpt_path_periph(ccb->ccb_h.path); - softc = (struct ses_softc *)periph->softc; - - return (cam_periph_error(ccb, cflags, sflags, &softc->ses_saved_ccb)); -} - -static int -sesioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, struct thread *td) -{ - struct cam_periph *periph; - ses_encstat tmp; - ses_objstat objs; - ses_object *uobj; - struct ses_softc *ssc; - void *addr; - int error, i; - - - if (arg_addr) - addr = *((caddr_t *) arg_addr); - else - addr = NULL; - - periph = (struct cam_periph *)dev->si_drv1; - if (periph == NULL) - return (ENXIO); - - CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering sesioctl\n")); - - cam_periph_lock(periph); - ssc = (struct ses_softc *)periph->softc; - - /* - * Now check to see whether we're initialized or not. - * This actually should never fail as we're not supposed - * to get past ses_open w/o successfully initializing - * things. - */ - if ((ssc->ses_flags & SES_FLAG_INITIALIZED) == 0) { - cam_periph_unlock(periph); - return (ENXIO); - } - cam_periph_unlock(periph); - - error = 0; - - CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, - ("trying to do ioctl %#lx\n", cmd)); - - /* - * If this command can change the device's state, - * we must have the device open for writing. - * - * For commands that get information about the - * device- we don't need to lock the peripheral - * if we aren't running a command. The number - * of objects and the contents will stay stable - * after the first open that does initialization. - * The periph also can't go away while a user - * process has it open. - */ - switch (cmd) { - case SESIOC_GETNOBJ: - case SESIOC_GETOBJMAP: - case SESIOC_GETENCSTAT: - case SESIOC_GETOBJSTAT: - break; - default: - if ((flag & FWRITE) == 0) { - return (EBADF); - } - } - - switch (cmd) { - case SESIOC_GETNOBJ: - error = copyout(&ssc->ses_nobjects, addr, - sizeof (ssc->ses_nobjects)); - break; - - case SESIOC_GETOBJMAP: - for (uobj = addr, i = 0; i != ssc->ses_nobjects; i++) { - ses_object kobj; - kobj.obj_id = i; - kobj.subencid = ssc->ses_objmap[i].subenclosure; - kobj.object_type = ssc->ses_objmap[i].enctype; - error = copyout(&kobj, &uobj[i], sizeof (ses_object)); - if (error) { - break; - } - } - break; - - case SESIOC_GETENCSTAT: - cam_periph_lock(periph); - error = (*ssc->ses_vec.get_encstat)(ssc, 1); - if (error) { - cam_periph_unlock(periph); - break; - } - tmp = ssc->ses_encstat & ~ENCI_SVALID; - cam_periph_unlock(periph); - error = copyout(&tmp, addr, sizeof (ses_encstat)); - ssc->ses_encstat = tmp; - break; - - case SESIOC_SETENCSTAT: - error = copyin(addr, &tmp, sizeof (ses_encstat)); - if (error) - break; - cam_periph_lock(periph); - error = (*ssc->ses_vec.set_encstat)(ssc, tmp, 1); - cam_periph_unlock(periph); - break; - - case SESIOC_GETOBJSTAT: - error = copyin(addr, &objs, sizeof (ses_objstat)); - if (error) - break; - if (objs.obj_id >= ssc->ses_nobjects) { - error = EINVAL; - break; - } - cam_periph_lock(periph); - error = (*ssc->ses_vec.get_objstat)(ssc, &objs, 1); - cam_periph_unlock(periph); - if (error) - break; - error = copyout(&objs, addr, sizeof (ses_objstat)); - /* - * Always (for now) invalidate entry. - */ - ssc->ses_objmap[objs.obj_id].svalid = 0; - break; - - case SESIOC_SETOBJSTAT: - error = copyin(addr, &objs, sizeof (ses_objstat)); - if (error) - break; - - if (objs.obj_id >= ssc->ses_nobjects) { - error = EINVAL; - break; - } - cam_periph_lock(periph); - error = (*ssc->ses_vec.set_objstat)(ssc, &objs, 1); - cam_periph_unlock(periph); - - /* - * Always (for now) invalidate entry. - */ - ssc->ses_objmap[objs.obj_id].svalid = 0; - break; - - case SESIOC_INIT: - - cam_periph_lock(periph); - error = (*ssc->ses_vec.init_enc)(ssc); - cam_periph_unlock(periph); - break; - - default: - cam_periph_lock(periph); - error = cam_periph_ioctl(periph, cmd, arg_addr, seserror); - cam_periph_unlock(periph); - break; - } - return (error); -} - -#define SES_CFLAGS CAM_RETRY_SELTO -#define SES_FLAGS SF_NO_PRINT | SF_RETRY_UA -static int -ses_runcmd(struct ses_softc *ssc, char *cdb, int cdbl, char *dptr, int *dlenp) -{ - int error, dlen; - ccb_flags ddf; - union ccb *ccb; - - if (dptr) { - if ((dlen = *dlenp) < 0) { - dlen = -dlen; - ddf = CAM_DIR_OUT; - } else { - ddf = CAM_DIR_IN; - } - } else { - dlen = 0; - ddf = CAM_DIR_NONE; - } - - if (cdbl > IOCDBLEN) { - cdbl = IOCDBLEN; - } - - ccb = cam_periph_getccb(ssc->periph, 1); - cam_fill_csio(&ccb->csio, 0, sesdone, ddf, MSG_SIMPLE_Q_TAG, dptr, - dlen, sizeof (struct scsi_sense_data), cdbl, 60 * 1000); - bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); - - error = cam_periph_runccb(ccb, seserror, SES_CFLAGS, SES_FLAGS, NULL); - if (error) { - if (dptr) { - *dlenp = dlen; - } - } else { - if (dptr) { - *dlenp = ccb->csio.resid; - } - } - xpt_release_ccb(ccb); - return (error); -} - -static void -ses_log(struct ses_softc *ssc, const char *fmt, ...) -{ - va_list ap; - - printf("%s%d: ", ssc->periph->periph_name, ssc->periph->unit_number); - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); -} - -/* - * The code after this point runs on many platforms, - * so forgive the slightly awkward and nonconforming - * appearance. - */ - -/* - * Is this a device that supports enclosure services? - * - * It's a pretty simple ruleset- if it is device type 0x0D (13), it's - * an SES device. If it happens to be an old UNISYS SEN device, we can - * handle that too. - */ - -#define SAFTE_START 44 -#define SAFTE_END 50 -#define SAFTE_LEN SAFTE_END-SAFTE_START - -static enctyp -ses_type(void *buf, int buflen) -{ - unsigned char *iqd = buf; - - if (buflen < 8+SEN_ID_LEN) - return (SES_NONE); - - if ((iqd[0] & 0x1f) == T_ENCLOSURE) { - if (STRNCMP(&iqd[8], SEN_ID, SEN_ID_LEN) == 0) { - return (SES_SEN); - } else if ((iqd[2] & 0x7) > 2) { - return (SES_SES); - } else { - return (SES_SES_SCSI2); - } - return (SES_NONE); - } - -#ifdef SES_ENABLE_PASSTHROUGH - if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { - /* - * PassThrough Device. - */ - return (SES_SES_PASSTHROUGH); - } -#endif - - /* - * The comparison is short for a reason- - * some vendors were chopping it short. - */ - - if (buflen < SAFTE_END - 2) { - return (SES_NONE); - } - - if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) { - return (SES_SAFT); - } - return (SES_NONE); -} - -/* - * SES Native Type Device Support - */ - -/* - * SES Diagnostic Page Codes - */ - -typedef enum { - SesConfigPage = 0x1, - SesControlPage, -#define SesStatusPage SesControlPage - SesHelpTxt, - SesStringOut, -#define SesStringIn SesStringOut - SesThresholdOut, -#define SesThresholdIn SesThresholdOut - SesArrayControl, -#define SesArrayStatus SesArrayControl - SesElementDescriptor, - SesShortStatus -} SesDiagPageCodes; - -/* - * minimal amounts - */ - -/* - * Minimum amount of data, starting from byte 0, to have - * the config header. - */ -#define SES_CFGHDR_MINLEN 12 - -/* - * Minimum amount of data, starting from byte 0, to have - * the config header and one enclosure header. - */ -#define SES_ENCHDR_MINLEN 48 - -/* - * Take this value, subtract it from VEnclen and you know - * the length of the vendor unique bytes. - */ -#define SES_ENCHDR_VMIN 36 - -/* - * SES Data Structures - */ - -typedef struct { - uint32_t GenCode; /* Generation Code */ - uint8_t Nsubenc; /* Number of Subenclosures */ -} SesCfgHdr; - -typedef struct { - uint8_t Subencid; /* SubEnclosure Identifier */ - uint8_t Ntypes; /* # of supported types */ - uint8_t VEnclen; /* Enclosure Descriptor Length */ -} SesEncHdr; - -typedef struct { - uint8_t encWWN[8]; /* XXX- Not Right Yet */ - uint8_t encVid[8]; - uint8_t encPid[16]; - uint8_t encRev[4]; - uint8_t encVen[1]; -} SesEncDesc; - -typedef struct { - uint8_t enc_type; /* type of element */ - uint8_t enc_maxelt; /* maximum supported */ - uint8_t enc_subenc; /* in SubEnc # N */ - uint8_t enc_tlen; /* Type Descriptor Text Length */ -} SesThdr; - -typedef struct { - uint8_t comstatus; - uint8_t comstat[3]; -} SesComStat; - -struct typidx { - int ses_tidx; - int ses_oidx; -}; - -struct sscfg { - uint8_t ses_ntypes; /* total number of types supported */ - - /* - * We need to keep a type index as well as an - * object index for each object in an enclosure. - */ - struct typidx *ses_typidx; - - /* - * We also need to keep track of the number of elements - * per type of element. This is needed later so that we - * can find precisely in the returned status data the - * status for the Nth element of the Kth type. - */ - uint8_t * ses_eltmap; -}; - - -/* - * (de)canonicalization defines - */ -#define sbyte(x, byte) ((((uint32_t)(x)) >> (byte * 8)) & 0xff) -#define sbit(x, bit) (((uint32_t)(x)) << bit) -#define sset8(outp, idx, sval) (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) - -#define sset16(outp, idx, sval) \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) - - -#define sset24(outp, idx, sval) \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 2), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) - - -#define sset32(outp, idx, sval) \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 3), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 2), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 1), \ - (((uint8_t *)(outp))[idx++]) = sbyte(sval, 0) - -#define gbyte(x, byte) ((((uint32_t)(x)) & 0xff) << (byte * 8)) -#define gbit(lv, in, idx, shft, mask) lv = ((in[idx] >> shft) & mask) -#define sget8(inp, idx, lval) lval = (((uint8_t *)(inp))[idx++]) -#define gget8(inp, idx, lval) lval = (((uint8_t *)(inp))[idx]) - -#define sget16(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 1) | \ - (((uint8_t *)(inp))[idx+1]), idx += 2 - -#define gget16(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 1) | \ - (((uint8_t *)(inp))[idx+1]) - -#define sget24(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 2) | \ - gbyte((((uint8_t *)(inp))[idx+1]), 1) | \ - (((uint8_t *)(inp))[idx+2]), idx += 3 - -#define gget24(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 2) | \ - gbyte((((uint8_t *)(inp))[idx+1]), 1) | \ - (((uint8_t *)(inp))[idx+2]) - -#define sget32(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 3) | \ - gbyte((((uint8_t *)(inp))[idx+1]), 2) | \ - gbyte((((uint8_t *)(inp))[idx+2]), 1) | \ - (((uint8_t *)(inp))[idx+3]), idx += 4 - -#define gget32(inp, idx, lval) \ - lval = gbyte((((uint8_t *)(inp))[idx]), 3) | \ - gbyte((((uint8_t *)(inp))[idx+1]), 2) | \ - gbyte((((uint8_t *)(inp))[idx+2]), 1) | \ - (((uint8_t *)(inp))[idx+3]) - -#define SCSZ 0x2000 -#define CFLEN (256 + SES_ENCHDR_MINLEN) - -/* - * Routines specific && private to SES only - */ - -static int ses_getconfig(ses_softc_t *); -static int ses_getputstat(ses_softc_t *, int, SesComStat *, int, int); -static int ses_cfghdr(uint8_t *, int, SesCfgHdr *); -static int ses_enchdr(uint8_t *, int, uint8_t, SesEncHdr *); -static int ses_encdesc(uint8_t *, int, uint8_t, SesEncDesc *); -static int ses_getthdr(uint8_t *, int, int, SesThdr *); -static int ses_decode(char *, int, uint8_t *, int, int, SesComStat *); -static int ses_encode(char *, int, uint8_t *, int, int, SesComStat *); - -static int -ses_softc_init(ses_softc_t *ssc, int doinit) -{ - if (doinit == 0) { - struct sscfg *cc; - if (ssc->ses_nobjects) { - SES_FREE(ssc->ses_objmap, - ssc->ses_nobjects * sizeof (encobj)); - ssc->ses_objmap = NULL; - } - if ((cc = ssc->ses_private) != NULL) { - if (cc->ses_eltmap && cc->ses_ntypes) { - SES_FREE(cc->ses_eltmap, cc->ses_ntypes); - cc->ses_eltmap = NULL; - cc->ses_ntypes = 0; - } - if (cc->ses_typidx && ssc->ses_nobjects) { - SES_FREE(cc->ses_typidx, - ssc->ses_nobjects * sizeof (struct typidx)); - cc->ses_typidx = NULL; - } - SES_FREE(cc, sizeof (struct sscfg)); - ssc->ses_private = NULL; - } - ssc->ses_nobjects = 0; - return (0); - } - if (ssc->ses_private == NULL) { - ssc->ses_private = SES_MALLOC(sizeof (struct sscfg)); - } - if (ssc->ses_private == NULL) { - return (ENOMEM); - } - ssc->ses_nobjects = 0; - ssc->ses_encstat = 0; - return (ses_getconfig(ssc)); -} - -static int -ses_init_enc(ses_softc_t *ssc) -{ - return (0); -} - -static int -ses_get_encstat(ses_softc_t *ssc, int slpflag) -{ - SesComStat ComStat; - int status; - - if ((status = ses_getputstat(ssc, -1, &ComStat, slpflag, 1)) != 0) { - return (status); - } - ssc->ses_encstat = ComStat.comstatus | ENCI_SVALID; - return (0); -} - -static int -ses_set_encstat(ses_softc_t *ssc, uint8_t encstat, int slpflag) -{ - SesComStat ComStat; - int status; - - ComStat.comstatus = encstat & 0xf; - if ((status = ses_getputstat(ssc, -1, &ComStat, slpflag, 0)) != 0) { - return (status); - } - ssc->ses_encstat = encstat & 0xf; /* note no SVALID set */ - return (0); -} - -static int -ses_get_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflag) -{ - int i = (int)obp->obj_id; - - if (ssc->ses_objmap[i].svalid == 0) { - SesComStat ComStat; - int err = ses_getputstat(ssc, i, &ComStat, slpflag, 1); - if (err) - return (err); - ssc->ses_objmap[i].encstat[0] = ComStat.comstatus; - ssc->ses_objmap[i].encstat[1] = ComStat.comstat[0]; - ssc->ses_objmap[i].encstat[2] = ComStat.comstat[1]; - ssc->ses_objmap[i].encstat[3] = ComStat.comstat[2]; - ssc->ses_objmap[i].svalid = 1; - } - obp->cstat[0] = ssc->ses_objmap[i].encstat[0]; - obp->cstat[1] = ssc->ses_objmap[i].encstat[1]; - obp->cstat[2] = ssc->ses_objmap[i].encstat[2]; - obp->cstat[3] = ssc->ses_objmap[i].encstat[3]; - return (0); -} - -static int -ses_set_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflag) -{ - SesComStat ComStat; - int err; - /* - * If this is clear, we don't do diddly. - */ - if ((obp->cstat[0] & SESCTL_CSEL) == 0) { - return (0); - } - ComStat.comstatus = obp->cstat[0]; - ComStat.comstat[0] = obp->cstat[1]; - ComStat.comstat[1] = obp->cstat[2]; - ComStat.comstat[2] = obp->cstat[3]; - err = ses_getputstat(ssc, (int)obp->obj_id, &ComStat, slpflag, 0); - ssc->ses_objmap[(int)obp->obj_id].svalid = 0; - return (err); -} - -static int -ses_getconfig(ses_softc_t *ssc) -{ - struct sscfg *cc; - SesCfgHdr cf; - SesEncHdr hd; - SesEncDesc *cdp; - SesThdr thdr; - int err, amt, i, nobj, ntype, maxima; - char storage[CFLEN], *sdata; - static char cdb[6] = { - RECEIVE_DIAGNOSTIC, 0x1, SesConfigPage, SCSZ >> 8, SCSZ & 0xff, 0 - }; - - cc = ssc->ses_private; - if (cc == NULL) { - return (ENXIO); - } - - sdata = SES_MALLOC(SCSZ); - if (sdata == NULL) - return (ENOMEM); - - amt = SCSZ; - err = ses_runcmd(ssc, cdb, 6, sdata, &amt); - if (err) { - SES_FREE(sdata, SCSZ); - return (err); - } - amt = SCSZ - amt; - - if (ses_cfghdr((uint8_t *) sdata, amt, &cf)) { - SES_LOG(ssc, "Unable to parse SES Config Header\n"); - SES_FREE(sdata, SCSZ); - return (EIO); - } - if (amt < SES_ENCHDR_MINLEN) { - SES_LOG(ssc, "runt enclosure length (%d)\n", amt); - SES_FREE(sdata, SCSZ); - return (EIO); - } - - SES_VLOG(ssc, "GenCode %x %d Subenclosures\n", cf.GenCode, cf.Nsubenc); - - /* - * Now waltz through all the subenclosures toting up the - * number of types available in each. For this, we only - * really need the enclosure header. However, we get the - * enclosure descriptor for debug purposes, as well - * as self-consistency checking purposes. - */ - - maxima = cf.Nsubenc + 1; - cdp = (SesEncDesc *) storage; - for (ntype = i = 0; i < maxima; i++) { - MEMZERO((caddr_t)cdp, sizeof (*cdp)); - if (ses_enchdr((uint8_t *) sdata, amt, i, &hd)) { - SES_LOG(ssc, "Cannot Extract Enclosure Header %d\n", i); - SES_FREE(sdata, SCSZ); - return (EIO); - } - SES_VLOG(ssc, " SubEnclosure ID %d, %d Types With this ID, En" - "closure Length %d\n", hd.Subencid, hd.Ntypes, hd.VEnclen); - - if (ses_encdesc((uint8_t *)sdata, amt, i, cdp)) { - SES_LOG(ssc, "Can't get Enclosure Descriptor %d\n", i); - SES_FREE(sdata, SCSZ); - return (EIO); - } - SES_VLOG(ssc, " WWN: %02x%02x%02x%02x%02x%02x%02x%02x\n", - cdp->encWWN[0], cdp->encWWN[1], cdp->encWWN[2], - cdp->encWWN[3], cdp->encWWN[4], cdp->encWWN[5], - cdp->encWWN[6], cdp->encWWN[7]); - ntype += hd.Ntypes; - } - - /* - * Now waltz through all the types that are available, getting - * the type header so we can start adding up the number of - * objects available. - */ - for (nobj = i = 0; i < ntype; i++) { - if (ses_getthdr((uint8_t *)sdata, amt, i, &thdr)) { - SES_LOG(ssc, "Can't get Enclosure Type Header %d\n", i); - SES_FREE(sdata, SCSZ); - return (EIO); - } - SES_LOG(ssc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc " - "%d, Text Length %d\n", i, thdr.enc_type, thdr.enc_maxelt, - thdr.enc_subenc, thdr.enc_tlen); - nobj += thdr.enc_maxelt; - } - - - /* - * Now allocate the object array and type map. - */ - - ssc->ses_objmap = SES_MALLOC(nobj * sizeof (encobj)); - cc->ses_typidx = SES_MALLOC(nobj * sizeof (struct typidx)); - cc->ses_eltmap = SES_MALLOC(ntype); - - if (ssc->ses_objmap == NULL || cc->ses_typidx == NULL || - cc->ses_eltmap == NULL) { - if (ssc->ses_objmap) { - SES_FREE(ssc->ses_objmap, (nobj * sizeof (encobj))); - ssc->ses_objmap = NULL; - } - if (cc->ses_typidx) { - SES_FREE(cc->ses_typidx, - (nobj * sizeof (struct typidx))); - cc->ses_typidx = NULL; - } - if (cc->ses_eltmap) { - SES_FREE(cc->ses_eltmap, ntype); - cc->ses_eltmap = NULL; - } - SES_FREE(sdata, SCSZ); - return (ENOMEM); - } - MEMZERO(ssc->ses_objmap, nobj * sizeof (encobj)); - MEMZERO(cc->ses_typidx, nobj * sizeof (struct typidx)); - MEMZERO(cc->ses_eltmap, ntype); - cc->ses_ntypes = (uint8_t) ntype; - ssc->ses_nobjects = nobj; - - /* - * Now waltz through the # of types again to fill in the types - * (and subenclosure ids) of the allocated objects. - */ - nobj = 0; - for (i = 0; i < ntype; i++) { - int j; - if (ses_getthdr((uint8_t *)sdata, amt, i, &thdr)) { - continue; - } - cc->ses_eltmap[i] = thdr.enc_maxelt; - for (j = 0; j < thdr.enc_maxelt; j++) { - cc->ses_typidx[nobj].ses_tidx = i; - cc->ses_typidx[nobj].ses_oidx = j; - ssc->ses_objmap[nobj].subenclosure = thdr.enc_subenc; - ssc->ses_objmap[nobj++].enctype = thdr.enc_type; - } - } - SES_FREE(sdata, SCSZ); - return (0); -} - -static int -ses_getputstat(ses_softc_t *ssc, int objid, SesComStat *sp, int slp, int in) -{ - struct sscfg *cc; - int err, amt, bufsiz, tidx, oidx; - char cdb[6], *sdata; - - cc = ssc->ses_private; - if (cc == NULL) { - return (ENXIO); - } - - /* - * If we're just getting overall enclosure status, - * we only need 2 bytes of data storage. - * - * If we're getting anything else, we know how much - * storage we need by noting that starting at offset - * 8 in returned data, all object status bytes are 4 - * bytes long, and are stored in chunks of types(M) - * and nth+1 instances of type M. - */ - if (objid == -1) { - bufsiz = 2; - } else { - bufsiz = (ssc->ses_nobjects * 4) + (cc->ses_ntypes * 4) + 8; - } - sdata = SES_MALLOC(bufsiz); - if (sdata == NULL) - return (ENOMEM); - - cdb[0] = RECEIVE_DIAGNOSTIC; - cdb[1] = 1; - cdb[2] = SesStatusPage; - cdb[3] = bufsiz >> 8; - cdb[4] = bufsiz & 0xff; - cdb[5] = 0; - amt = bufsiz; - err = ses_runcmd(ssc, cdb, 6, sdata, &amt); - if (err) { - SES_FREE(sdata, bufsiz); - return (err); - } - amt = bufsiz - amt; - - if (objid == -1) { - tidx = -1; - oidx = -1; - } else { - tidx = cc->ses_typidx[objid].ses_tidx; - oidx = cc->ses_typidx[objid].ses_oidx; - } - if (in) { - if (ses_decode(sdata, amt, cc->ses_eltmap, tidx, oidx, sp)) { - err = ENODEV; - } - } else { - if (ses_encode(sdata, amt, cc->ses_eltmap, tidx, oidx, sp)) { - err = ENODEV; - } else { - cdb[0] = SEND_DIAGNOSTIC; - cdb[1] = 0x10; - cdb[2] = 0; - cdb[3] = bufsiz >> 8; - cdb[4] = bufsiz & 0xff; - cdb[5] = 0; - amt = -bufsiz; - err = ses_runcmd(ssc, cdb, 6, sdata, &amt); - } - } - SES_FREE(sdata, bufsiz); - return (0); -} - - -/* - * Routines to parse returned SES data structures. - * Architecture and compiler independent. - */ - -static int -ses_cfghdr(uint8_t *buffer, int buflen, SesCfgHdr *cfp) -{ - if (buflen < SES_CFGHDR_MINLEN) { - return (-1); - } - gget8(buffer, 1, cfp->Nsubenc); - gget32(buffer, 4, cfp->GenCode); - return (0); -} - -static int -ses_enchdr(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncHdr *chp) -{ - int s, off = 8; - for (s = 0; s < SubEncId; s++) { - if (off + 3 > amt) - return (-1); - off += buffer[off+3] + 4; - } - if (off + 3 > amt) { - return (-1); - } - gget8(buffer, off+1, chp->Subencid); - gget8(buffer, off+2, chp->Ntypes); - gget8(buffer, off+3, chp->VEnclen); - return (0); -} - -static int -ses_encdesc(uint8_t *buffer, int amt, uint8_t SubEncId, SesEncDesc *cdp) -{ - int s, e, enclen, off = 8; - for (s = 0; s < SubEncId; s++) { - if (off + 3 > amt) - return (-1); - off += buffer[off+3] + 4; - } - if (off + 3 > amt) { - return (-1); - } - gget8(buffer, off+3, enclen); - off += 4; - if (off >= amt) - return (-1); - - e = off + enclen; - if (e > amt) { - e = amt; - } - MEMCPY(cdp, &buffer[off], e - off); - return (0); -} - -static int -ses_getthdr(uint8_t *buffer, int amt, int nth, SesThdr *thp) -{ - int s, off = 8; - - if (amt < SES_CFGHDR_MINLEN) { - return (-1); - } - for (s = 0; s < buffer[1]; s++) { - if (off + 3 > amt) - return (-1); - off += buffer[off+3] + 4; - } - if (off + 3 > amt) { - return (-1); - } - off += buffer[off+3] + 4 + (nth * 4); - if (amt < (off + 4)) - return (-1); - - gget8(buffer, off++, thp->enc_type); - gget8(buffer, off++, thp->enc_maxelt); - gget8(buffer, off++, thp->enc_subenc); - gget8(buffer, off, thp->enc_tlen); - return (0); -} - -/* - * This function needs a little explanation. - * - * The arguments are: - * - * - * char *b, int amt - * - * These describes the raw input SES status data and length. - * - * uint8_t *ep - * - * This is a map of the number of types for each element type - * in the enclosure. - * - * int elt - * - * This is the element type being sought. If elt is -1, - * then overall enclosure status is being sought. - * - * int elm - * - * This is the ordinal Mth element of type elt being sought. - * - * SesComStat *sp - * - * This is the output area to store the status for - * the Mth element of type Elt. - */ - -static int -ses_decode(char *b, int amt, uint8_t *ep, int elt, int elm, SesComStat *sp) -{ - int idx, i; - - /* - * If it's overall enclosure status being sought, get that. - * We need at least 2 bytes of status data to get that. - */ - if (elt == -1) { - if (amt < 2) - return (-1); - gget8(b, 1, sp->comstatus); - sp->comstat[0] = 0; - sp->comstat[1] = 0; - sp->comstat[2] = 0; - return (0); - } - - /* - * Check to make sure that the Mth element is legal for type Elt. - */ - - if (elm >= ep[elt]) - return (-1); - - /* - * Starting at offset 8, start skipping over the storage - * for the element types we're not interested in. - */ - for (idx = 8, i = 0; i < elt; i++) { - idx += ((ep[i] + 1) * 4); - } - - /* - * Skip over Overall status for this element type. - */ - idx += 4; - - /* - * And skip to the index for the Mth element that we're going for. - */ - idx += (4 * elm); - - /* - * Make sure we haven't overflowed the buffer. - */ - if (idx+4 > amt) - return (-1); - - /* - * Retrieve the status. - */ - gget8(b, idx++, sp->comstatus); - gget8(b, idx++, sp->comstat[0]); - gget8(b, idx++, sp->comstat[1]); - gget8(b, idx++, sp->comstat[2]); -#if 0 - PRINTF("Get Elt 0x%x Elm 0x%x (idx %d)\n", elt, elm, idx-4); -#endif - return (0); -} - -/* - * This is the mirror function to ses_decode, but we set the 'select' - * bit for the object which we're interested in. All other objects, - * after a status fetch, should have that bit off. Hmm. It'd be easy - * enough to ensure this, so we will. - */ - -static int -ses_encode(char *b, int amt, uint8_t *ep, int elt, int elm, SesComStat *sp) -{ - int idx, i; - - /* - * If it's overall enclosure status being sought, get that. - * We need at least 2 bytes of status data to get that. - */ - if (elt == -1) { - if (amt < 2) - return (-1); - i = 0; - sset8(b, i, 0); - sset8(b, i, sp->comstatus & 0xf); -#if 0 - PRINTF("set EncStat %x\n", sp->comstatus); -#endif - return (0); - } - - /* - * Check to make sure that the Mth element is legal for type Elt. - */ - - if (elm >= ep[elt]) - return (-1); - - /* - * Starting at offset 8, start skipping over the storage - * for the element types we're not interested in. - */ - for (idx = 8, i = 0; i < elt; i++) { - idx += ((ep[i] + 1) * 4); - } - - /* - * Skip over Overall status for this element type. - */ - idx += 4; - - /* - * And skip to the index for the Mth element that we're going for. - */ - idx += (4 * elm); - - /* - * Make sure we haven't overflowed the buffer. - */ - if (idx+4 > amt) - return (-1); - - /* - * Set the status. - */ - sset8(b, idx, sp->comstatus); - sset8(b, idx, sp->comstat[0]); - sset8(b, idx, sp->comstat[1]); - sset8(b, idx, sp->comstat[2]); - idx -= 4; - -#if 0 - PRINTF("Set Elt 0x%x Elm 0x%x (idx %d) with %x %x %x %x\n", - elt, elm, idx, sp->comstatus, sp->comstat[0], - sp->comstat[1], sp->comstat[2]); -#endif - - /* - * Now make sure all other 'Select' bits are off. - */ - for (i = 8; i < amt; i += 4) { - if (i != idx) - b[i] &= ~0x80; - } - /* - * And make sure the INVOP bit is clear. - */ - b[2] &= ~0x10; - - return (0); -} - -/* - * SAF-TE Type Device Emulation - */ - -static int safte_getconfig(ses_softc_t *); -static int safte_rdstat(ses_softc_t *, int); -static int set_objstat_sel(ses_softc_t *, ses_objstat *, int); -static int wrbuf16(ses_softc_t *, uint8_t, uint8_t, uint8_t, uint8_t, int); -static void wrslot_stat(ses_softc_t *, int); -static int perf_slotop(ses_softc_t *, uint8_t, uint8_t, int); - -#define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \ - SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) -/* - * SAF-TE specific defines- Mandatory ones only... - */ - -/* - * READ BUFFER ('get' commands) IDs- placed in offset 2 of cdb - */ -#define SAFTE_RD_RDCFG 0x00 /* read enclosure configuration */ -#define SAFTE_RD_RDESTS 0x01 /* read enclosure status */ -#define SAFTE_RD_RDDSTS 0x04 /* read drive slot status */ - -/* - * WRITE BUFFER ('set' commands) IDs- placed in offset 0 of databuf - */ -#define SAFTE_WT_DSTAT 0x10 /* write device slot status */ -#define SAFTE_WT_SLTOP 0x12 /* perform slot operation */ -#define SAFTE_WT_FANSPD 0x13 /* set fan speed */ -#define SAFTE_WT_ACTPWS 0x14 /* turn on/off power supply */ -#define SAFTE_WT_GLOBAL 0x15 /* send global command */ - - -#define SAFT_SCRATCH 64 -#define NPSEUDO_THERM 16 -#define NPSEUDO_ALARM 1 -struct scfg { - /* - * Cached Configuration - */ - uint8_t Nfans; /* Number of Fans */ - uint8_t Npwr; /* Number of Power Supplies */ - uint8_t Nslots; /* Number of Device Slots */ - uint8_t DoorLock; /* Door Lock Installed */ - uint8_t Ntherm; /* Number of Temperature Sensors */ - uint8_t Nspkrs; /* Number of Speakers */ - uint8_t Nalarm; /* Number of Alarms (at least one) */ - /* - * Cached Flag Bytes for Global Status - */ - uint8_t flag1; - uint8_t flag2; - /* - * What object index ID is where various slots start. - */ - uint8_t pwroff; - uint8_t slotoff; -#define SAFT_ALARM_OFFSET(cc) (cc)->slotoff - 1 -}; - -#define SAFT_FLG1_ALARM 0x1 -#define SAFT_FLG1_GLOBFAIL 0x2 -#define SAFT_FLG1_GLOBWARN 0x4 -#define SAFT_FLG1_ENCPWROFF 0x8 -#define SAFT_FLG1_ENCFANFAIL 0x10 -#define SAFT_FLG1_ENCPWRFAIL 0x20 -#define SAFT_FLG1_ENCDRVFAIL 0x40 -#define SAFT_FLG1_ENCDRVWARN 0x80 - -#define SAFT_FLG2_LOCKDOOR 0x4 -#define SAFT_PRIVATE sizeof (struct scfg) - -static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; -#define SAFT_BAIL(r, x, k, l) \ - if ((r) >= (x)) { \ - SES_LOG(ssc, safte_2little, x, __LINE__);\ - SES_FREE((k), (l)); \ - return (EIO); \ - } - - -static int -safte_softc_init(ses_softc_t *ssc, int doinit) -{ - int err, i, r; - struct scfg *cc; - - if (doinit == 0) { - if (ssc->ses_nobjects) { - if (ssc->ses_objmap) { - SES_FREE(ssc->ses_objmap, - ssc->ses_nobjects * sizeof (encobj)); - ssc->ses_objmap = NULL; - } - ssc->ses_nobjects = 0; - } - if (ssc->ses_private) { - SES_FREE(ssc->ses_private, SAFT_PRIVATE); - ssc->ses_private = NULL; - } - return (0); - } - - if (ssc->ses_private == NULL) { - ssc->ses_private = SES_MALLOC(SAFT_PRIVATE); - if (ssc->ses_private == NULL) { - return (ENOMEM); - } - MEMZERO(ssc->ses_private, SAFT_PRIVATE); - } - - ssc->ses_nobjects = 0; - ssc->ses_encstat = 0; - - if ((err = safte_getconfig(ssc)) != 0) { - return (err); - } - - /* - * The number of objects here, as well as that reported by the - * READ_BUFFER/GET_CONFIG call, are the over-temperature flags (15) - * that get reported during READ_BUFFER/READ_ENC_STATUS. - */ - cc = ssc->ses_private; - ssc->ses_nobjects = cc->Nfans + cc->Npwr + cc->Nslots + cc->DoorLock + - cc->Ntherm + cc->Nspkrs + NPSEUDO_THERM + NPSEUDO_ALARM; - ssc->ses_objmap = (encobj *) - SES_MALLOC(ssc->ses_nobjects * sizeof (encobj)); - if (ssc->ses_objmap == NULL) { - return (ENOMEM); - } - MEMZERO(ssc->ses_objmap, ssc->ses_nobjects * sizeof (encobj)); - - r = 0; - /* - * Note that this is all arranged for the convenience - * in later fetches of status. - */ - for (i = 0; i < cc->Nfans; i++) - ssc->ses_objmap[r++].enctype = SESTYP_FAN; - cc->pwroff = (uint8_t) r; - for (i = 0; i < cc->Npwr; i++) - ssc->ses_objmap[r++].enctype = SESTYP_POWER; - for (i = 0; i < cc->DoorLock; i++) - ssc->ses_objmap[r++].enctype = SESTYP_DOORLOCK; - for (i = 0; i < cc->Nspkrs; i++) - ssc->ses_objmap[r++].enctype = SESTYP_ALARM; - for (i = 0; i < cc->Ntherm; i++) - ssc->ses_objmap[r++].enctype = SESTYP_THERM; - for (i = 0; i < NPSEUDO_THERM; i++) - ssc->ses_objmap[r++].enctype = SESTYP_THERM; - ssc->ses_objmap[r++].enctype = SESTYP_ALARM; - cc->slotoff = (uint8_t) r; - for (i = 0; i < cc->Nslots; i++) - ssc->ses_objmap[r++].enctype = SESTYP_DEVICE; - return (0); -} - -static int -safte_init_enc(ses_softc_t *ssc) -{ - int err; - static char cdb0[6] = { SEND_DIAGNOSTIC }; - - err = ses_runcmd(ssc, cdb0, 6, NULL, 0); - if (err) { - return (err); - } - DELAY(5000); - err = wrbuf16(ssc, SAFTE_WT_GLOBAL, 0, 0, 0, 1); - return (err); -} - -static int -safte_get_encstat(ses_softc_t *ssc, int slpflg) -{ - return (safte_rdstat(ssc, slpflg)); -} - -static int -safte_set_encstat(ses_softc_t *ssc, uint8_t encstat, int slpflg) -{ - struct scfg *cc = ssc->ses_private; - if (cc == NULL) - return (0); - /* - * Since SAF-TE devices aren't necessarily sticky in terms - * of state, make our soft copy of enclosure status 'sticky'- - * that is, things set in enclosure status stay set (as implied - * by conditions set in reading object status) until cleared. - */ - ssc->ses_encstat &= ~ALL_ENC_STAT; - ssc->ses_encstat |= (encstat & ALL_ENC_STAT); - ssc->ses_encstat |= ENCI_SVALID; - cc->flag1 &= ~(SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL|SAFT_FLG1_GLOBWARN); - if ((encstat & (SES_ENCSTAT_CRITICAL|SES_ENCSTAT_UNRECOV)) != 0) { - cc->flag1 |= SAFT_FLG1_ALARM|SAFT_FLG1_GLOBFAIL; - } else if ((encstat & SES_ENCSTAT_NONCRITICAL) != 0) { - cc->flag1 |= SAFT_FLG1_GLOBWARN; - } - return (wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg)); -} - -static int -safte_get_objstat(ses_softc_t *ssc, ses_objstat *obp, int slpflg) -{ - int i = (int)obp->obj_id; - - if ((ssc->ses_encstat & ENCI_SVALID) == 0 || - (ssc->ses_objmap[i].svalid) == 0) { - int err = safte_rdstat(ssc, slpflg); - if (err) - return (err); - } - obp->cstat[0] = ssc->ses_objmap[i].encstat[0]; - obp->cstat[1] = ssc->ses_objmap[i].encstat[1]; - obp->cstat[2] = ssc->ses_objmap[i].encstat[2]; - obp->cstat[3] = ssc->ses_objmap[i].encstat[3]; - return (0); -} - - -static int -safte_set_objstat(ses_softc_t *ssc, ses_objstat *obp, int slp) -{ - int idx, err; - encobj *ep; - struct scfg *cc; - - - SES_DLOG(ssc, "safte_set_objstat(%d): %x %x %x %x\n", - (int)obp->obj_id, obp->cstat[0], obp->cstat[1], obp->cstat[2], - obp->cstat[3]); - - /* - * If this is clear, we don't do diddly. - */ - if ((obp->cstat[0] & SESCTL_CSEL) == 0) { - return (0); - } - - err = 0; - /* - * Check to see if the common bits are set and do them first. - */ - if (obp->cstat[0] & ~SESCTL_CSEL) { - err = set_objstat_sel(ssc, obp, slp); - if (err) - return (err); - } - - cc = ssc->ses_private; - if (cc == NULL) - return (0); - - idx = (int)obp->obj_id; - ep = &ssc->ses_objmap[idx]; - - switch (ep->enctype) { - case SESTYP_DEVICE: - { - uint8_t slotop = 0; - /* - * XXX: I should probably cache the previous state - * XXX: of SESCTL_DEVOFF so that when it goes from - * XXX: true to false I can then set PREPARE FOR OPERATION - * XXX: flag in PERFORM SLOT OPERATION write buffer command. - */ - if (obp->cstat[2] & (SESCTL_RQSINS|SESCTL_RQSRMV)) { - slotop |= 0x2; - } - if (obp->cstat[2] & SESCTL_RQSID) { - slotop |= 0x4; - } - err = perf_slotop(ssc, (uint8_t) idx - (uint8_t) cc->slotoff, - slotop, slp); - if (err) - return (err); - if (obp->cstat[3] & SESCTL_RQSFLT) { - ep->priv |= 0x2; - } else { - ep->priv &= ~0x2; - } - if (ep->priv & 0xc6) { - ep->priv &= ~0x1; - } else { - ep->priv |= 0x1; /* no errors */ - } - wrslot_stat(ssc, slp); - break; - } - case SESTYP_POWER: - if (obp->cstat[3] & SESCTL_RQSTFAIL) { - cc->flag1 |= SAFT_FLG1_ENCPWRFAIL; - } else { - cc->flag1 &= ~SAFT_FLG1_ENCPWRFAIL; - } - err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - if (err) - return (err); - if (obp->cstat[3] & SESCTL_RQSTON) { - (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, - idx - cc->pwroff, 0, 0, slp); - } else { - (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, - idx - cc->pwroff, 0, 1, slp); - } - break; - case SESTYP_FAN: - if (obp->cstat[3] & SESCTL_RQSTFAIL) { - cc->flag1 |= SAFT_FLG1_ENCFANFAIL; - } else { - cc->flag1 &= ~SAFT_FLG1_ENCFANFAIL; - } - err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - if (err) - return (err); - if (obp->cstat[3] & SESCTL_RQSTON) { - uint8_t fsp; - if ((obp->cstat[3] & 0x7) == 7) { - fsp = 4; - } else if ((obp->cstat[3] & 0x7) == 6) { - fsp = 3; - } else if ((obp->cstat[3] & 0x7) == 4) { - fsp = 2; - } else { - fsp = 1; - } - (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, fsp, 0, slp); - } else { - (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); - } - break; - case SESTYP_DOORLOCK: - if (obp->cstat[3] & 0x1) { - cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; - } else { - cc->flag2 |= SAFT_FLG2_LOCKDOOR; - } - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - break; - case SESTYP_ALARM: - /* - * On all nonzero but the 'muted' bit, we turn on the alarm, - */ - obp->cstat[3] &= ~0xa; - if (obp->cstat[3] & 0x40) { - cc->flag2 &= ~SAFT_FLG1_ALARM; - } else if (obp->cstat[3] != 0) { - cc->flag2 |= SAFT_FLG1_ALARM; - } else { - cc->flag2 &= ~SAFT_FLG1_ALARM; - } - ep->priv = obp->cstat[3]; - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - break; - default: - break; - } - ep->svalid = 0; - return (0); -} - -static int -safte_getconfig(ses_softc_t *ssc) -{ - struct scfg *cfg; - int err, amt; - char *sdata; - static char cdb[10] = - { READ_BUFFER, 1, SAFTE_RD_RDCFG, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; - - cfg = ssc->ses_private; - if (cfg == NULL) - return (ENXIO); - - sdata = SES_MALLOC(SAFT_SCRATCH); - if (sdata == NULL) - return (ENOMEM); - - amt = SAFT_SCRATCH; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - if (err) { - SES_FREE(sdata, SAFT_SCRATCH); - return (err); - } - amt = SAFT_SCRATCH - amt; - if (amt < 6) { - SES_LOG(ssc, "too little data (%d) for configuration\n", amt); - SES_FREE(sdata, SAFT_SCRATCH); - return (EIO); - } - SES_VLOG(ssc, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm %d Nspkrs %d\n", - sdata[0], sdata[1], sdata[2], sdata[3], sdata[4], sdata[5]); - cfg->Nfans = sdata[0]; - cfg->Npwr = sdata[1]; - cfg->Nslots = sdata[2]; - cfg->DoorLock = sdata[3]; - cfg->Ntherm = sdata[4]; - cfg->Nspkrs = sdata[5]; - cfg->Nalarm = NPSEUDO_ALARM; - SES_FREE(sdata, SAFT_SCRATCH); - return (0); -} - -static int -safte_rdstat(ses_softc_t *ssc, int slpflg) -{ - int err, oid, r, i, hiwater, nitems, amt; - uint16_t tempflags; - size_t buflen; - uint8_t status, oencstat; - char *sdata, cdb[10]; - struct scfg *cc = ssc->ses_private; - - - /* - * The number of objects overstates things a bit, - * both for the bogus 'thermometer' entries and - * the drive status (which isn't read at the same - * time as the enclosure status), but that's okay. - */ - buflen = 4 * cc->Nslots; - if (ssc->ses_nobjects > buflen) - buflen = ssc->ses_nobjects; - sdata = SES_MALLOC(buflen); - if (sdata == NULL) - return (ENOMEM); - - cdb[0] = READ_BUFFER; - cdb[1] = 1; - cdb[2] = SAFTE_RD_RDESTS; - cdb[3] = 0; - cdb[4] = 0; - cdb[5] = 0; - cdb[6] = 0; - cdb[7] = (buflen >> 8) & 0xff; - cdb[8] = buflen & 0xff; - cdb[9] = 0; - amt = buflen; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - if (err) { - SES_FREE(sdata, buflen); - return (err); - } - hiwater = buflen - amt; - - - /* - * invalidate all status bits. - */ - for (i = 0; i < ssc->ses_nobjects; i++) - ssc->ses_objmap[i].svalid = 0; - oencstat = ssc->ses_encstat & ALL_ENC_STAT; - ssc->ses_encstat = 0; - - - /* - * Now parse returned buffer. - * If we didn't get enough data back, - * that's considered a fatal error. - */ - oid = r = 0; - - for (nitems = i = 0; i < cc->Nfans; i++) { - SAFT_BAIL(r, hiwater, sdata, buflen); - /* - * 0 = Fan Operational - * 1 = Fan is malfunctioning - * 2 = Fan is not present - * 0x80 = Unknown or Not Reportable Status - */ - ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ - ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ - switch ((int)(uint8_t)sdata[r]) { - case 0: - nitems++; - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - /* - * We could get fancier and cache - * fan speeds that we have set, but - * that isn't done now. - */ - ssc->ses_objmap[oid].encstat[3] = 7; - break; - - case 1: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; - /* - * FAIL and FAN STOPPED synthesized - */ - ssc->ses_objmap[oid].encstat[3] = 0x40; - /* - * Enclosure marked with CRITICAL error - * if only one fan or no thermometers, - * else the NONCRITICAL error is set. - */ - if (cc->Nfans == 1 || cc->Ntherm == 0) - ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; - else - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - break; - case 2: - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_NOTINSTALLED; - ssc->ses_objmap[oid].encstat[3] = 0; - /* - * Enclosure marked with CRITICAL error - * if only one fan or no thermometers, - * else the NONCRITICAL error is set. - */ - if (cc->Nfans == 1) - ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; - else - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - break; - case 0x80: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - default: - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_UNSUPPORTED; - SES_LOG(ssc, "Unknown fan%d status 0x%x\n", i, - sdata[r] & 0xff); - break; - } - ssc->ses_objmap[oid++].svalid = 1; - r++; - } - - /* - * No matter how you cut it, no cooling elements when there - * should be some there is critical. - */ - if (cc->Nfans && nitems == 0) { - ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; - } - - - for (i = 0; i < cc->Npwr; i++) { - SAFT_BAIL(r, hiwater, sdata, buflen); - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; - ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ - ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ - ssc->ses_objmap[oid].encstat[3] = 0x20; /* requested on */ - switch ((uint8_t)sdata[r]) { - case 0x00: /* pws operational and on */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - break; - case 0x01: /* pws operational and off */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = 0x10; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - case 0x10: /* pws is malfunctioning and commanded on */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; - ssc->ses_objmap[oid].encstat[3] = 0x61; - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - break; - - case 0x11: /* pws is malfunctioning and commanded off */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NONCRIT; - ssc->ses_objmap[oid].encstat[3] = 0x51; - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - break; - case 0x20: /* pws is not present */ - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_NOTINSTALLED; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - case 0x21: /* pws is present */ - /* - * This is for enclosures that cannot tell whether the - * device is on or malfunctioning, but know that it is - * present. Just fall through. - */ - /* FALLTHROUGH */ - case 0x80: /* Unknown or Not Reportable Status */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - default: - SES_LOG(ssc, "unknown power supply %d status (0x%x)\n", - i, sdata[r] & 0xff); - break; - } - ssc->ses_objmap[oid++].svalid = 1; - r++; - } - - /* - * Skip over Slot SCSI IDs - */ - r += cc->Nslots; - - /* - * We always have doorlock status, no matter what, - * but we only save the status if we have one. - */ - SAFT_BAIL(r, hiwater, sdata, buflen); - if (cc->DoorLock) { - /* - * 0 = Door Locked - * 1 = Door Unlocked, or no Lock Installed - * 0x80 = Unknown or Not Reportable Status - */ - ssc->ses_objmap[oid].encstat[1] = 0; - ssc->ses_objmap[oid].encstat[2] = 0; - switch ((uint8_t)sdata[r]) { - case 0: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = 0; - break; - case 1: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = 1; - break; - case 0x80: - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNKNOWN; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_encstat |= SES_ENCSTAT_INFO; - break; - default: - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_UNSUPPORTED; - SES_LOG(ssc, "unknown lock status 0x%x\n", - sdata[r] & 0xff); - break; - } - ssc->ses_objmap[oid++].svalid = 1; - } - r++; - - /* - * We always have speaker status, no matter what, - * but we only save the status if we have one. - */ - SAFT_BAIL(r, hiwater, sdata, buflen); - if (cc->Nspkrs) { - ssc->ses_objmap[oid].encstat[1] = 0; - ssc->ses_objmap[oid].encstat[2] = 0; - if (sdata[r] == 1) { - /* - * We need to cache tone urgency indicators. - * Someday. - */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NONCRIT; - ssc->ses_objmap[oid].encstat[3] = 0x8; - ssc->ses_encstat |= SES_ENCSTAT_NONCRITICAL; - } else if (sdata[r] == 0) { - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = 0; - } else { - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_UNSUPPORTED; - ssc->ses_objmap[oid].encstat[3] = 0; - SES_LOG(ssc, "unknown spkr status 0x%x\n", - sdata[r] & 0xff); - } - ssc->ses_objmap[oid++].svalid = 1; - } - r++; - - for (i = 0; i < cc->Ntherm; i++) { - SAFT_BAIL(r, hiwater, sdata, buflen); - /* - * Status is a range from -10 to 245 deg Celsius, - * which we need to normalize to -20 to -245 according - * to the latest SCSI spec, which makes little - * sense since this would overflow an 8bit value. - * Well, still, the base normalization is -20, - * not -10, so we have to adjust. - * - * So what's over and under temperature? - * Hmm- we'll state that 'normal' operating - * is 10 to 40 deg Celsius. - */ - - /* - * Actually.... All of the units that people out in the world - * seem to have do not come even close to setting a value that - * complies with this spec. - * - * The closest explanation I could find was in an - * LSI-Logic manual, which seemed to indicate that - * this value would be set by whatever the I2C code - * would interpolate from the output of an LM75 - * temperature sensor. - * - * This means that it is impossible to use the actual - * numeric value to predict anything. But we don't want - * to lose the value. So, we'll propagate the *uncorrected* - * value and set SES_OBJSTAT_NOTAVAIL. We'll depend on the - * temperature flags for warnings. - */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_NOTAVAIL; - ssc->ses_objmap[oid].encstat[1] = 0; - ssc->ses_objmap[oid].encstat[2] = sdata[r]; - ssc->ses_objmap[oid].encstat[3] = 0; - ssc->ses_objmap[oid++].svalid = 1; - r++; - } - - /* - * Now, for "pseudo" thermometers, we have two bytes - * of information in enclosure status- 16 bits. Actually, - * the MSB is a single TEMP ALERT flag indicating whether - * any other bits are set, but, thanks to fuzzy thinking, - * in the SAF-TE spec, this can also be set even if no - * other bits are set, thus making this really another - * binary temperature sensor. - */ - - SAFT_BAIL(r, hiwater, sdata, buflen); - tempflags = sdata[r++]; - SAFT_BAIL(r, hiwater, sdata, buflen); - tempflags |= (tempflags << 8) | sdata[r++]; - - for (i = 0; i < NPSEUDO_THERM; i++) { - ssc->ses_objmap[oid].encstat[1] = 0; - if (tempflags & (1 << (NPSEUDO_THERM - i - 1))) { - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_CRIT; - ssc->ses_objmap[4].encstat[2] = 0xff; - /* - * Set 'over temperature' failure. - */ - ssc->ses_objmap[oid].encstat[3] = 8; - ssc->ses_encstat |= SES_ENCSTAT_CRITICAL; - } else { - /* - * We used to say 'not available' and synthesize a - * nominal 30 deg (C)- that was wrong. Actually, - * Just say 'OK', and use the reserved value of - * zero. - */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[2] = 0; - ssc->ses_objmap[oid].encstat[3] = 0; - } - ssc->ses_objmap[oid++].svalid = 1; - } - - /* - * Get alarm status. - */ - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - ssc->ses_objmap[oid].encstat[3] = ssc->ses_objmap[oid].priv; - ssc->ses_objmap[oid++].svalid = 1; - - /* - * Now get drive slot status - */ - cdb[2] = SAFTE_RD_RDDSTS; - amt = buflen; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - if (err) { - SES_FREE(sdata, buflen); - return (err); - } - hiwater = buflen - amt; - for (r = i = 0; i < cc->Nslots; i++, r += 4) { - SAFT_BAIL(r+3, hiwater, sdata, buflen); - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_UNSUPPORTED; - ssc->ses_objmap[oid].encstat[1] = (uint8_t) i; - ssc->ses_objmap[oid].encstat[2] = 0; - ssc->ses_objmap[oid].encstat[3] = 0; - status = sdata[r+3]; - if ((status & 0x1) == 0) { /* no device */ - ssc->ses_objmap[oid].encstat[0] = - SES_OBJSTAT_NOTINSTALLED; - } else { - ssc->ses_objmap[oid].encstat[0] = SES_OBJSTAT_OK; - } - if (status & 0x2) { - ssc->ses_objmap[oid].encstat[2] = 0x8; - } - if ((status & 0x4) == 0) { - ssc->ses_objmap[oid].encstat[3] = 0x10; - } - ssc->ses_objmap[oid++].svalid = 1; - } - /* see comment below about sticky enclosure status */ - ssc->ses_encstat |= ENCI_SVALID | oencstat; - SES_FREE(sdata, buflen); - return (0); -} - -static int -set_objstat_sel(ses_softc_t *ssc, ses_objstat *obp, int slp) -{ - int idx; - encobj *ep; - struct scfg *cc = ssc->ses_private; - - if (cc == NULL) - return (0); - - idx = (int)obp->obj_id; - ep = &ssc->ses_objmap[idx]; - - switch (ep->enctype) { - case SESTYP_DEVICE: - if (obp->cstat[0] & SESCTL_PRDFAIL) { - ep->priv |= 0x40; - } - /* SESCTL_RSTSWAP has no correspondence in SAF-TE */ - if (obp->cstat[0] & SESCTL_DISABLE) { - ep->priv |= 0x80; - /* - * Hmm. Try to set the 'No Drive' flag. - * Maybe that will count as a 'disable'. - */ - } - if (ep->priv & 0xc6) { - ep->priv &= ~0x1; - } else { - ep->priv |= 0x1; /* no errors */ - } - wrslot_stat(ssc, slp); - break; - case SESTYP_POWER: - /* - * Okay- the only one that makes sense here is to - * do the 'disable' for a power supply. - */ - if (obp->cstat[0] & SESCTL_DISABLE) { - (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, - idx - cc->pwroff, 0, 0, slp); - } - break; - case SESTYP_FAN: - /* - * Okay- the only one that makes sense here is to - * set fan speed to zero on disable. - */ - if (obp->cstat[0] & SESCTL_DISABLE) { - /* remember- fans are the first items, so idx works */ - (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); - } - break; - case SESTYP_DOORLOCK: - /* - * Well, we can 'disable' the lock. - */ - if (obp->cstat[0] & SESCTL_DISABLE) { - cc->flag2 &= ~SAFT_FLG2_LOCKDOOR; - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - } - break; - case SESTYP_ALARM: - /* - * Well, we can 'disable' the alarm. - */ - if (obp->cstat[0] & SESCTL_DISABLE) { - cc->flag2 &= ~SAFT_FLG1_ALARM; - ep->priv |= 0x40; /* Muted */ - (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, - cc->flag2, 0, slp); - } - break; - default: - break; - } - ep->svalid = 0; - return (0); -} - -/* - * This function handles all of the 16 byte WRITE BUFFER commands. - */ -static int -wrbuf16(ses_softc_t *ssc, uint8_t op, uint8_t b1, uint8_t b2, - uint8_t b3, int slp) -{ - int err, amt; - char *sdata; - struct scfg *cc = ssc->ses_private; - static char cdb[10] = { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, 16, 0 }; - - if (cc == NULL) - return (0); - - sdata = SES_MALLOC(16); - if (sdata == NULL) - return (ENOMEM); - - SES_DLOG(ssc, "saf_wrbuf16 %x %x %x %x\n", op, b1, b2, b3); - - sdata[0] = op; - sdata[1] = b1; - sdata[2] = b2; - sdata[3] = b3; - MEMZERO(&sdata[4], 12); - amt = -16; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - SES_FREE(sdata, 16); - return (err); -} - -/* - * This function updates the status byte for the device slot described. - * - * Since this is an optional SAF-TE command, there's no point in - * returning an error. - */ -static void -wrslot_stat(ses_softc_t *ssc, int slp) -{ - int i, amt; - encobj *ep; - char cdb[10], *sdata; - struct scfg *cc = ssc->ses_private; - - if (cc == NULL) - return; - - SES_DLOG(ssc, "saf_wrslot\n"); - cdb[0] = WRITE_BUFFER; - cdb[1] = 1; - cdb[2] = 0; - cdb[3] = 0; - cdb[4] = 0; - cdb[5] = 0; - cdb[6] = 0; - cdb[7] = 0; - cdb[8] = cc->Nslots * 3 + 1; - cdb[9] = 0; - - sdata = SES_MALLOC(cc->Nslots * 3 + 1); - if (sdata == NULL) - return; - MEMZERO(sdata, cc->Nslots * 3 + 1); - - sdata[0] = SAFTE_WT_DSTAT; - for (i = 0; i < cc->Nslots; i++) { - ep = &ssc->ses_objmap[cc->slotoff + i]; - SES_DLOG(ssc, "saf_wrslot %d <- %x\n", i, ep->priv & 0xff); - sdata[1 + (3 * i)] = ep->priv & 0xff; - } - amt = -(cc->Nslots * 3 + 1); - (void) ses_runcmd(ssc, cdb, 10, sdata, &amt); - SES_FREE(sdata, cc->Nslots * 3 + 1); -} - -/* - * This function issues the "PERFORM SLOT OPERATION" command. - */ -static int -perf_slotop(ses_softc_t *ssc, uint8_t slot, uint8_t opflag, int slp) -{ - int err, amt; - char *sdata; - struct scfg *cc = ssc->ses_private; - static char cdb[10] = - { WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SAFT_SCRATCH, 0 }; - - if (cc == NULL) - return (0); - - sdata = SES_MALLOC(SAFT_SCRATCH); - if (sdata == NULL) - return (ENOMEM); - MEMZERO(sdata, SAFT_SCRATCH); - - sdata[0] = SAFTE_WT_SLTOP; - sdata[1] = slot; - sdata[2] = opflag; - SES_DLOG(ssc, "saf_slotop slot %d op %x\n", slot, opflag); - amt = -SAFT_SCRATCH; - err = ses_runcmd(ssc, cdb, 10, sdata, &amt); - SES_FREE(sdata, SAFT_SCRATCH); - return (err); -} diff --git a/sys/cam/scsi/scsi_ses.h b/sys/cam/scsi/scsi_ses.h index a52d5171c..0aeb22e0c 100644 --- a/sys/cam/scsi/scsi_ses.h +++ b/sys/cam/scsi/scsi_ses.h @@ -29,107 +29,2131 @@ * */ -#define SESIOC ('s' - 040) -#define SESIOC_GETNOBJ _IO(SESIOC, 1) -#define SESIOC_GETOBJMAP _IO(SESIOC, 2) -#define SESIOC_GETENCSTAT _IO(SESIOC, 3) -#define SESIOC_SETENCSTAT _IO(SESIOC, 4) -#define SESIOC_GETOBJSTAT _IO(SESIOC, 5) -#define SESIOC_SETOBJSTAT _IO(SESIOC, 6) -#define SESIOC_GETTEXT _IO(SESIOC, 7) -#define SESIOC_INIT _IO(SESIOC, 8) +#ifndef _SCSI_SES_H_ +#define _SCSI_SES_H_ -/* - * Platform Independent Definitions for SES devices. - */ -/* - * SCSI Based Environmental Services Application Defines - * - * Based almost entirely on SCSI-3 SES Revision 8A specification, - * but slightly abstracted as the underlying device may in fact - * be a SAF-TE or vendor unique device. - */ -/* - * SES Driver Operations: - * (The defines themselves are platform and access method specific) - * - * SESIOC_GETNOBJ - * SESIOC_GETOBJMAP - * SESIOC_GETENCSTAT - * SESIOC_SETENCSTAT - * SESIOC_GETOBJSTAT - * SESIOC_SETOBJSTAT - * SESIOC_INIT - * - * - * An application finds out how many objects an SES instance - * is managing by performing a SESIOC_GETNOBJ operation. It then - * performs a SESIOC_GETOBJMAP to get the map that contains the - * object identifiers for all objects (see ses_object below). - * This information is static. - * - * The application may perform SESIOC_GETOBJSTAT operations to retrieve - * status on an object (see the ses_objstat structure below), SESIOC_SETOBJSTAT - * operations to set status for an object. - * - * Similarly overall enclosure status me be fetched or set via - * SESIOC_GETENCSTAT or SESIOC_SETENCSTAT operations (see ses_encstat below). - * - * Readers should note that there is nothing that requires either a set - * or a clear operation to actually latch and do anything in the target. - * - * A SESIOC_INIT operation causes the enclosure to be initialized. - */ +#include -typedef struct { - unsigned int obj_id; /* Object Identifier */ - unsigned char subencid; /* SubEnclosure ID */ - unsigned char object_type; /* Object Type */ -} ses_object; - -/* Object Types */ -#define SESTYP_UNSPECIFIED 0x00 -#define SESTYP_DEVICE 0x01 -#define SESTYP_POWER 0x02 -#define SESTYP_FAN 0x03 -#define SESTYP_THERM 0x04 -#define SESTYP_DOORLOCK 0x05 -#define SESTYP_ALARM 0x06 -#define SESTYP_ESCC 0x07 /* Enclosure SCC */ -#define SESTYP_SCC 0x08 /* SCC */ -#define SESTYP_NVRAM 0x09 -#define SESTYP_UPS 0x0b -#define SESTYP_DISPLAY 0x0c -#define SESTYP_KEYPAD 0x0d -#define SESTYP_ENCLOSURE 0x0e -#define SESTYP_SCSIXVR 0x0f -#define SESTYP_LANGUAGE 0x10 -#define SESTYP_COMPORT 0x11 -#define SESTYP_VOM 0x12 -#define SESTYP_AMMETER 0x13 -#define SESTYP_SCSI_TGT 0x14 -#define SESTYP_SCSI_INI 0x15 -#define SESTYP_SUBENC 0x16 -#define SESTYP_ARRAY 0x17 -#define SESTYP_SASEXPANDER 0x18 -#define SESTYP_SASCONNECTOR 0x19 +/*========================== Field Extraction Macros =========================*/ +#define MK_ENUM(S, F, SUFFIX) S ## _ ## F ## SUFFIX -/* - * Overall Enclosure Status - */ -typedef unsigned char ses_encstat; -#define SES_ENCSTAT_UNRECOV 0x1 -#define SES_ENCSTAT_CRITICAL 0x2 -#define SES_ENCSTAT_NONCRITICAL 0x4 -#define SES_ENCSTAT_INFO 0x8 +#define GEN_GETTER(LS, US, LF, UF) \ +static inline int \ +LS ## _get_ ## LF(struct LS *elem) { \ + return ((elem->bytes[MK_ENUM(US,UF,_BYTE)] & MK_ENUM(US,UF,_MASK)) \ + >> MK_ENUM(US,UF,_SHIFT)); \ +} + +#define GEN_SETTER(LS, US, LF, UF) \ +static inline void \ +LS ## _set_ ## LF(struct LS *elem, int val) { \ + elem->bytes[MK_ENUM(US,UF,_BYTE)] &= ~MK_ENUM(US,UF,_MASK); \ + elem->bytes[MK_ENUM(US,UF,_BYTE)] |= \ + (val << MK_ENUM(US,UF,_SHIFT)) & MK_ENUM(US,UF,_MASK); \ +} + +#define GEN_HDR_GETTER(LS, US, LF, UF) \ +static inline int \ +LS ## _get_ ## LF(struct LS *page) { \ + return ((page->hdr.page_specific_flags & MK_ENUM(US,UF,_MASK)) \ + >> MK_ENUM(US,UF,_SHIFT)); \ +} + +#define GEN_HDR_SETTER(LS, US, LF, UF) \ +static inline void \ +LS ## _set_ ## LF(struct LS *page, int val) { \ + page->hdr.page_specific_flags &= ~MK_ENUM(US,UF,_MASK); \ + page->hdr.page_specific_flags |= \ + (val << MK_ENUM(US,UF,_SHIFT)) & MK_ENUM(US,UF,_MASK); \ +} + +#define GEN_ACCESSORS(LS, US, LF, UF) \ +GEN_GETTER(LS, US, LF, UF) \ +GEN_SETTER(LS, US, LF, UF) + +#define GEN_HDR_ACCESSORS(LS, US, LF, UF) \ +GEN_HDR_GETTER(LS, US, LF, UF) \ +GEN_HDR_SETTER(LS, US, LF, UF) + +/*=============== Common SCSI ENC Diagnostic Page Structures ===============*/ +struct ses_page_hdr { + uint8_t page_code; + uint8_t page_specific_flags; + uint8_t length[2]; + uint8_t gen_code[4]; +}; + +static inline size_t +ses_page_length(const struct ses_page_hdr *hdr) +{ + /* + * The page length as received only accounts for bytes that + * follow the length field, namely starting with the generation + * code field. + */ + return (scsi_2btoul(hdr->length) + + offsetof(struct ses_page_hdr, gen_code)); +} + +/*============= SCSI ENC Configuration Diagnostic Page Structures ============*/ +struct ses_enc_desc { + uint8_t byte0; + /* + * reserved0 : 1, + * rel_id : 3, relative enclosure process id + * reserved1 : 1, + * num_procs : 3; number of enclosure procesenc + */ + uint8_t subenc_id; /* Sub-enclosure Identifier */ + uint8_t num_types; /* # of supported types */ + uint8_t length; /* Enclosure Descriptor Length */ + uint8_t logical_id[8]; /* formerly wwn */ + uint8_t vendor_id[8]; + uint8_t product_id[16]; + uint8_t product_rev[4]; + uint8_t vendor_bytes[]; +}; + +static inline uint8_t * +ses_enc_desc_last_byte(struct ses_enc_desc *encdesc) +{ + return (&encdesc->length + encdesc->length); +} + +static inline struct ses_enc_desc * +ses_enc_desc_next(struct ses_enc_desc *encdesc) +{ + return ((struct ses_enc_desc *)(ses_enc_desc_last_byte(encdesc) + 1)); +} + +static inline int +ses_enc_desc_is_complete(struct ses_enc_desc *encdesc, uint8_t *last_buf_byte) +{ + return (&encdesc->length <= last_buf_byte + && ses_enc_desc_last_byte(encdesc) <= last_buf_byte); +} + +struct ses_elm_type_desc { + uint8_t etype_elm_type; /* type of element */ + uint8_t etype_maxelt; /* maximum supported */ + uint8_t etype_subenc; /* in sub-enclosure #n */ + uint8_t etype_txt_len; /* Type Descriptor Text Length */ +}; + +struct ses_cfg_page { + struct ses_page_hdr hdr; + struct ses_enc_desc subencs[]; + /* type descriptors */ + /* type text */ +}; + +static inline int +ses_cfg_page_get_num_subenc(struct ses_cfg_page *page) +{ + return (page->hdr.page_specific_flags + 1); +} + + +/*================ SCSI SES Control Diagnostic Page Structures ==============*/ +struct ses_ctrl_common { + uint8_t bytes[1]; +}; + +enum ses_ctrl_common_field_data { + SES_CTRL_COMMON_SELECT_BYTE = 0, + SES_CTRL_COMMON_SELECT_MASK = 0x80, + SES_CTRL_COMMON_SELECT_SHIFT = 7, + + SES_CTRL_COMMON_PRDFAIL_BYTE = 0, + SES_CTRL_COMMON_PRDFAIL_MASK = 0x40, + SES_CTRL_COMMON_PRDFAIL_SHIFT = 6, + + SES_CTRL_COMMON_DISABLE_BYTE = 0, + SES_CTRL_COMMON_DISABLE_MASK = 0x20, + SES_CTRL_COMMON_DISABLE_SHIFT = 5, + + SES_CTRL_COMMON_RST_SWAP_BYTE = 0, + SES_CTRL_COMMON_RST_SWAP_MASK = 0x10, + SES_CTRL_COMMON_RST_SWAP_SHIFT = 4 +}; + +#define GEN_SES_CTRL_COMMON_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_common, SES_CTRL_COMMON, LCASE, UCASE) +GEN_SES_CTRL_COMMON_ACCESSORS(select, SELECT) +GEN_SES_CTRL_COMMON_ACCESSORS(prdfail, PRDFAIL) +GEN_SES_CTRL_COMMON_ACCESSORS(disable, DISABLE) +GEN_SES_CTRL_COMMON_ACCESSORS(rst_swap, RST_SWAP) +#undef GEN_SES_CTRL_COMMON_ACCESSORS + +/*------------------------ Device Slot Control Element ----------------------*/ +struct ses_ctrl_dev_slot { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_dev_slot_field_data { + SES_CTRL_DEV_SLOT_RQST_ACTIVE_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_ACTIVE_MASK = 0x80, + SES_CTRL_DEV_SLOT_RQST_ACTIVE_SHIFT = 7, + + SES_CTRL_DEV_SLOT_DO_NOT_REMOVE_BYTE = 1, + SES_CTRL_DEV_SLOT_DO_NOT_REMOVE_MASK = 0x40, + SES_CTRL_DEV_SLOT_DO_NOT_REMOVE_SHIFT = 6, + + SES_CTRL_DEV_SLOT_RQST_MISSING_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_MISSING_MASK = 0x10, + SES_CTRL_DEV_SLOT_RQST_MISSING_SHIFT = 4, + + SES_CTRL_DEV_SLOT_RQST_INSERT_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_INSERT_MASK = 0x08, + SES_CTRL_DEV_SLOT_RQST_INSERT_SHIFT = 3, + + SES_CTRL_DEV_SLOT_RQST_REMOVE_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_REMOVE_MASK = 0x04, + SES_CTRL_DEV_SLOT_RQST_REMOVE_SHIFT = 2, + + SES_CTRL_DEV_SLOT_RQST_IDENT_BYTE = 1, + SES_CTRL_DEV_SLOT_RQST_IDENT_MASK = 0x02, + SES_CTRL_DEV_SLOT_RQST_IDENT_SHIFT = 1, + + SES_CTRL_DEV_SLOT_RQST_FAULT_BYTE = 2, + SES_CTRL_DEV_SLOT_RQST_FAULT_MASK = 0x20, + SES_CTRL_DEV_SLOT_RQST_FAULT_SHIFT = 5, + + SES_CTRL_DEV_SLOT_DEVICE_OFF_BYTE = 2, + SES_CTRL_DEV_SLOT_DEVICE_OFF_MASK = 0x10, + SES_CTRL_DEV_SLOT_DEVICE_OFF_SHIFT = 4, + + SES_CTRL_DEV_SLOT_ENABLE_BYP_A_BYTE = 2, + SES_CTRL_DEV_SLOT_ENABLE_BYP_A_MASK = 0x08, + SES_CTRL_DEV_SLOT_ENABLE_BYP_A_SHIFT = 3, + + SES_CTRL_DEV_SLOT_ENABLE_BYP_B_BYTE = 2, + SES_CTRL_DEV_SLOT_ENABLE_BYP_B_MASK = 0x04, + SES_CTRL_DEV_SLOT_ENABLE_BYP_B_SHIFT = 2 +}; +#define GEN_SES_CTRL_DEV_SLOT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_dev_slot, SES_CTRL_DEV_SLOT, LCASE, UCASE) + +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_active, RQST_ACTIVE) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(do_not_remove, DO_NOT_REMOVE) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_missing, RQST_MISSING) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_insert, RQST_INSERT) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_remove, RQST_REMOVE) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(rqst_fault, RQST_FAULT) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(device_off, DEVICE_OFF) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(enable_byp_a, ENABLE_BYP_A) +GEN_SES_CTRL_DEV_SLOT_ACCESSORS(enable_byp_b, ENABLE_BYP_B) +#undef GEN_SES_CTRL_DEV_SLOT_ACCESSORS + +/*--------------------- Array Device Slot Control Element --------------------*/ +struct ses_ctrl_array_dev_slot { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_array_dev_slot_field_data { + SES_CTRL_ARRAY_DEV_SLOT_RQST_OK_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_OK_MASK = 0x80, + SES_CTRL_ARRAY_DEV_SLOT_RQST_OK_SHIFT = 7, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_RSVD_DEVICE_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_RSVD_DEVICE_MASK = 0x40, + SES_CTRL_ARRAY_DEV_SLOT_RQST_RSVD_DEVICE_SHIFT = 6, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_HOT_SPARE_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_HOT_SPARE_MASK = 0x20, + SES_CTRL_ARRAY_DEV_SLOT_RQST_HOT_SPARE_SHIFT = 5, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_CONS_CHECK_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_CONS_CHECK_MASK = 0x10, + SES_CTRL_ARRAY_DEV_SLOT_RQST_CONS_CHECK_SHIFT = 4, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_CRIT_ARRAY_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_CRIT_ARRAY_MASK = 0x08, + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_CRIT_ARRAY_SHIFT = 3, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_FAILED_ARRAY_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_FAILED_ARRAY_MASK = 0x04, + SES_CTRL_ARRAY_DEV_SLOT_RQST_IN_FAILED_ARRAY_SHIFT = 2, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_MASK = 0x02, + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_SHIFT = 1, + + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_ABORT_BYTE = 0, + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_ABORT_MASK = 0x01, + SES_CTRL_ARRAY_DEV_SLOT_RQST_REBUILD_REMAP_ABORT_SHIFT = 0 + + /* + * The remaining fields are identical to the device + * slot element type. Access them through the device slot + * element type and its accessors. + */ +}; +#define GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_array_dev_slot, SES_CTRL_ARRAY_DEV_SLOT, \ + LCASE, UCASE) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_ok, RQST_OK) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_rsvd_device, RQST_RSVD_DEVICE) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_hot_spare, RQST_HOT_SPARE) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_cons_check, RQST_CONS_CHECK) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_in_crit_array, RQST_IN_CRIT_ARRAY) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_in_failed_array, + RQST_IN_FAILED_ARRAY) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_rebuild_remap, RQST_REBUILD_REMAP) +GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS(rqst_rebuild_remap_abort, + RQST_REBUILD_REMAP_ABORT) +#undef GEN_SES_CTRL_ARRAY_DEV_SLOT_ACCESSORS + +/*----------------------- Power Supply Control Element -----------------------*/ +struct ses_ctrl_power_supply { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_power_supply_field_data { + SES_CTRL_POWER_SUPPLY_RQST_IDENT_BYTE = 0, + SES_CTRL_POWER_SUPPLY_RQST_IDENT_MASK = 0x80, + SES_CTRL_POWER_SUPPLY_RQST_IDENT_SHIFT = 7, + + SES_CTRL_POWER_SUPPLY_RQST_FAIL_BYTE = 2, + SES_CTRL_POWER_SUPPLY_RQST_FAIL_MASK = 0x40, + SES_CTRL_POWER_SUPPLY_RQST_FAIL_SHIFT = 6, + + SES_CTRL_POWER_SUPPLY_RQST_ON_BYTE = 2, + SES_CTRL_POWER_SUPPLY_RQST_ON_MASK = 0x20, + SES_CTRL_POWER_SUPPLY_RQST_ON_SHIFT = 5 +}; + +#define GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_power_supply, SES_CTRL_POWER_SUPPLY, LCASE, UCASE) +GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS(rqst_on, RQST_ON) +#undef GEN_SES_CTRL_POWER_SUPPLY_ACCESSORS + +/*-------------------------- Cooling Control Element -------------------------*/ +struct ses_ctrl_cooling { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_cooling_field_data { + SES_CTRL_COOLING_RQST_IDENT_BYTE = 0, + SES_CTRL_COOLING_RQST_IDENT_MASK = 0x80, + SES_CTRL_COOLING_RQST_IDENT_SHIFT = 7, + + SES_CTRL_COOLING_RQST_FAIL_BYTE = 2, + SES_CTRL_COOLING_RQST_FAIL_MASK = 0x40, + SES_CTRL_COOLING_RQST_FAIL_SHIFT = 6, + + SES_CTRL_COOLING_RQST_ON_BYTE = 2, + SES_CTRL_COOLING_RQST_ON_MASK = 0x20, + SES_CTRL_COOLING_RQST_ON_SHIFT = 5, + + SES_CTRL_COOLING_RQSTED_SPEED_CODE_BYTE = 2, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_MASK = 0x07, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_SHIFT = 2, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_UNCHANGED = 0x00, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_LOWEST = 0x01, + SES_CTRL_COOLING_RQSTED_SPEED_CODE_HIGHEST = 0x07 +}; + +#define GEN_SES_CTRL_COOLING_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_cooling, SES_CTRL_COOLING, LCASE, UCASE) +GEN_SES_CTRL_COOLING_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_COOLING_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_COOLING_ACCESSORS(rqst_on, RQST_ON) +GEN_SES_CTRL_COOLING_ACCESSORS(rqsted_speed_code, RQSTED_SPEED_CODE) +#undef GEN_SES_CTRL_COOLING_ACCESSORS + +/*-------------------- Temperature Sensor Control Element --------------------*/ +struct ses_ctrl_temp_sensor { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_temp_sensor_field_data { + SES_CTRL_TEMP_SENSOR_RQST_IDENT_BYTE = 0, + SES_CTRL_TEMP_SENSOR_RQST_IDENT_MASK = 0x80, + SES_CTRL_TEMP_SENSOR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_TEMP_SENSOR_RQST_FAIL_BYTE = 0, + SES_CTRL_TEMP_SENSOR_RQST_FAIL_MASK = 0x40, + SES_CTRL_TEMP_SENSOR_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_temp_sensor, SES_CTRL_TEMP_SENSOR, LCASE, UCASE) +GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_TEMP_SENSOR_ACCESSORS + +/*------------------------- Door Lock Control Element ------------------------*/ +struct ses_ctrl_door_lock { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_door_lock_field_data { + SES_CTRL_DOOR_LOCK_RQST_IDENT_BYTE = 0, + SES_CTRL_DOOR_LOCK_RQST_IDENT_MASK = 0x80, + SES_CTRL_DOOR_LOCK_RQST_IDENT_SHIFT = 7, + + SES_CTRL_DOOR_LOCK_RQST_FAIL_BYTE = 0, + SES_CTRL_DOOR_LOCK_RQST_FAIL_MASK = 0x40, + SES_CTRL_DOOR_LOCK_RQST_FAIL_SHIFT = 6, + + SES_CTRL_DOOR_LOCK_UNLOCK_BYTE = 2, + SES_CTRL_DOOR_LOCK_UNLOCK_MASK = 0x01, + SES_CTRL_DOOR_LOCK_UNLOCK_SHIFT = 0 +}; + +#define GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_door_lock, SES_CTRL_DOOR_LOCK, LCASE, UCASE) +GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_DOOR_LOCK_ACCESSORS(unlock, UNLOCK) +#undef GEN_SES_CTRL_DOOR_LOCK_ACCESSORS + +/*----------------------- Audible Alarm Control Element ----------------------*/ +struct ses_ctrl_audible_alarm { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_audible_alarm_field_data { + SES_CTRL_AUDIBLE_ALARM_RQST_IDENT_BYTE = 0, + SES_CTRL_AUDIBLE_ALARM_RQST_IDENT_MASK = 0x80, + SES_CTRL_AUDIBLE_ALARM_RQST_IDENT_SHIFT = 7, + + SES_CTRL_AUDIBLE_ALARM_RQST_FAIL_BYTE = 0, + SES_CTRL_AUDIBLE_ALARM_RQST_FAIL_MASK = 0x40, + SES_CTRL_AUDIBLE_ALARM_RQST_FAIL_SHIFT = 6, + + SES_CTRL_AUDIBLE_ALARM_SET_MUTE_BYTE = 2, + SES_CTRL_AUDIBLE_ALARM_SET_MUTE_MASK = 0x40, + SES_CTRL_AUDIBLE_ALARM_SET_MUTE_SHIFT = 6, + + SES_CTRL_AUDIBLE_ALARM_SET_REMIND_BYTE = 2, + SES_CTRL_AUDIBLE_ALARM_SET_REMIND_MASK = 0x10, + SES_CTRL_AUDIBLE_ALARM_SET_REMIND_SHIFT = 4, + + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_BYTE = 2, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_MASK = 0x0F, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_SHIFT = 0, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_INFO = 0x08, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_NON_CRIT = 0x04, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_CRIT = 0x02, + SES_CTRL_AUDIBLE_ALARM_TONE_CONTROL_UNRECOV = 0x01 +}; + +#define GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_audible_alarm, SES_CTRL_AUDIBLE_ALARM, LCASE, UCASE) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(set_mute, SET_MUTE) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(set_remind, SET_REMIND) +GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS(tone_control, TONE_CONTROL) +#undef GEN_SES_CTRL_AUDIBLE_ALARM_ACCESSORS + +/*--------- Enclosure Services Controller Electronics Control Element --------*/ +struct ses_ctrl_ecc_electronics { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_ecc_electronics_field_data { + SES_CTRL_ECC_ELECTRONICS_RQST_IDENT_BYTE = 0, + SES_CTRL_ECC_ELECTRONICS_RQST_IDENT_MASK = 0x80, + SES_CTRL_ECC_ELECTRONICS_RQST_IDENT_SHIFT = 7, + + SES_CTRL_ECC_ELECTRONICS_RQST_FAIL_BYTE = 0, + SES_CTRL_ECC_ELECTRONICS_RQST_FAIL_MASK = 0x40, + SES_CTRL_ECC_ELECTRONICS_RQST_FAIL_SHIFT = 6, + + SES_CTRL_ECC_ELECTRONICS_SELECT_ELEMENT_BYTE = 1, + SES_CTRL_ECC_ELECTRONICS_SELECT_ELEMENT_MASK = 0x01, + SES_CTRL_ECC_ELECTRONICS_SELECT_ELEMENT_SHIFT = 0 +}; + +#define GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_ecc_electronics, SES_CTRL_ECC_ELECTRONICS, \ + LCASE, UCASE) +GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS(select_element, SELECT_ELEMENT) +#undef GEN_SES_CTRL_ECC_ELECTRONICS_ACCESSORS + +/*----------- SCSI Services Controller Electronics Control Element -----------*/ +struct ses_ctrl_scc_electronics { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_scc_electronics_field_data { + SES_CTRL_SCC_ELECTRONICS_RQST_IDENT_BYTE = 0, + SES_CTRL_SCC_ELECTRONICS_RQST_IDENT_MASK = 0x80, + SES_CTRL_SCC_ELECTRONICS_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SCC_ELECTRONICS_RQST_FAIL_BYTE = 0, + SES_CTRL_SCC_ELECTRONICS_RQST_FAIL_MASK = 0x40, + SES_CTRL_SCC_ELECTRONICS_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_scc_electronics, SES_CTRL_SCC_ELECTRONICS, \ + LCASE, UCASE) +GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SCC_ELECTRONICS_ACCESSORS + +/*--------------------- Nonvolatile Cache Control Element --------------------*/ +struct ses_ctrl_nv_cache { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_nv_cache_field_data { + SES_CTRL_NV_CACHE_RQST_IDENT_BYTE = 0, + SES_CTRL_NV_CACHE_RQST_IDENT_MASK = 0x80, + SES_CTRL_NV_CACHE_RQST_IDENT_SHIFT = 7, + + SES_CTRL_NV_CACHE_RQST_FAIL_BYTE = 0, + SES_CTRL_NV_CACHE_RQST_FAIL_MASK = 0x40, + SES_CTRL_NV_CACHE_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_NV_CACHE_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_nv_cache, SES_CTRL_NV_CACHE, LCASE, UCASE) +GEN_SES_CTRL_NV_CACHE_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_NV_CACHE_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_NV_CACHE_ACCESSORS + +/*----------------- Invalid Operation Reason Control Element -----------------*/ +struct ses_ctrl_invalid_op_reason { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +/* There are no element specific fields currently defined in the spec. */ + +/*--------------- Uninterruptible Power Supply Control Element ---------------*/ +struct ses_ctrl_ups { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_ups_field_data { + SES_CTRL_UPS_RQST_IDENT_BYTE = 2, + SES_CTRL_UPS_RQST_IDENT_MASK = 0x80, + SES_CTRL_UPS_RQST_IDENT_SHIFT = 7, + + SES_CTRL_UPS_RQST_FAIL_BYTE = 2, + SES_CTRL_UPS_RQST_FAIL_MASK = 0x40, + SES_CTRL_UPS_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_UPS_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_ups, SES_CTRL_UPS, LCASE, UCASE) +GEN_SES_CTRL_UPS_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_UPS_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_UPS_ACCESSORS + +/*-------------------------- Display Control Element -------------------------*/ +struct ses_ctrl_display { + struct ses_ctrl_common common; + uint8_t bytes[1]; + uint8_t display_character[2]; +}; + +enum ses_ctrl_display_field_data { + SES_CTRL_DISPLAY_RQST_IDENT_BYTE = 0, + SES_CTRL_DISPLAY_RQST_IDENT_MASK = 0x80, + SES_CTRL_DISPLAY_RQST_IDENT_SHIFT = 7, + + SES_CTRL_DISPLAY_RQST_FAIL_BYTE = 0, + SES_CTRL_DISPLAY_RQST_FAIL_MASK = 0x40, + SES_CTRL_DISPLAY_RQST_FAIL_SHIFT = 6, + + SES_CTRL_DISPLAY_DISPLAY_MODE_BYTE = 0, + SES_CTRL_DISPLAY_DISPLAY_MODE_MASK = 0x03, + SES_CTRL_DISPLAY_DISPLAY_MODE_SHIFT = 6, + SES_CTRL_DISPLAY_DISPLAY_MODE_UNCHANGED = 0x0, + SES_CTRL_DISPLAY_DISPLAY_MODE_ESP = 0x1, + SES_CTRL_DISPLAY_DISPLAY_MODE_DC_FIELD = 0x2 +}; + +#define GEN_SES_CTRL_DISPLAY_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_display, SES_CTRL_DISPLAY, LCASE, UCASE) +GEN_SES_CTRL_DISPLAY_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_DISPLAY_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_DISPLAY_ACCESSORS(display_mode, DISPLAY_MODE) +#undef GEN_SES_CTRL_DISPLAY_ACCESSORS + +/*----------------------- Key Pad Entry Control Element ----------------------*/ +struct ses_ctrl_key_pad_entry { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_key_pad_entry_field_data { + SES_CTRL_KEY_PAD_ENTRY_RQST_IDENT_BYTE = 0, + SES_CTRL_KEY_PAD_ENTRY_RQST_IDENT_MASK = 0x80, + SES_CTRL_KEY_PAD_ENTRY_RQST_IDENT_SHIFT = 7, + + SES_CTRL_KEY_PAD_ENTRY_RQST_FAIL_BYTE = 0, + SES_CTRL_KEY_PAD_ENTRY_RQST_FAIL_MASK = 0x40, + SES_CTRL_KEY_PAD_ENTRY_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_key_pad_entry, SES_CTRL_KEY_PAD_ENTRY, LCASE, UCASE) +GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_KEY_PAD_ENTRY_ACCESSORS + +/*------------------------- Enclosure Control Element ------------------------*/ +struct ses_ctrl_enclosure { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_enclosure_field_data { + SES_CTRL_ENCLOSURE_RQST_IDENT_BYTE = 0, + SES_CTRL_ENCLOSURE_RQST_IDENT_MASK = 0x80, + SES_CTRL_ENCLOSURE_RQST_IDENT_SHIFT = 7, + + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_BYTE = 1, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_MASK = 0xC0, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_SHIFT = 6, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_NONE = 0x0, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_AFTER_DELAY = 0x1, + SES_CTRL_ENCLOSURE_POWER_CYCLE_RQST_CANCEL = 0x2, + + SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_BYTE = 1, + SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_MASK = 0x3F, + SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_SHIFT = 0, + SES_CTRL_ENCLOSURE_POWER_CYCLE_DELAY_MAX = 60,/*minutes*/ + + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_BYTE = 2, + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_MASK = 0xFC, + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_SHIFT = 2, + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_MAX_AUTO = 60, + SES_CTRL_ENCLOSURE_POWER_OFF_DURATION_MANUAL = 63, + + SES_CTRL_ENCLOSURE_RQST_FAIL_BYTE = 2, + SES_CTRL_ENCLOSURE_RQST_FAIL_MASK = 0x02, + SES_CTRL_ENCLOSURE_RQST_FAIL_SHIFT = 1, + + SES_CTRL_ENCLOSURE_RQST_WARN_BYTE = 2, + SES_CTRL_ENCLOSURE_RQST_WARN_MASK = 0x01, + SES_CTRL_ENCLOSURE_RQST_WARN_SHIFT = 0 +}; + +#define GEN_SES_CTRL_ENCLOSURE_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_enclosure, SES_CTRL_ENCLOSURE, LCASE, UCASE) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(power_cycle_rqst, POWER_CYCLE_RQST) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(power_cycle_delay, POWER_CYCLE_DELAY) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(power_off_duration, POWER_OFF_DURATION) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_ENCLOSURE_ACCESSORS(rqst_warn, RQST_WARN) +#undef GEN_SES_CTRL_ENCLOSURE_ACCESSORS + +/*------------------- SCSI Port/Transceiver Control Element ------------------*/ +struct ses_ctrl_scsi_port_or_xcvr { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_scsi_port_or_xcvr_field_data { + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_IDENT_BYTE = 0, + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_IDENT_MASK = 0x80, + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_FAIL_BYTE = 0, + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_FAIL_MASK = 0x40, + SES_CTRL_SCSI_PORT_OR_XCVR_RQST_FAIL_SHIFT = 6, + + SES_CTRL_SCSI_PORT_OR_XCVR_DISABLE_BYTE = 2, + SES_CTRL_SCSI_PORT_OR_XCVR_DISABLE_MASK = 0x10, + SES_CTRL_SCSI_PORT_OR_XCVR_DISABLE_SHIFT = 4 +}; + +#define GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_scsi_port_or_xcvr, SES_CTRL_SCSI_PORT_OR_XCVR,\ + LCASE, UCASE) +GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(disable, DISABLE) +GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SCSI_PORT_OR_XCVR_ACCESSORS + +/*------------------------- Language Control Element -------------------------*/ +struct ses_ctrl_language { + struct ses_ctrl_common common; + uint8_t bytes[1]; + uint8_t language_code[2]; +}; + +enum ses_ctrl_language_field_data { + SES_CTRL_LANGUAGE_RQST_IDENT_BYTE = 0, + SES_CTRL_LANGUAGE_RQST_IDENT_MASK = 0x80, + SES_CTRL_LANGUAGE_RQST_IDENT_SHIFT = 7 +}; + +#define GEN_SES_CTRL_LANGUAGE_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_language, SES_CTRL_LANGUAGE, LCASE, UCASE) +GEN_SES_CTRL_LANGUAGE_ACCESSORS(rqst_ident, RQST_IDENT) +#undef GEN_SES_CTRL_LANGUAGE_ACCESSORS + +/*-------------------- Communication Port Control Element --------------------*/ +struct ses_ctrl_comm_port { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_comm_port_field_data { + SES_CTRL_COMM_PORT_RQST_IDENT_BYTE = 0, + SES_CTRL_COMM_PORT_RQST_IDENT_MASK = 0x80, + SES_CTRL_COMM_PORT_RQST_IDENT_SHIFT = 7, + + SES_CTRL_COMM_PORT_RQST_FAIL_BYTE = 0, + SES_CTRL_COMM_PORT_RQST_FAIL_MASK = 0x40, + SES_CTRL_COMM_PORT_RQST_FAIL_SHIFT = 6, + + SES_CTRL_COMM_PORT_DISABLE_BYTE = 2, + SES_CTRL_COMM_PORT_DISABLE_MASK = 0x01, + SES_CTRL_COMM_PORT_DISABLE_SHIFT = 0 +}; + +#define GEN_SES_CTRL_COMM_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_comm_port, SES_CTRL_COMM_PORT, LCASE, UCASE) +GEN_SES_CTRL_COMM_PORT_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_COMM_PORT_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_COMM_PORT_ACCESSORS(disable, DISABLE) +#undef GEN_SES_CTRL_COMM_PORT_ACCESSORS + +/*---------------------- Voltage Sensor Control Element ----------------------*/ +struct ses_ctrl_voltage_sensor { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_voltage_sensor_field_data { + SES_CTRL_VOLTAGE_SENSOR_RQST_IDENT_BYTE = 0, + SES_CTRL_VOLTAGE_SENSOR_RQST_IDENT_MASK = 0x80, + SES_CTRL_VOLTAGE_SENSOR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_VOLTAGE_SENSOR_RQST_FAIL_BYTE = 0, + SES_CTRL_VOLTAGE_SENSOR_RQST_FAIL_MASK = 0x40, + SES_CTRL_VOLTAGE_SENSOR_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_voltage_sensor, SES_CTRL_VOLTAGE_SENSOR, \ + LCASE, UCASE) +GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_VOLTAGE_SENSOR_ACCESSORS + +/*---------------------- Current Sensor Control Element ----------------------*/ +struct ses_ctrl_current_sensor { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_current_sensor_field_data { + SES_CTRL_CURRENT_SENSOR_RQST_IDENT_BYTE = 0, + SES_CTRL_CURRENT_SENSOR_RQST_IDENT_MASK = 0x80, + SES_CTRL_CURRENT_SENSOR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_CURRENT_SENSOR_RQST_FAIL_BYTE = 0, + SES_CTRL_CURRENT_SENSOR_RQST_FAIL_MASK = 0x40, + SES_CTRL_CURRENT_SENSOR_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_current_sensor, SES_CTRL_CURRENT_SENSOR, \ + LCASE, UCASE) +GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_CURRENT_SENSOR_ACCESSORS + +/*--------------------- SCSI Target Port Control Element ---------------------*/ +struct ses_ctrl_target_port { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_scsi_target_port_field_data { + SES_CTRL_TARGET_PORT_RQST_IDENT_BYTE = 0, + SES_CTRL_TARGET_PORT_RQST_IDENT_MASK = 0x80, + SES_CTRL_TARGET_PORT_RQST_IDENT_SHIFT = 7, + + SES_CTRL_TARGET_PORT_RQST_FAIL_BYTE = 0, + SES_CTRL_TARGET_PORT_RQST_FAIL_MASK = 0x40, + SES_CTRL_TARGET_PORT_RQST_FAIL_SHIFT = 6, + + SES_CTRL_TARGET_PORT_ENABLE_BYTE = 2, + SES_CTRL_TARGET_PORT_ENABLE_MASK = 0x01, + SES_CTRL_TARGET_PORT_ENABLE_SHIFT = 0 +}; + +#define GEN_SES_CTRL_TARGET_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_target_port, SES_CTRL_TARGET_PORT, LCASE, UCASE) +GEN_SES_CTRL_TARGET_PORT_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_TARGET_PORT_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_TARGET_PORT_ACCESSORS(enable, ENABLE) +#undef GEN_SES_CTRL_TARGET_PORT_ACCESSORS + +/*-------------------- SCSI Initiator Port Control Element -------------------*/ +struct ses_ctrl_initiator_port { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_initiator_port_field_data { + SES_CTRL_INITIATOR_PORT_RQST_IDENT_BYTE = 0, + SES_CTRL_INITIATOR_PORT_RQST_IDENT_MASK = 0x80, + SES_CTRL_INITIATOR_PORT_RQST_IDENT_SHIFT = 7, + + SES_CTRL_INITIATOR_PORT_RQST_FAIL_BYTE = 0, + SES_CTRL_INITIATOR_PORT_RQST_FAIL_MASK = 0x40, + SES_CTRL_INITIATOR_PORT_RQST_FAIL_SHIFT = 6, + + SES_CTRL_INITIATOR_PORT_ENABLE_BYTE = 2, + SES_CTRL_INITIATOR_PORT_ENABLE_MASK = 0x01, + SES_CTRL_INITIATOR_PORT_ENABLE_SHIFT = 0 +}; + +#define GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_initiator_port, SES_CTRL_INITIATOR_PORT, \ + LCASE, UCASE) +GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(rqst_fail, RQST_FAIL) +GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS(enable, ENABLE) +#undef GEN_SES_CTRL_INITIATOR_PORT_ACCESSORS + +/*-------------------- Simple Subenclosure Control Element -------------------*/ +struct ses_ctrl_simple_subenc { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_simple_subenc_field_data { + SES_CTRL_SIMPlE_SUBSES_RQST_IDENT_BYTE = 0, + SES_CTRL_SIMPlE_SUBSES_RQST_IDENT_MASK = 0x80, + SES_CTRL_SIMPlE_SUBSES_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SIMPlE_SUBSES_RQST_FAIL_BYTE = 0, + SES_CTRL_SIMPlE_SUBSES_RQST_FAIL_MASK = 0x40, + SES_CTRL_SIMPlE_SUBSES_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_simple_subenc, SES_CTRL_SIMPlE_SUBSES, \ + LCASE, UCASE) +GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SIMPlE_SUBSES_ACCESSORS + +/*----------------------- SAS Expander Control Element -----------------------*/ +struct ses_ctrl_sas_expander { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_sas_expander_field_data { + SES_CTRL_SAS_EXPANDER_RQST_IDENT_BYTE = 0, + SES_CTRL_SAS_EXPANDER_RQST_IDENT_MASK = 0x80, + SES_CTRL_SAS_EXPANDER_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SAS_EXPANDER_RQST_FAIL_BYTE = 0, + SES_CTRL_SAS_EXPANDER_RQST_FAIL_MASK = 0x40, + SES_CTRL_SAS_EXPANDER_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_sas_expander, SES_CTRL_SAS_EXPANDER, LCASE, UCASE) +GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SAS_EXPANDER_ACCESSORS + +/*----------------------- SAS Connector Control Element ----------------------*/ +struct ses_ctrl_sas_connector { + struct ses_ctrl_common common; + uint8_t bytes[3]; +}; + +enum ses_ctrl_sas_connector_field_data { + SES_CTRL_SAS_CONNECTOR_RQST_IDENT_BYTE = 0, + SES_CTRL_SAS_CONNECTOR_RQST_IDENT_MASK = 0x80, + SES_CTRL_SAS_CONNECTOR_RQST_IDENT_SHIFT = 7, + + SES_CTRL_SAS_CONNECTOR_RQST_FAIL_BYTE = 2, + SES_CTRL_SAS_CONNECTOR_RQST_FAIL_MASK = 0x40, + SES_CTRL_SAS_CONNECTOR_RQST_FAIL_SHIFT = 6 +}; + +#define GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS(LCASE, UCASE) \ + GEN_ACCESSORS(ses_ctrl_sas_connector, SES_CTRL_SAS_CONNECTOR, \ + LCASE, UCASE) +GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS(rqst_ident, RQST_IDENT) +GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS(rqst_fail, RQST_FAIL) +#undef GEN_SES_CTRL_SAS_CONNECTOR_ACCESSORS + +/*------------------------- Universal Control Element ------------------------*/ +union ses_ctrl_element { + struct ses_ctrl_common common; + struct ses_ctrl_dev_slot dev_slot; + struct ses_ctrl_array_dev_slot array_dev_slot; + struct ses_ctrl_power_supply power_supply; + struct ses_ctrl_cooling cooling; + struct ses_ctrl_temp_sensor temp_sensor; + struct ses_ctrl_door_lock door_lock; + struct ses_ctrl_audible_alarm audible_alarm; + struct ses_ctrl_ecc_electronics ecc_electronics; + struct ses_ctrl_scc_electronics scc_electronics; + struct ses_ctrl_nv_cache nv_cache; + struct ses_ctrl_invalid_op_reason invalid_op_reason; + struct ses_ctrl_ups ups; + struct ses_ctrl_display display; + struct ses_ctrl_key_pad_entry key_pad_entry; + struct ses_ctrl_scsi_port_or_xcvr scsi_port_or_xcvr; + struct ses_ctrl_language language; + struct ses_ctrl_comm_port comm_port; + struct ses_ctrl_voltage_sensor voltage_sensor; + struct ses_ctrl_current_sensor current_sensor; + struct ses_ctrl_target_port target_port; + struct ses_ctrl_initiator_port initiator_port; + struct ses_ctrl_simple_subenc simple_subenc; + struct ses_ctrl_sas_expander sas_expander; + struct ses_ctrl_sas_connector sas_connector; +}; + +/*--------------------- SCSI SES Control Diagnostic Page ---------------------*/ +struct ses_ctrl_page { + struct ses_page_hdr hdr; + union ses_ctrl_element elements[]; +}; + +enum ses_ctrl_page_field_data { + SES_CTRL_PAGE_INFO_MASK = 0x08, + SES_CTRL_PAGE_INFO_SHIFT = 3, + + SES_CTRL_PAGE_NON_CRIT_MASK = 0x04, + SES_CTRL_PAGE_NON_CRIT_SHIFT = 2, + + SES_CTRL_PAGE_CRIT_MASK = 0x02, + SES_CTRL_PAGE_CRIT_SHIFT = 1, + + SES_CTRL_PAGE_UNRECOV_MASK = 0x01, + SES_CTRL_PAGE_UNRECOV_SHIFT = 0 +}; + +#define GEN_SES_CTRL_PAGE_ACCESSORS(LCASE, UCASE) \ + GEN_HDR_ACCESSORS(ses_ctrl_page, SES_CTRL_PAGE, LCASE, UCASE) + +GEN_SES_CTRL_PAGE_ACCESSORS(info, INFO) +GEN_SES_CTRL_PAGE_ACCESSORS(non_crit, NON_CRIT) +GEN_SES_CTRL_PAGE_ACCESSORS(crit, CRIT) +GEN_SES_CTRL_PAGE_ACCESSORS(unrecov, UNRECOV) +#undef GEN_SES_CTRL_PAGE_ACCESSORS + +/*================= SCSI SES Status Diagnostic Page Structures ===============*/ +struct ses_status_common { + uint8_t bytes[1]; +}; + +enum ses_status_common_field_data { + SES_STATUS_COMMON_PRDFAIL_BYTE = 0, + SES_STATUS_COMMON_PRDFAIL_MASK = 0x40, + SES_STATUS_COMMON_PRDFAIL_SHIFT = 6, + + SES_STATUS_COMMON_DISABLED_BYTE = 0, + SES_STATUS_COMMON_DISABLED_MASK = 0x20, + SES_STATUS_COMMON_DISABLED_SHIFT = 5, + + SES_STATUS_COMMON_SWAP_BYTE = 0, + SES_STATUS_COMMON_SWAP_MASK = 0x10, + SES_STATUS_COMMON_SWAP_SHIFT = 4, + + SES_STATUS_COMMON_ELEMENT_STATUS_CODE_BYTE = 0, + SES_STATUS_COMMON_ELEMENT_STATUS_CODE_MASK = 0x0F, + SES_STATUS_COMMON_ELEMENT_STATUS_CODE_SHIFT = 0 +}; + +#define GEN_SES_STATUS_COMMON_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_common, SES_STATUS_COMMON, LCASE, UCASE) + +GEN_SES_STATUS_COMMON_ACCESSORS(prdfail, PRDFAIL) +GEN_SES_STATUS_COMMON_ACCESSORS(disabled, DISABLED) +GEN_SES_STATUS_COMMON_ACCESSORS(swap, SWAP) +GEN_SES_STATUS_COMMON_ACCESSORS(element_status_code, ELEMENT_STATUS_CODE) +#undef GEN_SES_STATUS_COMMON_ACCESSORS + +/*------------------------- Device Slot Status Element -----------------------*/ +struct ses_status_dev_slot { + struct ses_status_common common; + uint8_t slot_address; + uint8_t bytes[2]; +}; + +enum ses_status_dev_slot_field_data { + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_A_BYTE = 0, + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_A_MASK = 0x80, + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_A_SHIFT = 7, + + SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_BYTE = 0, + SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_MASK = 0x40, + SES_STATUS_DEV_SLOT_DO_NOT_REMOVE_SHIFT = 6, + + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_A_BYTE = 0, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_A_MASK = 0x20, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_A_SHIFT = 5, + + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_B_BYTE = 0, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_B_MASK = 0x10, + SES_STATUS_DEV_SLOT_ENCLOSURE_BYPED_B_SHIFT = 4, + + SES_STATUS_DEV_SLOT_INSERT_READY_BYTE = 0, + SES_STATUS_DEV_SLOT_INSERT_READY_MASK = 0x08, + SES_STATUS_DEV_SLOT_INSERT_READY_SHIFT = 3, + + SES_STATUS_DEV_SLOT_REMOVE_BYTE = 0, + SES_STATUS_DEV_SLOT_REMOVE_MASK = 0x04, + SES_STATUS_DEV_SLOT_REMOVE_SHIFT = 2, + + SES_STATUS_DEV_SLOT_IDENT_BYTE = 0, + SES_STATUS_DEV_SLOT_IDENT_MASK = 0x02, + SES_STATUS_DEV_SLOT_IDENT_SHIFT = 1, + + SES_STATUS_DEV_SLOT_REPORT_BYTE = 0, + SES_STATUS_DEV_SLOT_REPORT_MASK = 0x01, + SES_STATUS_DEV_SLOT_REPORT_SHIFT = 0, + + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_B_BYTE = 1, + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_B_MASK = 0x80, + SES_STATUS_DEV_SLOT_APP_CLIENT_BYPED_B_SHIFT = 7, + + SES_STATUS_DEV_SLOT_FAULT_SENSED_BYTE = 1, + SES_STATUS_DEV_SLOT_FAULT_SENSED_MASK = 0x40, + SES_STATUS_DEV_SLOT_FAULT_SENSED_SHIFT = 6, + + SES_STATUS_DEV_SLOT_FAULT_REQUESTED_BYTE = 1, + SES_STATUS_DEV_SLOT_FAULT_REQUESTED_MASK = 0x20, + SES_STATUS_DEV_SLOT_FAULT_REQUESTED_SHIFT = 5, + + SES_STATUS_DEV_SLOT_DEVICE_OFF_BYTE = 1, + SES_STATUS_DEV_SLOT_DEVICE_OFF_MASK = 0x10, + SES_STATUS_DEV_SLOT_DEVICE_OFF_SHIFT = 4, + + SES_STATUS_DEV_SLOT_BYPED_A_BYTE = 1, + SES_STATUS_DEV_SLOT_BYPED_A_MASK = 0x08, + SES_STATUS_DEV_SLOT_BYPED_A_SHIFT = 3, + + SES_STATUS_DEV_SLOT_BYPED_B_BYTE = 1, + SES_STATUS_DEV_SLOT_BYPED_B_MASK = 0x04, + SES_STATUS_DEV_SLOT_BYPED_B_SHIFT = 2, + + SES_STATUS_DEV_SLOT_DEVICE_BYPED_A_BYTE = 1, + SES_STATUS_DEV_SLOT_DEVICE_BYPED_A_MASK = 0x02, + SES_STATUS_DEV_SLOT_DEVICE_BYPED_A_SHIFT = 1, + + SES_STATUS_DEV_SLOT_DEVICE_BYPED_B_BYTE = 1, + SES_STATUS_DEV_SLOT_DEVICE_BYPED_B_MASK = 0x01, + SES_STATUS_DEV_SLOT_DEVICE_BYPED_B_SHIFT = 0 +}; +#define GEN_SES_STATUS_DEV_SLOT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_dev_slot, SES_STATUS_DEV_SLOT, LCASE, UCASE) + +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(app_client_byped_a, APP_CLIENT_BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(do_not_remove, DO_NOT_REMOVE) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_a, ENCLOSURE_BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(enclosure_byped_b, ENCLOSURE_BYPED_B) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(insert_ready, INSERT_READY) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(remove, REMOVE) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(report, REPORT) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(app_client_byped_b, APP_CLIENT_BYPED_B) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(fault_sensed, FAULT_SENSED) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(fault_requested, FAULT_REQUESTED) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(device_off, DEVICE_OFF) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(byped_a, BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(byped_b, BYPED_B) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(device_byped_a, DEVICE_BYPED_A) +GEN_SES_STATUS_DEV_SLOT_ACCESSORS(device_byped_b, DEVICE_BYPED_B) +#undef GEN_SES_STATUS_DEV_SLOT_ACCESSORS + +/*---------------------- Array Device Slot Status Element --------------------*/ +struct ses_status_array_dev_slot { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_array_dev_slot_field_data { + SES_STATUS_ARRAY_DEV_SLOT_OK_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_OK_MASK = 0x80, + SES_STATUS_ARRAY_DEV_SLOT_OK_SHIFT = 7, + + SES_STATUS_ARRAY_DEV_SLOT_RSVD_DEVICE_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_RSVD_DEVICE_MASK = 0x40, + SES_STATUS_ARRAY_DEV_SLOT_RSVD_DEVICE_SHIFT = 6, + + SES_STATUS_ARRAY_DEV_SLOT_HOT_SPARE_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_HOT_SPARE_MASK = 0x20, + SES_STATUS_ARRAY_DEV_SLOT_HOT_SPARE_SHIFT = 5, + + SES_STATUS_ARRAY_DEV_SLOT_CONS_CHECK_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_CONS_CHECK_MASK = 0x10, + SES_STATUS_ARRAY_DEV_SLOT_CONS_CHECK_SHIFT = 4, + + SES_STATUS_ARRAY_DEV_SLOT_IN_CRIT_ARRAY_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_IN_CRIT_ARRAY_MASK = 0x08, + SES_STATUS_ARRAY_DEV_SLOT_IN_CRIT_ARRAY_SHIFT = 3, + + SES_STATUS_ARRAY_DEV_SLOT_IN_FAILED_ARRAY_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_IN_FAILED_ARRAY_MASK = 0x04, + SES_STATUS_ARRAY_DEV_SLOT_IN_FAILED_ARRAY_SHIFT = 2, + + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_MASK = 0x02, + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_SHIFT = 1, + + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_ABORT_BYTE = 0, + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_ABORT_MASK = 0x01, + SES_STATUS_ARRAY_DEV_SLOT_REBUILD_REMAP_ABORT_SHIFT = 0 + + /* + * The remaining fields are identical to the device + * slot element type. Access them through the device slot + * element type and its accessors. + */ +}; +#define GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_array_dev_slot, SES_STATUS_ARRAY_DEV_SLOT, \ + LCASE, UCASE) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(ok, OK) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(rsvd_device, RSVD_DEVICE) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(hot_spare, HOT_SPARE) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(cons_check, CONS_CHECK) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(in_crit_array, IN_CRIT_ARRAY) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(in_failed_array, IN_FAILED_ARRAY) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(rebuild_remap, REBUILD_REMAP) +GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS(rebuild_remap_abort, + REBUILD_REMAP_ABORT) +#undef GEN_SES_STATUS_ARRAY_DEV_SLOT_ACCESSORS + +/*----------------------- Power Supply Status Element ------------------------*/ +struct ses_status_power_supply { + struct ses_status_common common; + uint8_t bytes[3]; +}; +enum ses_status_power_supply_field_data { + SES_STATUS_POWER_SUPPLY_IDENT_BYTE = 0, + SES_STATUS_POWER_SUPPLY_IDENT_MASK = 0x80, + SES_STATUS_POWER_SUPPLY_IDENT_SHIFT = 7, + + SES_STATUS_POWER_SUPPLY_DC_OVER_VOLTAGE_BYTE = 1, + SES_STATUS_POWER_SUPPLY_DC_OVER_VOLTAGE_MASK = 0x08, + SES_STATUS_POWER_SUPPLY_DC_OVER_VOLTAGE_SHIFT = 3, + + SES_STATUS_POWER_SUPPLY_DC_UNDER_VOLTAGE_BYTE = 1, + SES_STATUS_POWER_SUPPLY_DC_UNDER_VOLTAGE_MASK = 0x04, + SES_STATUS_POWER_SUPPLY_DC_UNDER_VOLTAGE_SHIFT = 2, + + SES_STATUS_POWER_SUPPLY_DC_OVER_CURRENT_BYTE = 1, + SES_STATUS_POWER_SUPPLY_DC_OVER_CURRENT_MASK = 0x02, + SES_STATUS_POWER_SUPPLY_DC_OVER_CURRENT_SHIFT = 1, + + SES_STATUS_POWER_SUPPLY_HOT_SWAP_BYTE = 2, + SES_STATUS_POWER_SUPPLY_HOT_SWAP_MASK = 0x80, + SES_STATUS_POWER_SUPPLY_HOT_SWAP_SHIFT = 7, + + SES_STATUS_POWER_SUPPLY_FAIL_BYTE = 2, + SES_STATUS_POWER_SUPPLY_FAIL_MASK = 0x40, + SES_STATUS_POWER_SUPPLY_FAIL_SHIFT = 6, + + SES_STATUS_POWER_SUPPLY_REQUESTED_ON_BYTE = 2, + SES_STATUS_POWER_SUPPLY_REQUESTED_ON_MASK = 0x20, + SES_STATUS_POWER_SUPPLY_REQUESTED_ON_SHIFT = 5, + + SES_STATUS_POWER_SUPPLY_OFF_BYTE = 2, + SES_STATUS_POWER_SUPPLY_OFF_MASK = 0x10, + SES_STATUS_POWER_SUPPLY_OFF_SHIFT = 4, + + SES_STATUS_POWER_SUPPLY_OVERTMP_FAIL_BYTE = 2, + SES_STATUS_POWER_SUPPLY_OVERTMP_FAIL_MASK = 0x08, + SES_STATUS_POWER_SUPPLY_OVERTMP_FAIL_SHIFT = 3, + + SES_STATUS_POWER_SUPPLY_TEMP_WARN_BYTE = 2, + SES_STATUS_POWER_SUPPLY_TEMP_WARN_MASK = 0x04, + SES_STATUS_POWER_SUPPLY_TEMP_WARN_SHIFT = 2, + + SES_STATUS_POWER_SUPPLY_AC_FAIL_BYTE = 2, + SES_STATUS_POWER_SUPPLY_AC_FAIL_MASK = 0x02, + SES_STATUS_POWER_SUPPLY_AC_FAIL_SHIFT = 1, + + SES_STATUS_POWER_SUPPLY_DC_FAIL_BYTE = 2, + SES_STATUS_POWER_SUPPLY_DC_FAIL_MASK = 0x01, + SES_STATUS_POWER_SUPPLY_DC_FAIL_SHIFT = 0 +}; + +#define GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_power_supply, SES_STATUS_POWER_SUPPLY, LCASE, UCASE) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_over_voltage, DC_OVER_VOLTAGE) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_under_voltage, DC_UNDER_VOLTAGE) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_over_current, DC_OVER_CURRENT) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(hot_swap, HOT_SWAP) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(requested_on, REQUESTED_ON) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(off, OFF) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(overtmp_fail, OVERTMP_FAIL) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(temp_warn, TEMP_WARN) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(ac_fail, AC_FAIL) +GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS(dc_fail, DC_FAIL) +#undef GEN_SES_STATUS_POWER_SUPPLY_ACCESSORS + +/*-------------------------- Cooling Status Element --------------------------*/ +struct ses_status_cooling { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_cooling_field_data { + SES_STATUS_COOLING_IDENT_BYTE = 0, + SES_STATUS_COOLING_IDENT_MASK = 0x80, + SES_STATUS_COOLING_IDENT_SHIFT = 7, + + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_MSB_BYTE = 0, + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_MSB_MASK = 0x07, + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_MSB_SHIFT = 0, + + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_LSB_BYTE = 1, + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_LSB_MASK = 0xFF, + SES_STATUS_COOLING_ACTUAL_FAN_SPEED_LSB_SHIFT = 0, + + SES_STATUS_COOLING_HOT_SWAP_BYTE = 2, + SES_STATUS_COOLING_HOT_SWAP_MASK = 0x40, + SES_STATUS_COOLING_HOT_SWAP_SHIFT = 6, + + SES_STATUS_COOLING_FAIL_BYTE = 2, + SES_STATUS_COOLING_FAIL_MASK = 0x40, + SES_STATUS_COOLING_FAIL_SHIFT = 6, + + SES_STATUS_COOLING_REQUESTED_ON_BYTE = 2, + SES_STATUS_COOLING_REQUESTED_ON_MASK = 0x20, + SES_STATUS_COOLING_REQUESTED_ON_SHIFT = 5, + + SES_STATUS_COOLING_OFF_BYTE = 2, + SES_STATUS_COOLING_OFF_MASK = 0x20, + SES_STATUS_COOLING_OFF_SHIFT = 5, + + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_BYTE = 2, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_MASK = 0x07, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_SHIFT = 2, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_STOPPED = 0x00, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_LOWEST = 0x01, + SES_STATUS_COOLING_ACTUAL_SPEED_CODE_HIGHEST = 0x07 +}; + +#define GEN_SES_STATUS_COOLING_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_cooling, SES_STATUS_COOLING, LCASE, UCASE) +GEN_SES_STATUS_COOLING_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_COOLING_ACCESSORS(actual_fan_speed_msb, ACTUAL_FAN_SPEED_MSB) +GEN_SES_STATUS_COOLING_ACCESSORS(actual_fan_speed_lsb, ACTUAL_FAN_SPEED_LSB) +GEN_SES_STATUS_COOLING_ACCESSORS(hot_swap, HOT_SWAP) +GEN_SES_STATUS_COOLING_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_COOLING_ACCESSORS(requested_on, REQUESTED_ON) +GEN_SES_STATUS_COOLING_ACCESSORS(off, OFF) +GEN_SES_STATUS_COOLING_ACCESSORS(actual_speed_code, ACTUAL_SPEED_CODE) +#undef GEN_SES_STATUS_COOLING_ACCESSORS + +static inline int +ses_status_cooling_get_actual_fan_speed(struct ses_status_cooling *elem) +{ + return (ses_status_cooling_get_actual_fan_speed_msb(elem) << 8 + | ses_status_cooling_get_actual_fan_speed_lsb(elem)); +} + +/*-------------------- Temperature Sensor Status Element ---------------------*/ +struct ses_status_temp_sensor { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_temp_sensor_field_data { + SES_STATUS_TEMP_SENSOR_IDENT_BYTE = 0, + SES_STATUS_TEMP_SENSOR_IDENT_MASK = 0x80, + SES_STATUS_TEMP_SENSOR_IDENT_SHIFT = 7, + + SES_STATUS_TEMP_SENSOR_FAIL_BYTE = 0, + SES_STATUS_TEMP_SENSOR_FAIL_MASK = 0x40, + SES_STATUS_TEMP_SENSOR_FAIL_SHIFT = 6, + + SES_STATUS_TEMP_SENSOR_TEMPERATURE_BYTE = 1, + SES_STATUS_TEMP_SENSOR_TEMPERATURE_MASK = 0xFF, + SES_STATUS_TEMP_SENSOR_TEMPERATURE_SHIFT = 0, + + SES_STATUS_TEMP_SENSOR_OT_FAILURE_BYTE = 2, + SES_STATUS_TEMP_SENSOR_OT_FAILURE_MASK = 0x08, + SES_STATUS_TEMP_SENSOR_OT_FAILURE_SHIFT = 3, + + SES_STATUS_TEMP_SENSOR_OT_WARNING_BYTE = 2, + SES_STATUS_TEMP_SENSOR_OT_WARNING_MASK = 0x04, + SES_STATUS_TEMP_SENSOR_OT_WARNING_SHIFT = 2, + + SES_STATUS_TEMP_SENSOR_UT_FAILURE_BYTE = 2, + SES_STATUS_TEMP_SENSOR_UT_FAILURE_MASK = 0x02, + SES_STATUS_TEMP_SENSOR_UT_FAILURE_SHIFT = 1, + + SES_STATUS_TEMP_SENSOR_UT_WARNING_BYTE = 2, + SES_STATUS_TEMP_SENSOR_UT_WARNING_MASK = 0x01, + SES_STATUS_TEMP_SENSOR_UT_WARNING_SHIFT = 0 +}; + +#define GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_temp_sensor, SES_STATUS_TEMP_SENSOR, LCASE, UCASE) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(temperature, TEMPERATURE) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ot_failure, OT_FAILURE) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ot_warning, OT_WARNING) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ut_failure, UT_FAILURE) +GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS(ut_warning, UT_WARNING) +#undef GEN_SES_STATUS_TEMP_SENSOR_ACCESSORS + +/*------------------------- Door Lock Status Element -------------------------*/ +struct ses_status_door_lock { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_door_lock_field_data { + SES_STATUS_DOOR_LOCK_IDENT_BYTE = 0, + SES_STATUS_DOOR_LOCK_IDENT_MASK = 0x80, + SES_STATUS_DOOR_LOCK_IDENT_SHIFT = 7, + + SES_STATUS_DOOR_LOCK_FAIL_BYTE = 0, + SES_STATUS_DOOR_LOCK_FAIL_MASK = 0x40, + SES_STATUS_DOOR_LOCK_FAIL_SHIFT = 6, + + SES_STATUS_DOOR_LOCK_UNLOCKED_BYTE = 2, + SES_STATUS_DOOR_LOCK_UNLOCKED_MASK = 0x01, + SES_STATUS_DOOR_LOCK_UNLOCKED_SHIFT = 0 +}; + +#define GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_door_lock, SES_STATUS_DOOR_LOCK, LCASE, UCASE) +GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_DOOR_LOCK_ACCESSORS(unlocked, UNLOCKED) +#undef GEN_SES_STATUS_DOOR_LOCK_ACCESSORS + +/*----------------------- Audible Alarm Status Element -----------------------*/ +struct ses_status_audible_alarm { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_audible_alarm_field_data { + SES_STATUS_AUDIBLE_ALARM_IDENT_BYTE = 0, + SES_STATUS_AUDIBLE_ALARM_IDENT_MASK = 0x80, + SES_STATUS_AUDIBLE_ALARM_IDENT_SHIFT = 7, + + SES_STATUS_AUDIBLE_ALARM_FAIL_BYTE = 0, + SES_STATUS_AUDIBLE_ALARM_FAIL_MASK = 0x40, + SES_STATUS_AUDIBLE_ALARM_FAIL_SHIFT = 6, + + SES_STATUS_AUDIBLE_ALARM_RQST_MUTE_BYTE = 2, + SES_STATUS_AUDIBLE_ALARM_RQST_MUTE_MASK = 0x80, + SES_STATUS_AUDIBLE_ALARM_RQST_MUTE_SHIFT = 7, + + SES_STATUS_AUDIBLE_ALARM_MUTED_BYTE = 2, + SES_STATUS_AUDIBLE_ALARM_MUTED_MASK = 0x40, + SES_STATUS_AUDIBLE_ALARM_MUTED_SHIFT = 6, + + SES_STATUS_AUDIBLE_ALARM_REMIND_BYTE = 2, + SES_STATUS_AUDIBLE_ALARM_REMIND_MASK = 0x10, + SES_STATUS_AUDIBLE_ALARM_REMIND_SHIFT = 4, + + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_BYTE = 2, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_MASK = 0x0F, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_SHIFT = 0, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_INFO = 0x08, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_NON_CRIT = 0x04, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_CRIT = 0x02, + SES_STATUS_AUDIBLE_ALARM_TONE_INDICATOR_UNRECOV = 0x01 +}; + +#define GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_audible_alarm, SES_STATUS_AUDIBLE_ALARM, LCASE, UCASE) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(rqst_mute, RQST_MUTE) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(muted, MUTED) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(remind, REMIND) +GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS(tone_indicator, TONE_INDICATOR) +#undef GEN_SES_STATUS_AUDIBLE_ALARM_ACCESSORS + +/*---------- Enclosure Services Statusler Electronics Status Element ---------*/ +struct ses_status_ecc_electronics { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_ecc_electronics_field_data { + SES_STATUS_ECC_ELECTRONICS_IDENT_BYTE = 0, + SES_STATUS_ECC_ELECTRONICS_IDENT_MASK = 0x80, + SES_STATUS_ECC_ELECTRONICS_IDENT_SHIFT = 7, + + SES_STATUS_ECC_ELECTRONICS_FAIL_BYTE = 0, + SES_STATUS_ECC_ELECTRONICS_FAIL_MASK = 0x40, + SES_STATUS_ECC_ELECTRONICS_FAIL_SHIFT = 6, + + SES_STATUS_ECC_ELECTRONICS_REPORT_BYTE = 1, + SES_STATUS_ECC_ELECTRONICS_REPORT_MASK = 0x01, + SES_STATUS_ECC_ELECTRONICS_REPORT_SHIFT = 0, + + SES_STATUS_ECC_ELECTRONICS_HOT_SWAP_BYTE = 2, + SES_STATUS_ECC_ELECTRONICS_HOT_SWAP_MASK = 0x80, + SES_STATUS_ECC_ELECTRONICS_HOT_SWAP_SHIFT = 7 +}; + +#define GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_ecc_electronics, SES_STATUS_ECC_ELECTRONICS, \ + LCASE, UCASE) +GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(report, REPORT) +GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS(hot_swap, HOT_SWAP) +#undef GEN_SES_STATUS_ECC_ELECTRONICS_ACCESSORS + +/*------------ SCSI Services Statusler Electronics Status Element ------------*/ +struct ses_status_scc_electronics { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_scc_electronics_field_data { + SES_STATUS_SCC_ELECTRONICS_IDENT_BYTE = 0, + SES_STATUS_SCC_ELECTRONICS_IDENT_MASK = 0x80, + SES_STATUS_SCC_ELECTRONICS_IDENT_SHIFT = 7, + + SES_STATUS_SCC_ELECTRONICS_FAIL_BYTE = 0, + SES_STATUS_SCC_ELECTRONICS_FAIL_MASK = 0x40, + SES_STATUS_SCC_ELECTRONICS_FAIL_SHIFT = 6, + + SES_STATUS_SCC_ELECTRONICS_REPORT_BYTE = 1, + SES_STATUS_SCC_ELECTRONICS_REPORT_MASK = 0x01, + SES_STATUS_SCC_ELECTRONICS_REPORT_SHIFT = 0 +}; + +#define GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_scc_electronics, SES_STATUS_SCC_ELECTRONICS, \ + LCASE, UCASE) +GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS(report, REPORT) +#undef GEN_SES_STATUS_SCC_ELECTRONICS_ACCESSORS + +/*--------------------- Nonvolatile Cache Status Element ---------------------*/ +struct ses_status_nv_cache { + struct ses_status_common common; + uint8_t bytes[1]; + uint8_t cache_size[2]; +}; + +enum ses_status_nv_cache_field_data { + SES_STATUS_NV_CACHE_IDENT_BYTE = 0, + SES_STATUS_NV_CACHE_IDENT_MASK = 0x80, + SES_STATUS_NV_CACHE_IDENT_SHIFT = 7, + + SES_STATUS_NV_CACHE_FAIL_BYTE = 0, + SES_STATUS_NV_CACHE_FAIL_MASK = 0x40, + SES_STATUS_NV_CACHE_FAIL_SHIFT = 6, + + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_BYTE = 0, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_MASK = 0x03, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_SHIFT = 0, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_BYTES = 0x0, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_KBYTES = 0x1, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_MBYTES = 0x2, + SES_STATUS_NV_CACHE_SIZE_MULTIPLIER_GBYTES = 0x3 +}; + +#define GEN_SES_STATUS_NV_CACHE_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_nv_cache, SES_STATUS_NV_CACHE, LCASE, UCASE) +GEN_SES_STATUS_NV_CACHE_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_NV_CACHE_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_NV_CACHE_ACCESSORS(size_multiplier, SIZE_MULTIPLIER) +#undef GEN_SES_STATUS_NV_CACHE_ACCESSORS + +static inline uintmax_t +ses_status_nv_cache_get_cache_size(struct ses_status_nv_cache *elem) +{ + uintmax_t cache_size; + int multiplier; + + /* Multiplier is in units of 2^10 */ + cache_size = scsi_2btoul(elem->cache_size); + multiplier = 10 * ses_status_nv_cache_get_size_multiplier(elem); + return (cache_size << multiplier); +} + +/*----------------- Invalid Operation Reason Status Element ------------------*/ +struct ses_status_invalid_op_reason { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_invalid_op_field_data { + SES_STATUS_INVALID_OP_REASON_TYPE_BYTE = 0, + SES_STATUS_INVALID_OP_REASON_TYPE_MASK = 0xC0, + SES_STATUS_INVALID_OP_REASON_TYPE_SHIFT = 6, + SES_STATUS_INVALID_OP_REASON_TYPE_PC_ERROR = 0x00, + SES_STATUS_INVALID_OP_REASON_TYPE_PF_ERROR = 0x01, + SES_STATUS_INVALID_OP_REASON_TYPE_VS_ERROR = 0x03, + + SES_STATUS_INVALID_OP_REASON_PC_ERROR_PC_NOT_SUPPORTED_BYTE = 0, + SES_STATUS_INVALID_OP_REASON_PC_ERROR_PC_NOT_SUPPORTED_MASK = 0x01, + SES_STATUS_INVALID_OP_REASON_PC_ERROR_PC_NOT_SUPPORTED_SHIFT = 0, + + SES_STATUS_INVALID_OP_REASON_PF_ERROR_BIT_NUMBER_BYTE = 0, + SES_STATUS_INVALID_OP_REASON_PF_ERROR_BIT_NUMBER_MASK = 0x03, + SES_STATUS_INVALID_OP_REASON_PF_ERROR_BIT_NUMBER_SHIFT = 0 +}; + +#define GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_invalid_op_reason, SES_STATUS_INVALID_OP_REASON, \ + LCASE, UCASE) +GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(type, TYPE) +GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(pc_error_pc_not_supported, + PC_ERROR_PC_NOT_SUPPORTED) +GEN_SES_STATUS_INVALID_OP_REASON_ACCESSORS(pf_error_bit_number, + PF_ERROR_BIT_NUMBER) +#undef GEN_SES_STATUS_INVALID_OP_ACCESSORS + +/*--------------- Uninterruptible Power Supply Status Element ----------------*/ +struct ses_status_ups { + struct ses_status_common common; + /* Minutes of remaining capacity. */ + uint8_t battery_status; + uint8_t bytes[2]; +}; + +enum ses_status_ups_field_data { + SES_STATUS_UPS_AC_LO_BYTE = 0, + SES_STATUS_UPS_AC_LO_MASK = 0x80, + SES_STATUS_UPS_AC_LO_SHIFT = 7, + + SES_STATUS_UPS_AC_HI_BYTE = 0, + SES_STATUS_UPS_AC_HI_MASK = 0x40, + SES_STATUS_UPS_AC_HI_SHIFT = 6, + + SES_STATUS_UPS_AC_QUAL_BYTE = 0, + SES_STATUS_UPS_AC_QUAL_MASK = 0x20, + SES_STATUS_UPS_AC_QUAL_SHIFT = 5, + + SES_STATUS_UPS_AC_FAIL_BYTE = 0, + SES_STATUS_UPS_AC_FAIL_MASK = 0x10, + SES_STATUS_UPS_AC_FAIL_SHIFT = 4, + + SES_STATUS_UPS_DC_FAIL_BYTE = 0, + SES_STATUS_UPS_DC_FAIL_MASK = 0x08, + SES_STATUS_UPS_DC_FAIL_SHIFT = 3, + + SES_STATUS_UPS_UPS_FAIL_BYTE = 0, + SES_STATUS_UPS_UPS_FAIL_MASK = 0x04, + SES_STATUS_UPS_UPS_FAIL_SHIFT = 2, + + SES_STATUS_UPS_WARN_BYTE = 0, + SES_STATUS_UPS_WARN_MASK = 0x02, + SES_STATUS_UPS_WARN_SHIFT = 1, + + SES_STATUS_UPS_INTF_FAIL_BYTE = 0, + SES_STATUS_UPS_INTF_FAIL_MASK = 0x01, + SES_STATUS_UPS_INTF_FAIL_SHIFT = 0, + + SES_STATUS_UPS_IDENT_BYTE = 0, + SES_STATUS_UPS_IDENT_MASK = 0x80, + SES_STATUS_UPS_IDENT_SHIFT = 7, + + SES_STATUS_UPS_FAIL_BYTE = 1, + SES_STATUS_UPS_FAIL_MASK = 0x40, + SES_STATUS_UPS_FAIL_SHIFT = 6, + + SES_STATUS_UPS_BATT_FAIL_BYTE = 1, + SES_STATUS_UPS_BATT_FAIL_MASK = 0x02, + SES_STATUS_UPS_BATT_FAIL_SHIFT = 1, + + SES_STATUS_UPS_BPF_BYTE = 1, + SES_STATUS_UPS_BPF_MASK = 0x01, + SES_STATUS_UPS_BPF_SHIFT = 0 +}; + +#define GEN_SES_STATUS_UPS_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_ups, SES_STATUS_UPS, LCASE, UCASE) +GEN_SES_STATUS_UPS_ACCESSORS(ac_lo, AC_LO) +GEN_SES_STATUS_UPS_ACCESSORS(ac_hi, AC_HI) +GEN_SES_STATUS_UPS_ACCESSORS(ac_qual, AC_QUAL) +GEN_SES_STATUS_UPS_ACCESSORS(ac_fail, AC_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(dc_fail, DC_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(ups_fail, UPS_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(warn, WARN) +GEN_SES_STATUS_UPS_ACCESSORS(intf_fail, INTF_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_UPS_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(batt_fail, BATT_FAIL) +GEN_SES_STATUS_UPS_ACCESSORS(bpf, BPF) +#undef GEN_SES_STATUS_UPS_ACCESSORS + +/*-------------------------- Display Status Element --------------------------*/ +struct ses_status_display { + struct ses_status_common common; + uint8_t bytes[1]; + uint8_t display_character[2]; +}; + +enum ses_status_display_field_data { + SES_STATUS_DISPLAY_IDENT_BYTE = 0, + SES_STATUS_DISPLAY_IDENT_MASK = 0x80, + SES_STATUS_DISPLAY_IDENT_SHIFT = 7, + + SES_STATUS_DISPLAY_FAIL_BYTE = 0, + SES_STATUS_DISPLAY_FAIL_MASK = 0x40, + SES_STATUS_DISPLAY_FAIL_SHIFT = 6, + + SES_STATUS_DISPLAY_DISPLAY_MODE_BYTE = 0, + SES_STATUS_DISPLAY_DISPLAY_MODE_MASK = 0x03, + SES_STATUS_DISPLAY_DISPLAY_MODE_SHIFT = 6, + SES_STATUS_DISPLAY_DISPLAY_MODE_DC_FIELD_UNSUPP = 0x0, + SES_STATUS_DISPLAY_DISPLAY_MODE_DC_FIELD_SUPP = 0x1, + SES_STATUS_DISPLAY_DISPLAY_MODE_DC_FIELD = 0x2 +}; + +#define GEN_SES_STATUS_DISPLAY_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_display, SES_STATUS_DISPLAY, LCASE, UCASE) +GEN_SES_STATUS_DISPLAY_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_DISPLAY_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_DISPLAY_ACCESSORS(display_mode, DISPLAY_MODE) +#undef GEN_SES_STATUS_DISPLAY_ACCESSORS + +/*----------------------- Key Pad Entry Status Element -----------------------*/ +struct ses_status_key_pad_entry { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_key_pad_entry_field_data { + SES_STATUS_KEY_PAD_ENTRY_IDENT_BYTE = 0, + SES_STATUS_KEY_PAD_ENTRY_IDENT_MASK = 0x80, + SES_STATUS_KEY_PAD_ENTRY_IDENT_SHIFT = 7, + + SES_STATUS_KEY_PAD_ENTRY_FAIL_BYTE = 0, + SES_STATUS_KEY_PAD_ENTRY_FAIL_MASK = 0x40, + SES_STATUS_KEY_PAD_ENTRY_FAIL_SHIFT = 6 +}; + +#define GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_key_pad_entry, SES_STATUS_KEY_PAD_ENTRY, LCASE, UCASE) +GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS(fail, FAIL) +#undef GEN_SES_STATUS_KEY_PAD_ENTRY_ACCESSORS + +/*------------------------- Enclosure Status Element -------------------------*/ +struct ses_status_enclosure { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_enclosure_field_data { + SES_STATUS_ENCLOSURE_IDENT_BYTE = 0, + SES_STATUS_ENCLOSURE_IDENT_MASK = 0x80, + SES_STATUS_ENCLOSURE_IDENT_SHIFT = 7, + + SES_STATUS_ENCLOSURE_TIME_UNTIL_POWER_CYCLE_BYTE = 1, + SES_STATUS_ENCLOSURE_TIME_UNTIL_POWER_CYCLE_MASK = 0xFC, + SES_STATUS_ENCLOSURE_TIME_UNTIL_POWER_CYCLE_SHIFT = 2, + + SES_STATUS_ENCLOSURE_FAIL_BYTE = 1, + SES_STATUS_ENCLOSURE_FAIL_MASK = 0x02, + SES_STATUS_ENCLOSURE_FAIL_SHIFT = 1, + + SES_STATUS_ENCLOSURE_WARN_BYTE = 1, + SES_STATUS_ENCLOSURE_WARN_MASK = 0x01, + SES_STATUS_ENCLOSURE_WARN_SHIFT = 0, + + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_BYTE = 2, + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_MASK = 0xFC, + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_SHIFT = 2, + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_MAX_AUTO = 60, + SES_STATUS_ENCLOSURE_REQUESTED_POWER_OFF_DURATION_MANUAL = 63, + + SES_STATUS_ENCLOSURE_REQUESTED_FAIL_BYTE = 2, + SES_STATUS_ENCLOSURE_REQUESTED_FAIL_MASK = 0x02, + SES_STATUS_ENCLOSURE_REQUESTED_FAIL_SHIFT = 1, + + SES_STATUS_ENCLOSURE_REQUESTED_WARN_BYTE = 2, + SES_STATUS_ENCLOSURE_REQUESTED_WARN_MASK = 0x01, + SES_STATUS_ENCLOSURE_REQUESTED_WARN_SHIFT = 0 +}; + +#define GEN_SES_STATUS_ENCLOSURE_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_enclosure, SES_STATUS_ENCLOSURE, LCASE, UCASE) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(time_until_power_cycle, + TIME_UNTIL_POWER_CYCLE) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(warn, WARN) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(requested_power_off_duration, + REQUESTED_POWER_OFF_DURATION) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(requested_fail, REQUESTED_FAIL) +GEN_SES_STATUS_ENCLOSURE_ACCESSORS(requested_warn, REQUESTED_WARN) +#undef GEN_SES_STATUS_ENCLOSURE_ACCESSORS + +/*------------------- SCSI Port/Transceiver Status Element -------------------*/ +struct ses_status_scsi_port_or_xcvr { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_scsi_port_or_xcvr_field_data { + SES_STATUS_SCSI_PORT_OR_XCVR_IDENT_BYTE = 0, + SES_STATUS_SCSI_PORT_OR_XCVR_IDENT_MASK = 0x80, + SES_STATUS_SCSI_PORT_OR_XCVR_IDENT_SHIFT = 7, + + SES_STATUS_SCSI_PORT_OR_XCVR_FAIL_BYTE = 0, + SES_STATUS_SCSI_PORT_OR_XCVR_FAIL_MASK = 0x40, + SES_STATUS_SCSI_PORT_OR_XCVR_FAIL_SHIFT = 6, + + SES_STATUS_SCSI_PORT_OR_XCVR_REPORT_BYTE = 1, + SES_STATUS_SCSI_PORT_OR_XCVR_REPORT_MASK = 0x01, + SES_STATUS_SCSI_PORT_OR_XCVR_REPORT_SHIFT = 0, + + SES_STATUS_SCSI_PORT_OR_XCVR_DISABLED_BYTE = 2, + SES_STATUS_SCSI_PORT_OR_XCVR_DISABLED_MASK = 0x10, + SES_STATUS_SCSI_PORT_OR_XCVR_DISABLED_SHIFT = 4, + + SES_STATUS_SCSI_PORT_OR_XCVR_LOL_BYTE = 2, + SES_STATUS_SCSI_PORT_OR_XCVR_LOL_MASK = 0x02, + SES_STATUS_SCSI_PORT_OR_XCVR_LOL_SHIFT = 1, + + SES_STATUS_SCSI_PORT_OR_XCVR_XMIT_FAIL_BYTE = 2, + SES_STATUS_SCSI_PORT_OR_XCVR_XMIT_FAIL_MASK = 0x01, + SES_STATUS_SCSI_PORT_OR_XCVR_XMIT_FAIL_SHIFT = 0 +}; + +#define GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_scsi_port_or_xcvr, SES_STATUS_SCSI_PORT_OR_XCVR,\ + LCASE, UCASE) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(report, REPORT) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(disable, DISABLED) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(lol, LOL) +GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS(xmit_fail, XMIT_FAIL) +#undef GEN_SES_STATUS_SCSI_PORT_OR_XCVR_ACCESSORS + +/*------------------------- Language Status Element --------------------------*/ +struct ses_status_language { + struct ses_status_common common; + uint8_t bytes[1]; + uint8_t language_code[2]; +}; + +enum ses_status_language_field_data { + SES_STATUS_LANGUAGE_IDENT_BYTE = 0, + SES_STATUS_LANGUAGE_IDENT_MASK = 0x80, + SES_STATUS_LANGUAGE_IDENT_SHIFT = 7 +}; + +#define GEN_SES_STATUS_LANGUAGE_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_language, SES_STATUS_LANGUAGE, LCASE, UCASE) +GEN_SES_STATUS_LANGUAGE_ACCESSORS(ident, IDENT) +#undef GEN_SES_STATUS_LANGUAGE_ACCESSORS + +/*-------------------- Communication Port Status Element ---------------------*/ +struct ses_status_comm_port { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_comm_port_field_data { + SES_STATUS_COMM_PORT_IDENT_BYTE = 0, + SES_STATUS_COMM_PORT_IDENT_MASK = 0x80, + SES_STATUS_COMM_PORT_IDENT_SHIFT = 7, + + SES_STATUS_COMM_PORT_FAIL_BYTE = 0, + SES_STATUS_COMM_PORT_FAIL_MASK = 0x40, + SES_STATUS_COMM_PORT_FAIL_SHIFT = 6, + + SES_STATUS_COMM_PORT_DISABLED_BYTE = 2, + SES_STATUS_COMM_PORT_DISABLED_MASK = 0x01, + SES_STATUS_COMM_PORT_DISABLED_SHIFT = 0 +}; + +#define GEN_SES_STATUS_COMM_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_comm_port, SES_STATUS_COMM_PORT, LCASE, UCASE) +GEN_SES_STATUS_COMM_PORT_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_COMM_PORT_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_COMM_PORT_ACCESSORS(disabled, DISABLED) +#undef GEN_SES_STATUS_COMM_PORT_ACCESSORS + +/*---------------------- Voltage Sensor Status Element -----------------------*/ +struct ses_status_voltage_sensor { + struct ses_status_common common; + uint8_t bytes[1]; + uint8_t voltage[2]; +}; + +enum ses_status_voltage_sensor_field_data { + SES_STATUS_VOLTAGE_SENSOR_IDENT_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_IDENT_MASK = 0x80, + SES_STATUS_VOLTAGE_SENSOR_IDENT_SHIFT = 7, + + SES_STATUS_VOLTAGE_SENSOR_FAIL_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_FAIL_MASK = 0x40, + SES_STATUS_VOLTAGE_SENSOR_FAIL_SHIFT = 6, + + SES_STATUS_VOLTAGE_SENSOR_WARN_OVER_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_WARN_OVER_MASK = 0x08, + SES_STATUS_VOLTAGE_SENSOR_WARN_OVER_SHIFT = 3, + + SES_STATUS_VOLTAGE_SENSOR_WARN_UNDER_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_WARN_UNDER_MASK = 0x04, + SES_STATUS_VOLTAGE_SENSOR_WARN_UNDER_SHIFT = 2, + + SES_STATUS_VOLTAGE_SENSOR_CRIT_OVER_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_CRIT_OVER_MASK = 0x02, + SES_STATUS_VOLTAGE_SENSOR_CRIT_OVER_SHIFT = 1, + + SES_STATUS_VOLTAGE_SENSOR_CRIT_UNDER_BYTE = 0, + SES_STATUS_VOLTAGE_SENSOR_CRIT_UNDER_MASK = 0x01, + SES_STATUS_VOLTAGE_SENSOR_CRIT_UNDER_SHIFT = 0 +}; + +#define GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_voltage_sensor, SES_STATUS_VOLTAGE_SENSOR, \ + LCASE, UCASE) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(warn_over, WARN_OVER) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(warn_under, WARN_UNDER) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(crit_over, CRIT_OVER) +GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS(crit_under, CRIT_UNDER) +#undef GEN_SES_STATUS_VOLTAGE_SENSOR_ACCESSORS + +/*---------------------- Current Sensor Status Element -----------------------*/ +struct ses_status_current_sensor { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_current_sensor_field_data { + SES_STATUS_CURRENT_SENSOR_IDENT_BYTE = 0, + SES_STATUS_CURRENT_SENSOR_IDENT_MASK = 0x80, + SES_STATUS_CURRENT_SENSOR_IDENT_SHIFT = 7, + + SES_STATUS_CURRENT_SENSOR_FAIL_BYTE = 0, + SES_STATUS_CURRENT_SENSOR_FAIL_MASK = 0x40, + SES_STATUS_CURRENT_SENSOR_FAIL_SHIFT = 6, + + SES_STATUS_CURRENT_SENSOR_WARN_OVER_BYTE = 0, + SES_STATUS_CURRENT_SENSOR_WARN_OVER_MASK = 0x08, + SES_STATUS_CURRENT_SENSOR_WARN_OVER_SHIFT = 3, + + SES_STATUS_CURRENT_SENSOR_CRIT_OVER_BYTE = 0, + SES_STATUS_CURRENT_SENSOR_CRIT_OVER_MASK = 0x02, + SES_STATUS_CURRENT_SENSOR_CRIT_OVER_SHIFT = 1 +}; + +#define GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_current_sensor, SES_STATUS_CURRENT_SENSOR, \ + LCASE, UCASE) +GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(warn_over, WARN_OVER) +GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS(crit_over, CRIT_OVER) +#undef GEN_SES_STATUS_CURRENT_SENSOR_ACCESSORS + +/*--------------------- SCSI Target Port Status Element ----------------------*/ +struct ses_status_target_port { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_scsi_target_port_field_data { + SES_STATUS_TARGET_PORT_IDENT_BYTE = 0, + SES_STATUS_TARGET_PORT_IDENT_MASK = 0x80, + SES_STATUS_TARGET_PORT_IDENT_SHIFT = 7, + + SES_STATUS_TARGET_PORT_FAIL_BYTE = 0, + SES_STATUS_TARGET_PORT_FAIL_MASK = 0x40, + SES_STATUS_TARGET_PORT_FAIL_SHIFT = 6, + + SES_STATUS_TARGET_PORT_REPORT_BYTE = 1, + SES_STATUS_TARGET_PORT_REPORT_MASK = 0x01, + SES_STATUS_TARGET_PORT_REPORT_SHIFT = 0, + + SES_STATUS_TARGET_PORT_ENABLED_BYTE = 2, + SES_STATUS_TARGET_PORT_ENABLED_MASK = 0x01, + SES_STATUS_TARGET_PORT_ENABLED_SHIFT = 0 +}; + +#define GEN_SES_STATUS_TARGET_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_target_port, SES_STATUS_TARGET_PORT, LCASE, UCASE) +GEN_SES_STATUS_TARGET_PORT_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_TARGET_PORT_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_TARGET_PORT_ACCESSORS(report, REPORT) +GEN_SES_STATUS_TARGET_PORT_ACCESSORS(enabled, ENABLED) +#undef GEN_SES_STATUS_TARGET_PORT_ACCESSORS + +/*-------------------- SCSI Initiator Port Status Element --------------------*/ +struct ses_status_initiator_port { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_scsi_initiator_port_field_data { + SES_STATUS_INITIATOR_PORT_IDENT_BYTE = 0, + SES_STATUS_INITIATOR_PORT_IDENT_MASK = 0x80, + SES_STATUS_INITIATOR_PORT_IDENT_SHIFT = 7, + + SES_STATUS_INITIATOR_PORT_FAIL_BYTE = 0, + SES_STATUS_INITIATOR_PORT_FAIL_MASK = 0x40, + SES_STATUS_INITIATOR_PORT_FAIL_SHIFT = 6, + + SES_STATUS_INITIATOR_PORT_REPORT_BYTE = 1, + SES_STATUS_INITIATOR_PORT_REPORT_MASK = 0x01, + SES_STATUS_INITIATOR_PORT_REPORT_SHIFT = 0, + + SES_STATUS_INITIATOR_PORT_ENABLED_BYTE = 2, + SES_STATUS_INITIATOR_PORT_ENABLED_MASK = 0x01, + SES_STATUS_INITIATOR_PORT_ENABLED_SHIFT = 0 +}; + +#define GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_initiator_port, SES_STATUS_INITIATOR_PORT, \ + LCASE, UCASE) +GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(fail, FAIL) +GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(report, REPORT) +GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS(enabled, ENABLED) +#undef GEN_SES_STATUS_INITIATOR_PORT_ACCESSORS + +/*-------------------- Simple Subenclosure Status Element --------------------*/ +struct ses_status_simple_subses { + struct ses_status_common common; + uint8_t bytes[2]; + uint8_t short_enclosure_status; +}; + +enum ses_status_simple_subses_field_data { + SES_STATUS_SIMPlE_SUBSES_IDENT_BYTE = 0, + SES_STATUS_SIMPlE_SUBSES_IDENT_MASK = 0x80, + SES_STATUS_SIMPlE_SUBSES_IDENT_SHIFT = 7, + + SES_STATUS_SIMPlE_SUBSES_FAIL_BYTE = 0, + SES_STATUS_SIMPlE_SUBSES_FAIL_MASK = 0x40, + SES_STATUS_SIMPlE_SUBSES_FAIL_SHIFT = 6 +}; + +#define GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_simple_subses, SES_STATUS_SIMPlE_SUBSES, \ + LCASE, UCASE) +GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS(fail, FAIL) +#undef GEN_SES_STATUS_SIMPlE_SUBSES_ACCESSORS + +/*----------------------- SAS Expander Status Element ------------------------*/ +struct ses_status_sas_expander { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_sas_expander_field_data { + SES_STATUS_SAS_EXPANDER_IDENT_BYTE = 0, + SES_STATUS_SAS_EXPANDER_IDENT_MASK = 0x80, + SES_STATUS_SAS_EXPANDER_IDENT_SHIFT = 7, + + SES_STATUS_SAS_EXPANDER_FAIL_BYTE = 0, + SES_STATUS_SAS_EXPANDER_FAIL_MASK = 0x40, + SES_STATUS_SAS_EXPANDER_FAIL_SHIFT = 6 +}; + +#define GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_sas_expander, SES_STATUS_SAS_EXPANDER, LCASE, UCASE) +GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS(fail, FAIL) +#undef GEN_SES_STATUS_SAS_EXPANDER_ACCESSORS + +/*----------------------- SAS Connector Status Element -----------------------*/ +struct ses_status_sas_connector { + struct ses_status_common common; + uint8_t bytes[3]; +}; + +enum ses_status_sas_connector_field_data { + SES_STATUS_SAS_CONNECTOR_IDENT_BYTE = 0, + SES_STATUS_SAS_CONNECTOR_IDENT_MASK = 0x80, + SES_STATUS_SAS_CONNECTOR_IDENT_SHIFT = 7, + + SES_STATUS_SAS_CONNECTOR_TYPE_BYTE = 0, + SES_STATUS_SAS_CONNECTOR_TYPE_MASK = 0x7F, + SES_STATUS_SAS_CONNECTOR_TYPE_SHIFT = 0, + + SES_STATUS_SAS_CONNECTOR_PHYS_LINK_BYTE = 1, + SES_STATUS_SAS_CONNECTOR_PHYS_LINK_MASK = 0xFF, + SES_STATUS_SAS_CONNECTOR_PHYS_LINK_SHIFT = 0, + SES_STATUS_SAS_CONNECTOR_PHYS_LINK_ALL = 0xFF, + + SES_STATUS_SAS_CONNECTOR_FAIL_BYTE = 2, + SES_STATUS_SAS_CONNECTOR_FAIL_MASK = 0x40, + SES_STATUS_SAS_CONNECTOR_FAIL_SHIFT = 6, +}; + +#define GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(LCASE, UCASE) \ + GEN_GETTER(ses_status_sas_connector, SES_STATUS_SAS_CONNECTOR, \ + LCASE, UCASE) +GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(ident, IDENT) +GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(type, TYPE) +GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(phys_link, PHYS_LINK) +GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS(fail, FAIL) +#undef GEN_SES_STATUS_SAS_CONNECTOR_ACCESSORS + +/*------------------------- Universal Status Element -------------------------*/ +union ses_status_element { + struct ses_status_common common; + struct ses_status_dev_slot dev_slot; + struct ses_status_array_dev_slot array_dev_slot; + struct ses_status_power_supply power_supply; + struct ses_status_cooling cooling; + struct ses_status_temp_sensor temp_sensor; + struct ses_status_door_lock door_lock; + struct ses_status_audible_alarm audible_alarm; + struct ses_status_ecc_electronics ecc_electronics; + struct ses_status_scc_electronics scc_electronics; + struct ses_status_nv_cache nv_cache; + struct ses_status_invalid_op_reason invalid_op_reason; + struct ses_status_ups ups; + struct ses_status_display display; + struct ses_status_key_pad_entry key_pad_entry; + struct ses_status_scsi_port_or_xcvr scsi_port_or_xcvr; + struct ses_status_language language; + struct ses_status_comm_port comm_port; + struct ses_status_voltage_sensor voltage_sensor; + struct ses_status_current_sensor current_sensor; + struct ses_status_target_port target_port; + struct ses_status_initiator_port initiator_port; + struct ses_status_simple_subses simple_subses; + struct ses_status_sas_expander sas_expander; + struct ses_status_sas_connector sas_connector; + uint8_t bytes[4]; +}; + +/*===================== SCSI SES Status Diagnostic Page =====================*/ +struct ses_status_page { + struct ses_page_hdr hdr; + union ses_status_element elements[]; +}; + +enum ses_status_page_field_data { + SES_STATUS_PAGE_INVOP_MASK = 0x10, + SES_STATUS_PAGE_INVOP_SHIFT = 4, + + SES_STATUS_PAGE_INFO_MASK = 0x08, + SES_STATUS_PAGE_INFO_SHIFT = 3, + + SES_STATUS_PAGE_NON_CRIT_MASK = 0x04, + SES_STATUS_PAGE_NON_CRIT_SHIFT = 2, + + SES_STATUS_PAGE_CRIT_MASK = 0x02, + SES_STATUS_PAGE_CRIT_SHIFT = 1, + + SES_STATUS_PAGE_UNRECOV_MASK = 0x01, + SES_STATUS_PAGE_UNRECOV_SHIFT = 0, + + SES_STATUS_PAGE_CHANGED_MASK = SES_STATUS_PAGE_INVOP_MASK + | SES_STATUS_PAGE_INFO_MASK + | SES_STATUS_PAGE_NON_CRIT_MASK + | SES_STATUS_PAGE_CRIT_MASK + | SES_STATUS_PAGE_UNRECOV_MASK, + SES_STATUS_PAGE_CHANGED_SHIFT = 0, +}; + +#define GEN_SES_STATUS_PAGE_ACCESSORS(LCASE, UCASE) \ + GEN_HDR_ACCESSORS(ses_status_page, SES_STATUS_PAGE, LCASE, UCASE) + +GEN_SES_STATUS_PAGE_ACCESSORS(invop, INVOP) +GEN_SES_STATUS_PAGE_ACCESSORS(info, INFO) +GEN_SES_STATUS_PAGE_ACCESSORS(non_crit, NON_CRIT) +GEN_SES_STATUS_PAGE_ACCESSORS(crit, CRIT) +GEN_SES_STATUS_PAGE_ACCESSORS(unrecov, UNRECOV) +GEN_SES_STATUS_PAGE_ACCESSORS(changed, CHANGED) +#undef GEN_SES_STATUS_PAGE_ACCESSORS + +/*================ SCSI SES Element Descriptor Diagnostic Page ===============*/ +struct ses_elem_descr { + uint8_t reserved[2]; + uint8_t length[2]; + char description[]; +}; + +struct ses_elem_descr_page { + struct ses_page_hdr hdr; + struct ses_elem_descr descrs[]; +}; + +/*============ SCSI SES Additional Element Status Diagnostic Page ============*/ +struct ses_addl_elem_status_page { + struct ses_page_hdr hdr; +}; + +/*====================== Legacy (Deprecated) Structures ======================*/ +struct ses_control_page_hdr { + uint8_t page_code; + uint8_t control_flags; + uint8_t length[2]; + uint8_t gen_code[4]; +/* Followed by variable length array of descriptors. */ +}; + +struct ses_status_page_hdr { + uint8_t page_code; + uint8_t status_flags; + uint8_t length[2]; + uint8_t gen_code[4]; +/* Followed by variable length array of descriptors. */ +}; + +/* ses_page_hdr.reserved values */ /* - * Object Status + * Enclosure Status Diagnostic Page: + * uint8_t reserved : 3, + * invop : 1, + * info : 1, + * noncritical : 1, + * critical : 1, + * unrecov : 1; */ -typedef struct { - unsigned int obj_id; - unsigned char cstat[4]; -} ses_objstat; +#define SES_ENCSTAT_UNRECOV 0x01 +#define SES_ENCSTAT_CRITICAL 0x02 +#define SES_ENCSTAT_NONCRITICAL 0x04 +#define SES_ENCSTAT_INFO 0x08 +#define SES_ENCSTAT_INVOP 0x10 +/* Status mask: All of the above OR'd together */ +#define SES_STATUS_MASK 0x1f +#define SES_SET_STATUS_MASK 0xf +/* Element Descriptor Diagnostic Page: unused */ +/* Additional Element Status Diagnostic Page: unused */ + + /* Summary SES Status Defines, Common Status Codes */ #define SES_OBJSTAT_UNSUPPORTED 0 @@ -140,6 +2164,7 @@ typedef struct { #define SES_OBJSTAT_NOTINSTALLED 5 #define SES_OBJSTAT_UNKNOWN 6 #define SES_OBJSTAT_NOTAVAIL 7 +#define SES_OBJSTAT_NOACCESS 8 /* * For control pages, cstat[0] is the same for the @@ -181,3 +2206,236 @@ typedef union { unsigned int obj_id; char obj_text[1]; } ses_hlptxt; + +/*============================================================================*/ +struct ses_elm_desc_hdr { + uint8_t reserved[2]; + uint8_t length[2]; +}; + +/* + * SES v2 r20 6.1.13 - Element Additional Status diagnostic page + * Tables 26-28 (general), 29-32 (FC), 33-41 (SAS) + * + * Protocol identifier uses definitions in scsi_all.h; + * SPSP_PROTO_FC, SPSP_PROTO_SAS are the only ones used here. + */ + +struct ses_elm_fc_eip_hdr { + uint8_t num_phys; + uint8_t reserved[2]; + uint8_t dev_slot_num; + uint8_t node_name[8]; +}; + +struct ses_elm_fc_noneip_hdr { + uint8_t num_phys; + uint8_t reserved; + uint8_t node_name[8]; +}; + +struct ses_elm_fc_base_hdr { + uint8_t num_phys; +}; + +union ses_elm_fc_hdr { + struct ses_elm_fc_base_hdr base_hdr; + struct ses_elm_fc_eip_hdr eip_hdr; + struct ses_elm_fc_noneip_hdr noneip_hdr; +}; + +struct ses_elm_fc_port { + uint8_t port_loop_position; + uint8_t bypass_reason; +#define SES_FC_PORT_BYPASS_UNBYPASSED 0x00 + +#define SES_FC_PORT_BYPASS_LINKFAIL_RATE_TOO_HIGH 0x10 +#define SES_FC_PORT_BYPASS_SYNC_LOSS_RATE_TOO_HIGH 0x11 +#define SES_FC_PORT_BYPASS_SIGNAL_LOSS_RATE_TOO_HIGH 0x12 +#define SES_FC_PORT_BYPASS_SEQPROTO_ERR_RATE_TOO_HIGH 0x13 +#define SES_FC_PORT_BYPASS_INVAL_XMIT_RATE_TOO_HIGH 0x14 +#define SES_FC_PORT_BYPASS_CRC_ERR_RATE_TOO_HIGH 0x15 + +#define SES_FC_PORT_BYPASS_ERR_RATE_RESERVED_BEGIN 0x16 +#define SES_FC_PORT_BYPASS_ERR_RATE_RESERVED_END 0x1F + +#define SES_FC_PORT_BYPASS_LINKFAIL_COUNT_TOO_HIGH 0x20 +#define SES_FC_PORT_BYPASS_SYNC_LOSS_COUNT_TOO_HIGH 0x21 +#define SES_FC_PORT_BYPASS_SIGNAL_LOSS_COUNT_TOO_HIGH 0x22 +#define SES_FC_PORT_BYPASS_SEQPROTO_ERR_COUNT_TOO_HIGH 0x23 +#define SES_FC_PORT_BYPASS_INVAL_XMIT_COUNT_TOO_HIGH 0x24 +#define SES_FC_PORT_BYPASS_CRC_ERR_COUNT_TOO_HIGH 0x25 + +#define SES_FC_PORT_BYPASS_ERR_COUNT_RESERVED_BEGIN 0x26 +#define SES_FC_PORT_BYPASS_ERR_COUNT_RESERVED_END 0x2F + +#define SES_FC_PORT_BYPASS_RESERVED_BEGIN 0x30 +#define SES_FC_PORT_BYPASS_RESERVED_END 0xBF + +#define SES_FC_PORT_BYPASS_VENDOR_SPECIFIC_BEGIN 0xC0 +#define SES_FC_PORT_BYPASS_VENDOR_SPECIFIC_END 0xFF + uint8_t port_req_hard_addr; + uint8_t n_port_id[3]; + uint8_t n_port_name[8]; +}; + +struct ses_elm_sas_device_phy { + uint8_t byte0; + /* + * uint8_t reserved0 : 1, + * uint8_t device_type : 3, + * uint8_t reserved1 : 4; + */ + + uint8_t reserved0; + + /* Bit positions for initiator and target port protocols */ +#define SES_SASOBJ_DEV_PHY_SMP 0x2 +#define SES_SASOBJ_DEV_PHY_STP 0x4 +#define SES_SASOBJ_DEV_PHY_SSP 0x8 + /* Select all of the above protocols */ +#define SES_SASOBJ_DEV_PHY_PROTOMASK 0xe + uint8_t initiator_ports; + /* + * uint8_t reserved0 : 4, + * uint8_t ssp : 1, + * uint8_t stp : 1, + * uint8_t smp : 1, + * uint8_t reserved1 : 3; + */ + uint8_t target_ports; + /* + * uint8_t sata_port_selector : 1, + * uint8_t reserved : 3, + * uint8_t ssp : 1, + * uint8_t stp : 1, + * uint8_t smp : 1, + * uint8_t sata_device : 1; + */ + uint8_t parent_addr[8]; /* SAS address of parent */ + uint8_t phy_addr[8]; /* SAS address of this phy */ + uint8_t phy_id; + uint8_t reserved1[7]; +}; +#ifdef _KERNEL +int ses_elm_sas_dev_phy_sata_dev(struct ses_elm_sas_device_phy *); +int ses_elm_sas_dev_phy_sata_port(struct ses_elm_sas_device_phy *); +int ses_elm_sas_dev_phy_dev_type(struct ses_elm_sas_device_phy *); +#endif /* _KERNEL */ + +struct ses_elm_sas_expander_phy { + uint8_t connector_index; + uint8_t other_index; +}; + +struct ses_elm_sas_port_phy { + uint8_t phy_id; + uint8_t reserved; + uint8_t connector_index; + uint8_t other_index; + uint8_t phy_addr[8]; +}; + +struct ses_elm_sas_type0_base_hdr { + uint8_t num_phys; + uint8_t byte1; + /* + * uint8_t descriptor_type : 2, + * uint8_t reserved : 5, + * uint8_t not_all_phys : 1; + */ +#define SES_SASOBJ_TYPE0_NOT_ALL_PHYS(obj) \ + ((obj)->byte1 & 0x1) +}; + +struct ses_elm_sas_type0_eip_hdr { + struct ses_elm_sas_type0_base_hdr base; + uint8_t reserved; + uint8_t dev_slot_num; +}; + +struct ses_elm_sas_type1_expander_hdr { + uint8_t num_phys; + uint8_t byte1; + /* + * uint8_t descriptor_type : 2, + * uint8_t reserved : 6; + */ + uint8_t reserved[2]; + uint8_t sas_addr[8]; +}; + +struct ses_elm_sas_type1_nonexpander_hdr { + uint8_t num_phys; + uint8_t byte1; + /* + * uint8_t descriptor_type : 2, + * uint8_t reserved : 6; + */ + uint8_t reserved[2]; +}; + +/* NB: This is only usable for as long as the headers happen to match */ +struct ses_elm_sas_base_hdr { + uint8_t num_phys; + uint8_t byte1; + /* + * uint8_t descriptor_type : 2, + * uint8_t descr_specific : 6; + */ +#define SES_SASOBJ_TYPE_SLOT 0 +#define SES_SASOBJ_TYPE_OTHER 1 +}; + +union ses_elm_sas_hdr { + struct ses_elm_sas_base_hdr base_hdr; + struct ses_elm_sas_type0_base_hdr type0_noneip; + struct ses_elm_sas_type0_eip_hdr type0_eip; + struct ses_elm_sas_type1_expander_hdr type1_exp; + struct ses_elm_sas_type1_nonexpander_hdr type1_nonexp; +}; +int ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *); +int ses_elm_sas_descr_type(union ses_elm_sas_hdr *); + +struct ses_elm_addlstatus_base_hdr { + uint8_t byte0; + /* + * uint8_t invalid : 1, + * uint8_t reserved : 2, + * uint8_t eip : 1, + * uint8_t proto_id : 4; + */ + uint8_t length; +}; +int ses_elm_addlstatus_proto(struct ses_elm_addlstatus_base_hdr *); +int ses_elm_addlstatus_eip(struct ses_elm_addlstatus_base_hdr *); +int ses_elm_addlstatus_invalid(struct ses_elm_addlstatus_base_hdr *); + +struct ses_elm_addlstatus_eip_hdr { + struct ses_elm_addlstatus_base_hdr base; + uint8_t reserved; + uint8_t element_index; + /* NB: This define (currently) applies to all eip=1 headers */ +#define SES_EIP_HDR_EXTRA_LEN 2 +}; + +union ses_elm_addlstatus_descr_hdr { + struct ses_elm_addlstatus_base_hdr base; + struct ses_elm_addlstatus_eip_hdr eip; +}; + +union ses_elm_addlstatus_proto_hdr { + union ses_elm_fc_hdr fc; + union ses_elm_sas_hdr sas; +}; + +/*============================= Namespace Cleanup ============================*/ +#undef GEN_HDR_ACCESSORS +#undef GEN_ACCESSORS +#undef GEN_HDR_SETTER +#undef GEN_HDR_GETTER +#undef GEN_SETTER +#undef GEN_GETTER +#undef MK_ENUM + +#endif /* _SCSI_SES_H_ */ diff --git a/sys/conf/files b/sys/conf/files index eadb6418a..d7cf7055a 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -134,7 +134,9 @@ cam/scsi/scsi_low_pisa.c optional ct | ncv | nsp | stg 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 diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 9851229d5..853d0c21c 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -1276,8 +1276,53 @@ static int 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)); } diff --git a/sys/modules/cam/Makefile b/sys/modules/cam/Makefile index 35b36d84a..00a531a78 100644 --- a/sys/modules/cam/Makefile +++ b/sys/modules/cam/Makefile @@ -14,7 +14,6 @@ SRCS+= opt_scsi.h 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_da.c 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 diff --git a/usr.bin/kdump/mkioctls b/usr.bin/kdump/mkioctls index 7dd68cc6e..e79a5d8de 100644 --- a/usr.bin/kdump/mkioctls +++ b/usr.bin/kdump/mkioctls @@ -54,6 +54,8 @@ BEGIN { print "#include " print "#include " print "#include " + print "#include " + print "#include " print "" print "const char *ioctlname(u_long val);" print "" -- 2.45.0