/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2000 Matthew Jacob * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include <sys/cdefs.h> #include <sys/param.h> #include <sys/conf.h> #include <sys/errno.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/mutex.h> #include <sys/queue.h> #include <sys/sx.h> #include <sys/systm.h> #include <sys/sysctl.h> #include <sys/types.h> #include <cam/cam.h> #include <cam/cam_ccb.h> #include <cam/cam_periph.h> #include <cam/scsi/scsi_enc.h> #include <cam/scsi/scsi_enc_internal.h> #include <cam/scsi/scsi_message.h> /* * SAF-TE Type Device Emulation */ static int safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag); #define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \ SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO) /* * SAF-TE specific defines- Mandatory ones only... */ /* * READ BUFFER ('get' commands) IDs- placed in offset 2 of cdb */ #define SAFTE_RD_RDCFG 0x00 /* read enclosure configuration */ #define SAFTE_RD_RDESTS 0x01 /* read enclosure status */ #define SAFTE_RD_RDDSTS 0x04 /* read drive slot status */ #define SAFTE_RD_RDGFLG 0x05 /* read global flags */ /* * WRITE BUFFER ('set' commands) IDs- placed in offset 0 of databuf */ #define SAFTE_WT_DSTAT 0x10 /* write device slot status */ #define SAFTE_WT_SLTOP 0x12 /* perform slot operation */ #define SAFTE_WT_FANSPD 0x13 /* set fan speed */ #define SAFTE_WT_ACTPWS 0x14 /* turn on/off power supply */ #define SAFTE_WT_GLOBAL 0x15 /* send global command */ #define SAFT_SCRATCH 64 #define SCSZ 0x8000 typedef enum { SAFTE_UPDATE_NONE, SAFTE_UPDATE_READCONFIG, SAFTE_UPDATE_READGFLAGS, SAFTE_UPDATE_READENCSTATUS, SAFTE_UPDATE_READSLOTSTATUS, SAFTE_PROCESS_CONTROL_REQS, SAFTE_NUM_UPDATE_STATES } safte_update_action; static fsm_fill_handler_t safte_fill_read_buf_io; static fsm_fill_handler_t safte_fill_control_request; static fsm_done_handler_t safte_process_config; static fsm_done_handler_t safte_process_gflags; static fsm_done_handler_t safte_process_status; static fsm_done_handler_t safte_process_slotstatus; static fsm_done_handler_t safte_process_control_request; static struct enc_fsm_state enc_fsm_states[SAFTE_NUM_UPDATE_STATES] = { { "SAFTE_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL }, { "SAFTE_UPDATE_READCONFIG", SAFTE_RD_RDCFG, SAFT_SCRATCH, 60 * 1000, safte_fill_read_buf_io, safte_process_config, enc_error }, { "SAFTE_UPDATE_READGFLAGS", SAFTE_RD_RDGFLG, 16, 60 * 1000, safte_fill_read_buf_io, safte_process_gflags, enc_error }, { "SAFTE_UPDATE_READENCSTATUS", SAFTE_RD_RDESTS, SCSZ, 60 * 1000, safte_fill_read_buf_io, safte_process_status, enc_error }, { "SAFTE_UPDATE_READSLOTSTATUS", SAFTE_RD_RDDSTS, SCSZ, 60 * 1000, safte_fill_read_buf_io, safte_process_slotstatus, enc_error }, { "SAFTE_PROCESS_CONTROL_REQS", 0, SCSZ, 60 * 1000, safte_fill_control_request, safte_process_control_request, enc_error } }; typedef struct safte_control_request { int elm_idx; uint8_t elm_stat[4]; int result; TAILQ_ENTRY(safte_control_request) links; } safte_control_request_t; TAILQ_HEAD(safte_control_reqlist, safte_control_request); typedef struct safte_control_reqlist safte_control_reqlist_t; enum { SES_SETSTATUS_ENC_IDX = -1 }; static void safte_terminate_control_requests(safte_control_reqlist_t *reqlist, int result) { safte_control_request_t *req; while ((req = TAILQ_FIRST(reqlist)) != NULL) { TAILQ_REMOVE(reqlist, req, links); req->result = result; wakeup(req); } } struct scfg { /* * Cached Configuration */ uint8_t Nfans; /* Number of Fans */ uint8_t Npwr; /* Number of Power Supplies */ uint8_t Nslots; /* Number of Device Slots */ uint8_t DoorLock; /* Door Lock Installed */ uint8_t Ntherm; /* Number of Temperature Sensors */ uint8_t Nspkrs; /* Number of Speakers */ uint8_t Ntstats; /* Number of Thermostats */ /* * Cached Flag Bytes for Global Status */ uint8_t flag1; uint8_t flag2; /* * What object index ID is where various slots start. */ uint8_t pwroff; uint8_t slotoff; #define SAFT_ALARM_OFFSET(cc) (cc)->slotoff - 1 encioc_enc_status_t adm_status; encioc_enc_status_t enc_status; encioc_enc_status_t slot_status; safte_control_reqlist_t requests; safte_control_request_t *current_request; int current_request_stage; int current_request_stages; }; #define SAFT_FLG1_ALARM 0x1 #define SAFT_FLG1_GLOBFAIL 0x2 #define SAFT_FLG1_GLOBWARN 0x4 #define SAFT_FLG1_ENCPWROFF 0x8 #define SAFT_FLG1_ENCFANFAIL 0x10 #define SAFT_FLG1_ENCPWRFAIL 0x20 #define SAFT_FLG1_ENCDRVFAIL 0x40 #define SAFT_FLG1_ENCDRVWARN 0x80 #define SAFT_FLG2_LOCKDOOR 0x4 #define SAFT_PRIVATE sizeof (struct scfg) static char *safte_2little = "Too Little Data Returned (%d) at line %d\n"; #define SAFT_BAIL(r, x) \ if ((r) >= (x)) { \ ENC_VLOG(enc, safte_2little, x, __LINE__);\ return (EIO); \ } int emulate_array_devices = 1; SYSCTL_INT(_kern_cam_enc, OID_AUTO, emulate_array_devices, CTLFLAG_RWTUN, &emulate_array_devices, 0, "Emulate Array Devices for SAF-TE"); 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, NULL, MSG_SIMPLE_Q_TAG, state->page_code, buf, state->buf_size, state->timeout); } else { scsi_read_buffer(&ccb->csio, /*retries*/5, NULL, 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 = malloc(enc->enc_cache.nelms * sizeof(enc_element_t), M_SCSIENC, M_WAITOK|M_ZERO); 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++].elm_type = ELMTYP_FAN; cfg->pwroff = (uint8_t) r; for (i = 0; i < cfg->Npwr; i++) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_POWER; for (i = 0; i < cfg->DoorLock; i++) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_DOORLOCK; if (cfg->Nspkrs > 0) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_ALARM; for (i = 0; i < cfg->Ntherm; i++) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_THERM; for (i = 0; i <= cfg->Ntstats; i++) enc->enc_cache.elm_map[r++].elm_type = ELMTYP_THERM; cfg->slotoff = (uint8_t) r; for (i = 0; i < cfg->Nslots; i++) enc->enc_cache.elm_map[r++].elm_type = 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].elm_type == 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].elm_type == 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].elm_type == 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->elm_type) { 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->elm_type == 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, NULL, MSG_SIMPLE_Q_TAG, buf, xfer_len, state->timeout); } else { scsi_write_buffer(&ccb->csio, /*retries*/5, NULL, 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].elm_type; 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_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, .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); }