#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>
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)
#define SAFTE_RD_RDCFG 0x00
#define SAFTE_RD_RDESTS 0x01
#define SAFTE_RD_RDDSTS 0x04
#define SAFTE_RD_RDGFLG 0x05
#define SAFTE_WT_DSTAT 0x10
#define SAFTE_WT_SLTOP 0x12
#define SAFTE_WT_FANSPD 0x13
#define SAFTE_WT_ACTPWS 0x14
#define SAFTE_WT_GLOBAL 0x15
#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 {
uint8_t Nfans;
uint8_t Npwr;
uint8_t Nslots;
uint8_t DoorLock;
uint8_t Ntherm;
uint8_t Nspkrs;
uint8_t Ntstats;
uint8_t flag1;
uint8_t flag2;
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, 5,
NULL, MSG_SIMPLE_Q_TAG,
state->page_code, buf, state->buf_size,
state->timeout);
} else {
scsi_read_buffer(&ccb->csio, 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;
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);
cache->elm_map[oid].encstat[1] = 0;
cache->elm_map[oid].encstat[2] = 0;
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;
cache->elm_map[oid].encstat[3] |= 0x10;
cache->elm_map[oid].encstat[3] &= ~0x07;
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;
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++;
}
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;
cache->elm_map[oid].encstat[2] = 0;
cache->elm_map[oid].encstat[3] = 0x20;
switch (buf[r]) {
case 0x00:
cache->elm_map[oid].encstat[0] = SES_OBJSTAT_OK;
break;
case 0x01:
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:
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:
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:
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:
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:
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++;
}
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++;
}
SAFT_BAIL(r, xfer_len);
if (cfg->DoorLock) {
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++;
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++;
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);
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;
cache->elm_map[oid].encstat[3] = 8;
cfg->enc_status |= SES_ENCSTAT_CRITICAL;
} else {
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) {
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;
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, 5,
NULL, MSG_SIMPLE_Q_TAG,
buf, xfer_len, state->timeout);
} else {
scsi_write_buffer(&ccb->csio, 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 ((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);
}