summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2016-10-24 03:45:49 +0000
committerYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2016-10-24 03:45:49 +0000
commitc9debdefd39a16d906c5395291ba65e9cac0d1ee (patch)
treefc3d3e0514fb11ead81fe42917b5d48a9fb28003
parent0d960384a4a51417c19f7b9e8f780df5570fd772 (diff)
Make mfii(4) bio(4) capable.
ok dlg
-rw-r--r--share/man/man4/mfii.48
-rw-r--r--sys/dev/pci/mfii.c758
2 files changed, 762 insertions, 4 deletions
diff --git a/share/man/man4/mfii.4 b/share/man/man4/mfii.4
index 53c9294d03c..7edff0583e5 100644
--- a/share/man/man4/mfii.4
+++ b/share/man/man4/mfii.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: mfii.4,v 1.4 2015/01/09 11:25:05 dlg Exp $
+.\" $OpenBSD: mfii.4,v 1.5 2016/10/24 03:45:48 yasuoka Exp $
.\"
.\" Copyright (c) 2012 David Gwynne <dlg@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: January 9 2015 $
+.Dd $Mdocdate: October 24 2016 $
.Dt MFII 4
.Os
.Sh NAME
@@ -44,10 +44,14 @@ Although the controllers are actual RAID controllers,
they appear as SCSI controllers.
All RAID configuration is done through the controllers' BIOSes.
.Sh SEE ALSO
+.Xr bio 4 ,
.Xr intro 4 ,
.Xr pci 4 ,
.Xr scsi 4 ,
.Xr sd 4
+.Xr bioctl 8 ,
+.Xr sensorsd 8 ,
+.Xr sysctl 8
.Sh HISTORY
The
.Nm
diff --git a/sys/dev/pci/mfii.c b/sys/dev/pci/mfii.c
index c880ed10a58..c09177a8402 100644
--- a/sys/dev/pci/mfii.c
+++ b/sys/dev/pci/mfii.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mfii.c,v 1.26 2016/10/24 03:11:58 yasuoka Exp $ */
+/* $OpenBSD: mfii.c,v 1.27 2016/10/24 03:45:48 yasuoka Exp $ */
/*
* Copyright (c) 2012 David Gwynne <dlg@openbsd.org>
@@ -25,7 +25,9 @@
#include <sys/dkio.h>
#include <sys/types.h>
#include <sys/pool.h>
+#include <sys/dkio.h>
+#include <dev/biovar.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcivar.h>
@@ -213,6 +215,13 @@ struct mfii_iop {
u_int8_t sge_flag_eol;
};
+struct mfii_cfg {
+ struct mfi_conf *cfg;
+ struct mfi_array *cfg_array;
+ struct mfi_ld_cfg *cfg_ld;
+ struct mfi_hotspare *cfg_hs;
+};
+
struct mfii_softc {
struct device sc_dev;
const struct mfii_iop *sc_iop;
@@ -251,6 +260,9 @@ struct mfii_softc {
struct scsi_iopool sc_iopool;
struct mfi_ctrl_info sc_info;
+
+ struct ksensor *sc_sensors;
+ struct ksensordev sc_sensordev;
};
int mfii_match(struct device *, void *, void *);
@@ -338,6 +350,24 @@ int mfii_pd_scsi_cmd_cdb(struct mfii_softc *,
struct scsi_xfer *);
int mfii_scsi_ioctl_cache(struct scsi_link *, u_int,
struct dk_cache *);
+#if NBIO > 0
+int mfii_ioctl(struct device *, u_long, caddr_t);
+int mfii_fill_cfg(struct mfii_softc *, struct mfii_cfg *);
+int mfii_ioctl_inq(struct mfii_softc *, struct bioc_inq *);
+int mfii_ioctl_vol(struct mfii_softc *, struct bioc_vol *);
+int mfii_ioctl_disk(struct mfii_softc *,
+ struct bioc_disk *);
+int mfii_ioctl_alarm(struct mfii_softc *,
+ struct bioc_alarm *);
+int mfii_ioctl_blink(struct mfii_softc *,
+ struct bioc_blink *);
+int mfii_ioctl_setstate(struct mfii_softc *,
+ struct bioc_setstate *);
+int mfii_ioctl_patrol(struct mfii_softc *,
+ struct bioc_patrol *);
+int mfii_create_sensors(struct mfii_softc *);
+void mfii_refresh_sensors(void *);
+#endif
#define mfii_fw_state(_sc) mfii_read((_sc), MFI_OSP)
@@ -510,7 +540,8 @@ mfii_attach(struct device *parent, struct device *self, void *aux)
memset(&saa, 0, sizeof(saa));
saa.saa_sc_link = &sc->sc_link;
- config_found(&sc->sc_dev, &saa, scsiprint);
+ sc->sc_scsibus = (struct scsibus_softc *)
+ config_found(&sc->sc_dev, &saa, scsiprint);
mfii_syspd(sc);
@@ -518,6 +549,16 @@ mfii_attach(struct device *parent, struct device *self, void *aux)
mfii_write(sc, MFI_OSTS, 0xffffffff);
mfii_write(sc, MFI_OMSK, ~MFII_OSTS_INTR_VALID);
+#if NBIO > 0
+ if (bio_register(&sc->sc_dev, mfii_ioctl) != 0)
+ panic("%s: controller registration failed", DEVNAME(sc));
+
+#ifndef SMALL_KERNEL
+ if (mfii_create_sensors(sc) != 0)
+ printf("%s: unable to create sensors\n", DEVNAME(sc));
+#endif
+#endif /* NBIO > 0 */
+
return;
free_sgl:
mfii_dmamem_free(sc, sc->sc_sgl);
@@ -625,6 +666,10 @@ mfii_detach(struct device *self, int flags)
if (sc->sc_ih == NULL)
return (0);
+ if (sc->sc_sensors) {
+ sensordev_deinstall(&sc->sc_sensordev);
+ free(sc->sc_sensors, M_DEVBUF, sc->sc_info.mci_lds_present);
+ }
pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
mfii_dmamem_free(sc, sc->sc_sgl);
mfii_dmamem_free(sc, sc->sc_requests);
@@ -1069,6 +1114,10 @@ mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
hdr->mfh_flags = htole16(MFI_FRAME_DIR_WRITE);
memcpy(dma_buf, buf, len);
break;
+ default:
+ ccb->ccb_direction = MFII_DATA_NONE;
+ hdr->mfh_flags = htole16(MFI_FRAME_DIR_NONE);
+ break;
}
if (mfii_load_mfa(sc, ccb, &dcmd->mdf_sgl,
@@ -1809,7 +1858,11 @@ mfii_scsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag)
return mfii_scsi_ioctl_cache(link, cmd,
(struct dk_cache *)addr);
default:
+#if NBIO > 0
+ return mfii_ioctl(link->adapter_softc, cmd, addr);
+#else
break;
+#endif
}
return (ENOTTY);
}
@@ -1884,3 +1937,704 @@ mfii_scsi_ioctl_cache(struct scsi_link *link, u_int cmd, struct dk_cache *dc)
return (rv);
}
+
+#if NBIO > 0
+int
+mfii_ioctl(struct device *dev, u_long cmd, caddr_t addr)
+{
+ struct mfii_softc *sc = (struct mfii_softc *)dev;
+ int rv = ENOTTY;
+
+ switch (cmd) {
+ case BIOCINQ:
+ rv = mfii_ioctl_inq(sc, (struct bioc_inq *)addr);
+ break;
+ case BIOCVOL:
+ rv = mfii_ioctl_vol(sc, (struct bioc_vol *)addr);
+ break;
+ case BIOCDISK:
+ rv = mfii_ioctl_disk(sc, (struct bioc_disk *)addr);
+ break;
+ case BIOCALARM:
+ rv = mfii_ioctl_alarm(sc, (struct bioc_alarm *)addr);
+ break;
+ case BIOCBLINK:
+ rv = mfii_ioctl_blink(sc, (struct bioc_blink *)addr);
+ break;
+ case BIOCSETSTATE:
+ rv = mfii_ioctl_setstate(sc, (struct bioc_setstate *)addr);
+ break;
+ case BIOCPATROL:
+ rv = mfii_ioctl_patrol(sc, (struct bioc_patrol *)addr);
+ break;
+ }
+
+ return (rv);
+}
+
+int
+mfii_fill_cfg(struct mfii_softc *sc, struct mfii_cfg *cfg)
+{
+ int rv, mfc_size;
+ struct mfi_conf *mfc;
+ struct mfii_ccb *ccb;
+
+ mfc_size = sizeof(*mfc);
+ again:
+ mfc = malloc(mfc_size, M_TEMP, M_WAITOK | M_ZERO);
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_CONF_GET, NULL,
+ mfc, mfc_size, SCSI_DATA_IN|SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv == 0) {
+ mfc->mfc_size = letoh32(mfc->mfc_size);
+ mfc->mfc_no_array = letoh16(mfc->mfc_no_array);
+ mfc->mfc_array_size = letoh16(mfc->mfc_array_size);
+ mfc->mfc_no_ld = letoh16(mfc->mfc_no_ld);
+ mfc->mfc_ld_size = letoh16(mfc->mfc_ld_size);
+ mfc->mfc_no_hs = letoh16(mfc->mfc_no_hs);
+ mfc->mfc_hs_size = letoh16(mfc->mfc_hs_size);
+
+ if (mfc_size < mfc->mfc_size) {
+ mfc_size = mfc->mfc_size;
+ free(mfc, M_TEMP, mfc_size);
+ goto again;
+ }
+ /* remember allocated size for free() */
+ mfc->mfc_size = mfc_size;
+
+ cfg->cfg = mfc;
+ cfg->cfg_array = (struct mfi_array *)((caddr_t)mfc +
+ offsetof(struct mfi_conf, mfc_array));
+ cfg->cfg_ld = (struct mfi_ld_cfg *)((caddr_t)cfg->cfg_array +
+ mfc->mfc_array_size * mfc->mfc_no_array);
+ cfg->cfg_hs = (struct mfi_hotspare *)((caddr_t)cfg->cfg_ld +
+ mfc->mfc_ld_size * mfc->mfc_no_ld);
+
+ return (0);
+ }
+
+ free(mfc, M_TEMP, mfc_size);
+ return (rv);
+}
+
+int
+mfii_ioctl_inq(struct mfii_softc *sc, struct bioc_inq *bi)
+{
+ int rv;
+ struct mfii_cfg cfg = { .cfg = NULL };
+
+ rv = mfii_fill_cfg(sc, &cfg);
+ if (rv != 0)
+ return (rv);
+
+ bi->bi_novol = cfg.cfg->mfc_no_ld + cfg.cfg->mfc_no_hs;
+ bi->bi_nodisk = letoh16(sc->sc_info.mci_pd_disks_present);
+ strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev));
+
+ if (cfg.cfg != NULL)
+ free(cfg.cfg, M_TEMP, cfg.cfg->mfc_size);
+
+ return (0);
+}
+
+int
+mfii_ioctl_vol(struct mfii_softc *sc, struct bioc_vol *bv)
+{
+ int rv;
+ struct mfii_cfg cfg = { .cfg = NULL };
+ struct mfi_ld_cfg *ld;
+ struct mfi_ld_list *list = NULL;
+ struct scsi_link *link;
+ struct mfii_ccb *ccb;
+ uint8_t mbox[MFI_MBOX_SIZE];
+
+ if ((link = scsi_get_link(sc->sc_scsibus, bv->bv_volid, 0)) != NULL &&
+ link->device_softc != NULL) {
+ struct device *dev = link->device_softc;
+ strlcpy(bv->bv_dev, dev->dv_xname, sizeof(bv->bv_dev));
+ }
+ rv = mfii_fill_cfg(sc, &cfg);
+ if (rv != 0)
+ goto done;
+
+ if (bv->bv_volid >= cfg.cfg->mfc_no_ld) {
+ int hsid;
+ struct mfi_pd_details *pd;
+
+ hsid = bv->bv_volid - cfg.cfg->mfc_no_ld;
+ if (hsid >= cfg.cfg->mfc_no_hs)
+ return (EINVAL);
+
+ pd = malloc(sizeof(*pd), M_TEMP, M_WAITOK | M_ZERO);
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ memset(mbox, 0, sizeof(mbox));
+ *((uint16_t *)&mbox[0]) = cfg.cfg_hs[hsid].mhs_pd.mfp_id;
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_INFO, mbox,
+ pd, sizeof(*pd), SCSI_DATA_IN|SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv == 0) {
+ bv->bv_status = BIOC_SVONLINE;
+ bv->bv_size = letoh64(pd->mpd_size) * 512;
+ bv->bv_level = -1;
+ bv->bv_nodisk = 1;
+ }
+ free(pd, M_TEMP, sizeof(*pd));
+
+ goto done;
+ }
+
+ list = malloc(sizeof(*list), M_TEMP, M_WAITOK | M_ZERO);
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_GET_LIST, NULL,
+ list, sizeof(*list), SCSI_DATA_IN|SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ goto done;
+
+ if (bv->bv_volid >= letoh32(list->mll_no_ld)) {
+ rv = EINVAL;
+ goto done;
+ }
+
+ switch (list->mll_list[bv->bv_volid].mll_state) {
+ case MFI_LD_OFFLINE:
+ bv->bv_status = BIOC_SVOFFLINE;
+ break;
+ case MFI_LD_PART_DEGRADED:
+ case MFI_LD_DEGRADED:
+ bv->bv_status = BIOC_SVDEGRADED;
+ break;
+ case MFI_LD_ONLINE:
+ bv->bv_status = BIOC_SVONLINE;
+ break;
+ default:
+ bv->bv_status = BIOC_SVINVALID;
+ break;
+ }
+ bv->bv_size = letoh64(list->mll_list[bv->bv_volid].mll_size) * 512;
+
+ ld = cfg.cfg->mfc_ld + bv->bv_volid;
+ bv->bv_cache =
+ (ld->mlc_prop.mlp_cur_cache_policy & MR_LD_CACHE_WRITE_BACK)
+ ? BIOC_CVWRITEBACK : BIOC_CVWRITETHROUGH;
+
+ switch (ld->mlc_parm.mpa_pri_raid) {
+ case MFI_DDF_PRL_RAID0:
+ bv->bv_level = 0;
+ break;
+ case MFI_DDF_PRL_RAID1:
+ case MFI_DDF_PRL_RAID1E:
+ bv->bv_level = 1;
+ break;
+ case MFI_DDF_PRL_RAID3:
+ bv->bv_level = 3;
+ break;
+ case MFI_DDF_PRL_RAID4:
+ bv->bv_level = 4;
+ break;
+ case MFI_DDF_PRL_RAID5:
+ case MFI_DDF_PRL_RAID5E:
+ case MFI_DDF_PRL_RAID5EE:
+ bv->bv_level = 5;
+ break;
+ case MFI_DDF_PRL_RAID6:
+ bv->bv_level = 6;
+ break;
+ case MFI_DDF_PRL_JBOD:
+ case MFI_DDF_PRL_CONCAT:
+ default:
+ bv->bv_level = 0;
+ break;
+ }
+ bv->bv_nodisk =
+ ld->mlc_parm.mpa_no_drv_per_span * ld->mlc_parm.mpa_span_depth;
+ done:
+ free(list, M_TEMP, sizeof(*list));
+ if (cfg.cfg != NULL)
+ free(cfg.cfg, M_TEMP, cfg.cfg->mfc_size);
+
+ return (rv);
+}
+
+int
+mfii_ioctl_disk(struct mfii_softc *sc, struct bioc_disk *bd)
+{
+ int rv, spanidx, diskidx, arrayidx, pdidx;
+ struct mfii_cfg cfg = { .cfg = NULL };
+ struct mfi_ld_cfg *ld;
+ struct mfii_ccb *ccb;
+ struct scsi_inquiry_data *inq;
+ struct mfi_pd_details *pd_det = NULL;
+ uint8_t mbox[MFI_MBOX_SIZE];
+
+ rv = mfii_fill_cfg(sc, &cfg);
+ if (rv != 0)
+ goto done;
+
+ if (bd->bd_volid >= cfg.cfg->mfc_no_ld) {
+ int hsid = bd->bd_volid - cfg.cfg->mfc_no_ld;
+ if (hsid >= cfg.cfg->mfc_no_hs) {
+ rv = EINVAL;
+ goto done;
+ }
+ pdidx = letoh16(cfg.cfg_hs[hsid].mhs_pd.mfp_id);
+ } else {
+ ld = cfg.cfg->mfc_ld + bd->bd_volid;
+ spanidx = bd->bd_diskid / ld->mlc_parm.mpa_no_drv_per_span;
+ diskidx = bd->bd_diskid % ld->mlc_parm.mpa_no_drv_per_span;
+ if (spanidx < 0 || MFI_MAX_SPAN <= spanidx) {
+ rv = EINVAL;
+ goto done;
+ }
+ arrayidx =
+ letoh16(ld[bd->bd_volid].mlc_span[spanidx].mls_index);
+ if (arrayidx < 0 || cfg.cfg->mfc_no_array <= arrayidx) {
+ rv = EINVAL;
+ goto done;
+ }
+ pdidx = letoh16(
+ cfg.cfg->mfc_array[arrayidx].pd[diskidx].mar_pd.mfp_id);
+ }
+
+ memset(mbox, 0, sizeof(mbox));
+ *((uint16_t *)&mbox[0]) = htole16(pdidx);
+
+ pd_det = malloc(sizeof(*pd_det), M_TEMP, M_WAITOK | M_ZERO);
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_INFO, mbox,
+ pd_det, sizeof(*pd_det), SCSI_DATA_IN|SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ goto done;
+
+ bd->bd_channel = pd_det->mpd_enc_idx;
+ bd->bd_target = pd_det->mpd_enc_slot;
+
+ switch (letoh16(pd_det->mpd_fw_state)) {
+ case MFI_PD_UNCONFIG_GOOD:
+ bd->bd_status = BIOC_SDUNUSED;
+ break;
+ case MFI_PD_UNCONFIG_BAD:
+ bd->bd_status = BIOC_SDINVALID;
+ break;
+ case MFI_PD_HOTSPARE:
+ bd->bd_status = BIOC_SDHOTSPARE;
+ break;
+ case MFI_PD_OFFLINE:
+ bd->bd_status = BIOC_SDOFFLINE;
+ break;
+ case MFI_PD_FAILED:
+ bd->bd_status = BIOC_SDFAILED;
+ break;
+ case MFI_PD_REBUILD:
+ bd->bd_status = BIOC_SDREBUILD;
+ break;
+ case MFI_PD_ONLINE:
+ bd->bd_status = BIOC_SDONLINE;
+ break;
+ case MFI_PD_COPYBACK:
+ case MFI_PD_SYSTEM:
+ bd->bd_status = BIOC_SDINVALID;
+ break;
+ }
+ bd->bd_size = letoh64(pd_det->mpd_size) * 512;
+
+ inq = (struct scsi_inquiry_data *)pd_det->mpd_inq_data;
+
+ memset(bd->bd_vendor, 0, sizeof(bd->bd_vendor));
+ memcpy(bd->bd_vendor, inq->vendor,
+ MIN(sizeof(bd->bd_vendor) - 1, sizeof(inq->vendor)));
+
+ rv = 0;
+ done:
+ free(pd_det, M_TEMP, sizeof(*pd_det));
+ if (cfg.cfg != NULL)
+ free(cfg.cfg, M_TEMP, cfg.cfg->mfc_size);
+
+ return (rv);
+}
+
+int
+mfii_ioctl_setstate(struct mfii_softc *sc, struct bioc_setstate *bs)
+{
+ int rv, i;
+ struct mfii_ccb *ccb;
+ struct mfi_pd_list *list = NULL;
+ struct mfi_pd_details *pd = NULL;
+ uint8_t mbox[MFI_MBOX_SIZE];
+
+ list = malloc(sizeof(*list), M_TEMP, M_WAITOK | M_ZERO);
+ pd = malloc(sizeof(*pd), M_TEMP, M_WAITOK | M_ZERO);
+
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_LIST, NULL,
+ list, sizeof(*list), SCSI_DATA_IN | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ goto done;
+
+ for (i = 0; i < letoh16(list->mpl_no_pd); i++)
+ if (list->mpl_address[i].mpa_enc_index == bs->bs_channel &&
+ list->mpl_address[i].mpa_enc_slot == bs->bs_target)
+ break;
+ if (i >= letoh16(list->mpl_no_pd)) {
+ rv = EINVAL;
+ goto done;
+ }
+
+ memset(mbox, 0, sizeof(mbox));
+ *((uint16_t *)&mbox[0]) = list->mpl_address[i].mpa_pd_id;
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_INFO, mbox,
+ pd, sizeof(*pd), SCSI_DATA_IN | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ goto done;
+
+ memset(mbox, 0, sizeof(mbox));
+ *((uint16_t *)&mbox[0]) = pd->mpd_pd.mfp_id;
+ *((uint16_t *)&mbox[2]) = pd->mpd_pd.mfp_seq;
+
+ switch (bs->bs_status) {
+ case BIOC_SSONLINE:
+ *((uint16_t *)&mbox[4]) = htole16(MFI_PD_ONLINE);
+ break;
+ case BIOC_SSOFFLINE:
+ *((uint16_t *)&mbox[4]) = htole16(MFI_PD_OFFLINE);
+ break;
+ case BIOC_SSHOTSPARE:
+ *((uint16_t *)&mbox[4]) = htole16(MFI_PD_HOTSPARE);
+ break;
+ case BIOC_SSREBUILD:
+ *((uint16_t *)&mbox[4]) = htole16(MFI_PD_REBUILD);
+ break;
+ default:
+ rv = EINVAL;
+ goto done;
+ }
+
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_SET_STATE, mbox,
+ NULL, 0, SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+
+ done:
+ free(list, M_TEMP, sizeof(*list));
+ free(pd, M_TEMP, sizeof(*pd));
+
+ return (rv);
+}
+
+int
+mfii_ioctl_alarm(struct mfii_softc *sc, struct bioc_alarm *ba)
+{
+ struct mfii_ccb *ccb;
+ u_char spkr;
+ int rv, cmd, flags = 0;
+
+ if (!ISSET(letoh32(sc->sc_info.mci_hw_present), MFI_INFO_HW_ALARM))
+ return (ENXIO);
+
+ switch (ba->ba_status) {
+ case BIOC_SADISABLE:
+ cmd = MR_DCMD_SPEAKER_DISABLE;
+ break;
+ case BIOC_SAENABLE:
+ cmd = MR_DCMD_SPEAKER_ENABLE;
+ break;
+ case BIOC_SASILENCE:
+ cmd = MR_DCMD_SPEAKER_SILENCE;
+ break;
+ case BIOC_GASTATUS:
+ cmd = MR_DCMD_SPEAKER_GET;
+ flags = SCSI_DATA_IN;
+ break;
+ case BIOC_SATEST:
+ cmd = MR_DCMD_SPEAKER_TEST;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_SET_STATE, NULL,
+ &spkr, sizeof(spkr), flags | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ return (rv);
+
+ ba->ba_status = (ba->ba_status == BIOC_GASTATUS)? spkr : 0;
+
+ return (rv);
+}
+
+int
+mfii_ioctl_blink(struct mfii_softc *sc, struct bioc_blink *bb)
+{
+ struct mfi_pd_list *list = NULL;
+ struct mfii_ccb *ccb;
+ uint8_t mbox[MFI_MBOX_SIZE];
+ int rv, i, cmd;
+
+ list = malloc(sizeof(*list), M_TEMP, M_WAITOK | M_ZERO);
+
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_LIST, NULL,
+ list, sizeof(*list), SCSI_DATA_IN | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ goto done;
+
+ for (i = 0; i < letoh16(list->mpl_no_pd); i++)
+ if (list->mpl_address[i].mpa_enc_index == bb->bb_channel &&
+ list->mpl_address[i].mpa_enc_slot == bb->bb_target)
+ break;
+ if (i >= letoh16(list->mpl_no_pd)) {
+ rv = EINVAL;
+ goto done;
+ }
+
+ memset(mbox, 0, sizeof(mbox));
+ *((uint16_t *)&mbox[0]) = list->mpl_address[i].mpa_pd_id;
+
+ switch (bb->bb_status) {
+ case BIOC_SBUNBLINK:
+ cmd = MR_DCMD_PD_UNBLINK;
+ break;
+ case BIOC_SBBLINK:
+ case BIOC_SBALARM:
+ cmd = MR_DCMD_PD_BLINK;
+ break;
+ default:
+ rv = EINVAL;
+ goto done;
+ }
+
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, cmd, NULL, NULL, 0, SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+
+ done:
+ free(list, M_TEMP, sizeof(*list));
+
+ return (ENOTTY);
+}
+
+int
+mfii_ioctl_patrol(struct mfii_softc *sc, struct bioc_patrol *bp)
+{
+ int rv = EINVAL, cmd;
+ struct mfii_ccb *ccb;
+ struct mfi_pr_properties prop;
+ struct mfi_pr_status status;
+ uint32_t time;
+
+ switch (bp->bp_opcode) {
+ case BIOC_SPSTOP:
+ case BIOC_SPSTART:
+ cmd = (bp->bp_opcode == BIOC_SPSTART)
+ ? MR_DCMD_PR_START : MR_DCMD_PR_STOP;
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, cmd, NULL, NULL, 0, SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ break;
+
+ case BIOC_GPSTATUS:
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_GET_PROPERTIES, NULL,
+ &prop, sizeof(prop), SCSI_DATA_IN | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ break;
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_GET_STATUS, NULL,
+ &status, sizeof(status), SCSI_DATA_IN | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ break;
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_TIME_SECS_GET, NULL,
+ &time, sizeof(time), SCSI_DATA_IN | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ break;
+ time = letoh32(time);
+
+ switch (prop.op_mode) {
+ case MFI_PR_OPMODE_AUTO:
+ bp->bp_mode = BIOC_SPMAUTO;
+ bp->bp_autoival = letoh32(prop.exec_freq);
+ bp->bp_autonext = letoh32(prop.next_exec);
+ bp->bp_autonow = time;
+ break;
+ case MFI_PR_OPMODE_MANUAL:
+ bp->bp_mode = BIOC_SPMMANUAL;
+ break;
+ case MFI_PR_OPMODE_DISABLED:
+ bp->bp_mode = BIOC_SPMDISABLED;
+ break;
+ }
+
+ switch (status.state) {
+ case MFI_PR_STATE_STOPPED:
+ bp->bp_status = BIOC_SPSSTOPPED;
+ break;
+ case MFI_PR_STATE_READY:
+ bp->bp_status = BIOC_SPSREADY;
+ break;
+ case MFI_PR_STATE_ACTIVE:
+ bp->bp_status = BIOC_SPSACTIVE;
+ break;
+ case MFI_PR_STATE_ABORTED:
+ bp->bp_status = BIOC_SPSABORTED;
+ break;
+ }
+ break;
+
+ case BIOC_SPDISABLE:
+ case BIOC_SPMANUAL:
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_GET_PROPERTIES, NULL,
+ &prop, sizeof(prop), SCSI_DATA_IN | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ break;
+ prop.op_mode = (bp->bp_opcode == BIOC_SPDISABLE)
+ ? MFI_PR_OPMODE_DISABLED : MFI_PR_OPMODE_MANUAL;
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_SET_PROPERTIES, NULL,
+ &prop, sizeof(prop), SCSI_DATA_OUT | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ break;
+
+ case BIOC_SPAUTO:
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_GET_PROPERTIES, NULL,
+ &prop, sizeof(prop), SCSI_DATA_IN | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ break;
+ prop.op_mode = MFI_PR_OPMODE_AUTO;
+
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_TIME_SECS_GET, NULL,
+ &time, sizeof(time), SCSI_DATA_IN | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ if (rv != 0)
+ break;
+ time = letoh32(time);
+ if (bp->bp_autoival != 0) {
+ if (bp->bp_autoival == -1)
+ prop.exec_freq = htole32(0xffffffffUL);
+ else if (bp->bp_autoival > 0)
+ prop.exec_freq = htole32(bp->bp_autoival);
+ else {
+ rv = EINVAL;
+ break;
+ }
+ }
+ if (bp->bp_autonext != 0) {
+ if (bp->bp_autonext > 0)
+ prop.next_exec =
+ htole32(time + bp->bp_autonext);
+ else {
+ rv = EINVAL;
+ break;
+ }
+ }
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_SET_PROPERTIES, NULL,
+ &prop, sizeof(prop), SCSI_DATA_OUT | SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+ break;
+ }
+
+ return (rv);
+}
+
+#ifndef SMALL_KERNEL
+int
+mfii_create_sensors(struct mfii_softc *sc)
+{
+ int i, no_ld;
+ struct device *dev;
+ struct scsi_link *link;
+
+ no_ld = letoh16(sc->sc_info.mci_lds_present);
+
+ strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
+ sizeof(sc->sc_sensordev.xname));
+
+ sc->sc_sensors = mallocarray(no_ld, sizeof(struct ksensor),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->sc_sensors == NULL)
+ return (-1);
+
+ for (i = 0; i < no_ld; i++) {
+ if ((link = scsi_get_link(sc->sc_scsibus, i, 0)) == NULL ||
+ link->device_softc == NULL)
+ goto err;
+
+ dev = link->device_softc;
+ sc->sc_sensors[i].type = SENSOR_DRIVE;
+ sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
+ strlcpy(sc->sc_sensors[i].desc, dev->dv_xname,
+ sizeof(sc->sc_sensors[i].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
+ }
+
+ if (sensor_task_register(sc, mfii_refresh_sensors, 10) == NULL)
+ goto err;
+
+ sensordev_install(&sc->sc_sensordev);
+
+ return (0);
+ err:
+ free(sc->sc_sensors, M_DEVBUF, no_ld);
+
+ return (-1);
+}
+
+void
+mfii_refresh_sensors(void *arg)
+{
+ int i, rv;
+ struct mfi_ld_list *list = NULL;
+ struct mfii_softc *sc = arg;
+ struct mfii_ccb *ccb;
+
+ list = malloc(sizeof(*list), M_TEMP, M_WAITOK | M_ZERO);
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_GET_LIST, NULL,
+ list, sizeof(*list), SCSI_DATA_IN|SCSI_NOSLEEP);
+ scsi_io_put(&sc->sc_iopool, ccb);
+
+ if (rv == 0) {
+ for (i = 0; i < letoh16(sc->sc_info.mci_lds_present); i++) {
+ switch (list->mll_list[i].mll_state) {
+ case MFI_LD_OFFLINE:
+ sc->sc_sensors[i].value = SENSOR_DRIVE_FAIL;
+ sc->sc_sensors[i].status = SENSOR_S_CRIT;
+ break;
+ case MFI_LD_PART_DEGRADED:
+ case MFI_LD_DEGRADED:
+ sc->sc_sensors[i].value = SENSOR_DRIVE_PFAIL;
+ sc->sc_sensors[i].status = SENSOR_S_WARN;
+ break;
+ case MFI_LD_ONLINE:
+ sc->sc_sensors[i].value = SENSOR_DRIVE_ONLINE;
+ sc->sc_sensors[i].status = SENSOR_S_OK;
+ break;
+ default:
+ sc->sc_sensors[i].value = 0; /* unknown */
+ sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
+ break;
+ }
+ }
+ }
+
+ free(list, M_TEMP, sizeof(*list));
+}
+#endif
+#endif