53f5ac1310
Since SES specs do not define mechanism to map enclosure slots to SATA disks, AHCI EM code I written many years ago appeared quite useless, that always bugged me. I was thinking whether it was a good idea, but if LSI HBAs do that, why I shouldn't? This change introduces simple non-standard mechanism for the mapping into both AHCI EM and SES code, that makes AHCI EM on capable controllers (most of Intel's) a first-class SES citizen, allowing it to report disk physical path to GEOM, show devices inserted into each enclosure slot in `sesutil map` and `getencstat`, control locate and fault LEDs for specific devices with `sesutil locate adaX on` and `sesutil fault adaX on`, etc. I've successfully tested this on Supermicro X10DRH-i motherboard connected with sideband cable of its S-SATA Mini-SAS connector to SAS815TQ backplane. It can indicate with LEDs Locate, Fault and Rebuild/Remap SES statuses for each disk identical to real SES of Supermicro SAS2 backplanes. MFC after: 2 weeks
1133 lines
30 KiB
C
1133 lines
30 KiB
C
/*-
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
*
|
|
* Copyright (c) 2000 Matthew Jacob
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions, and the following disclaimer,
|
|
* without modification, immediately at the beginning of the file.
|
|
* 2. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/conf.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/sx.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <cam/cam.h>
|
|
#include <cam/cam_ccb.h>
|
|
#include <cam/cam_periph.h>
|
|
|
|
#include <cam/scsi/scsi_enc.h>
|
|
#include <cam/scsi/scsi_enc_internal.h>
|
|
#include <cam/scsi/scsi_message.h>
|
|
|
|
/*
|
|
* SAF-TE Type Device Emulation
|
|
*/
|
|
|
|
static int safte_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag);
|
|
|
|
#define ALL_ENC_STAT (SES_ENCSTAT_CRITICAL | SES_ENCSTAT_UNRECOV | \
|
|
SES_ENCSTAT_NONCRITICAL | SES_ENCSTAT_INFO)
|
|
/*
|
|
* SAF-TE specific defines- Mandatory ones only...
|
|
*/
|
|
|
|
/*
|
|
* READ BUFFER ('get' commands) IDs- placed in offset 2 of cdb
|
|
*/
|
|
#define SAFTE_RD_RDCFG 0x00 /* read enclosure configuration */
|
|
#define SAFTE_RD_RDESTS 0x01 /* read enclosure status */
|
|
#define SAFTE_RD_RDDSTS 0x04 /* read drive slot status */
|
|
#define SAFTE_RD_RDGFLG 0x05 /* read global flags */
|
|
|
|
/*
|
|
* WRITE BUFFER ('set' commands) IDs- placed in offset 0 of databuf
|
|
*/
|
|
#define SAFTE_WT_DSTAT 0x10 /* write device slot status */
|
|
#define SAFTE_WT_SLTOP 0x12 /* perform slot operation */
|
|
#define SAFTE_WT_FANSPD 0x13 /* set fan speed */
|
|
#define SAFTE_WT_ACTPWS 0x14 /* turn on/off power supply */
|
|
#define SAFTE_WT_GLOBAL 0x15 /* send global command */
|
|
|
|
#define SAFT_SCRATCH 64
|
|
#define SCSZ 0x8000
|
|
|
|
typedef enum {
|
|
SAFTE_UPDATE_NONE,
|
|
SAFTE_UPDATE_READCONFIG,
|
|
SAFTE_UPDATE_READGFLAGS,
|
|
SAFTE_UPDATE_READENCSTATUS,
|
|
SAFTE_UPDATE_READSLOTSTATUS,
|
|
SAFTE_PROCESS_CONTROL_REQS,
|
|
SAFTE_NUM_UPDATE_STATES
|
|
} safte_update_action;
|
|
|
|
static fsm_fill_handler_t safte_fill_read_buf_io;
|
|
static fsm_fill_handler_t safte_fill_control_request;
|
|
static fsm_done_handler_t safte_process_config;
|
|
static fsm_done_handler_t safte_process_gflags;
|
|
static fsm_done_handler_t safte_process_status;
|
|
static fsm_done_handler_t safte_process_slotstatus;
|
|
static fsm_done_handler_t safte_process_control_request;
|
|
|
|
static struct enc_fsm_state enc_fsm_states[SAFTE_NUM_UPDATE_STATES] =
|
|
{
|
|
{ "SAFTE_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL },
|
|
{
|
|
"SAFTE_UPDATE_READCONFIG",
|
|
SAFTE_RD_RDCFG,
|
|
SAFT_SCRATCH,
|
|
60 * 1000,
|
|
safte_fill_read_buf_io,
|
|
safte_process_config,
|
|
enc_error
|
|
},
|
|
{
|
|
"SAFTE_UPDATE_READGFLAGS",
|
|
SAFTE_RD_RDGFLG,
|
|
16,
|
|
60 * 1000,
|
|
safte_fill_read_buf_io,
|
|
safte_process_gflags,
|
|
enc_error
|
|
},
|
|
{
|
|
"SAFTE_UPDATE_READENCSTATUS",
|
|
SAFTE_RD_RDESTS,
|
|
SCSZ,
|
|
60 * 1000,
|
|
safte_fill_read_buf_io,
|
|
safte_process_status,
|
|
enc_error
|
|
},
|
|
{
|
|
"SAFTE_UPDATE_READSLOTSTATUS",
|
|
SAFTE_RD_RDDSTS,
|
|
SCSZ,
|
|
60 * 1000,
|
|
safte_fill_read_buf_io,
|
|
safte_process_slotstatus,
|
|
enc_error
|
|
},
|
|
{
|
|
"SAFTE_PROCESS_CONTROL_REQS",
|
|
0,
|
|
SCSZ,
|
|
60 * 1000,
|
|
safte_fill_control_request,
|
|
safte_process_control_request,
|
|
enc_error
|
|
}
|
|
};
|
|
|
|
typedef struct safte_control_request {
|
|
int elm_idx;
|
|
uint8_t elm_stat[4];
|
|
int result;
|
|
TAILQ_ENTRY(safte_control_request) links;
|
|
} safte_control_request_t;
|
|
TAILQ_HEAD(safte_control_reqlist, safte_control_request);
|
|
typedef struct safte_control_reqlist safte_control_reqlist_t;
|
|
enum {
|
|
SES_SETSTATUS_ENC_IDX = -1
|
|
};
|
|
|
|
static void
|
|
safte_terminate_control_requests(safte_control_reqlist_t *reqlist, int result)
|
|
{
|
|
safte_control_request_t *req;
|
|
|
|
while ((req = TAILQ_FIRST(reqlist)) != NULL) {
|
|
TAILQ_REMOVE(reqlist, req, links);
|
|
req->result = result;
|
|
wakeup(req);
|
|
}
|
|
}
|
|
|
|
struct scfg {
|
|
/*
|
|
* Cached Configuration
|
|
*/
|
|
uint8_t Nfans; /* Number of Fans */
|
|
uint8_t Npwr; /* Number of Power Supplies */
|
|
uint8_t Nslots; /* Number of Device Slots */
|
|
uint8_t DoorLock; /* Door Lock Installed */
|
|
uint8_t Ntherm; /* Number of Temperature Sensors */
|
|
uint8_t Nspkrs; /* Number of Speakers */
|
|
uint8_t Ntstats; /* Number of Thermostats */
|
|
/*
|
|
* Cached Flag Bytes for Global Status
|
|
*/
|
|
uint8_t flag1;
|
|
uint8_t flag2;
|
|
/*
|
|
* What object index ID is where various slots start.
|
|
*/
|
|
uint8_t pwroff;
|
|
uint8_t slotoff;
|
|
#define SAFT_ALARM_OFFSET(cc) (cc)->slotoff - 1
|
|
|
|
encioc_enc_status_t adm_status;
|
|
encioc_enc_status_t enc_status;
|
|
encioc_enc_status_t slot_status;
|
|
|
|
safte_control_reqlist_t requests;
|
|
safte_control_request_t *current_request;
|
|
int current_request_stage;
|
|
int current_request_stages;
|
|
};
|
|
|
|
#define SAFT_FLG1_ALARM 0x1
|
|
#define SAFT_FLG1_GLOBFAIL 0x2
|
|
#define SAFT_FLG1_GLOBWARN 0x4
|
|
#define SAFT_FLG1_ENCPWROFF 0x8
|
|
#define SAFT_FLG1_ENCFANFAIL 0x10
|
|
#define SAFT_FLG1_ENCPWRFAIL 0x20
|
|
#define SAFT_FLG1_ENCDRVFAIL 0x40
|
|
#define SAFT_FLG1_ENCDRVWARN 0x80
|
|
|
|
#define SAFT_FLG2_LOCKDOOR 0x4
|
|
#define SAFT_PRIVATE sizeof (struct scfg)
|
|
|
|
static char *safte_2little = "Too Little Data Returned (%d) at line %d\n";
|
|
#define SAFT_BAIL(r, x) \
|
|
if ((r) >= (x)) { \
|
|
ENC_VLOG(enc, safte_2little, x, __LINE__);\
|
|
return (EIO); \
|
|
}
|
|
|
|
int emulate_array_devices = 1;
|
|
SYSCTL_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_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);
|
|
}
|
|
|