Hi,

I'm working on making mfii(4) bio(4) capable.

If you have a machine which has mfii(4), I'd like you to test the diff
following.  (It might be risky for production machines for this
moment.)

After the diff applied, bioctl(8) against the disk (eg. sd0) starts
working and also "sysctl hw.sensors.mfii0" will appear.

Especially if you can configure a hotspare, testing it is very
helpful for me since I can't use a hotspare on my test machine.

Index: sys/dev/pci//mfii.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/mfii.c,v
retrieving revision 1.28
diff -u -p -r1.28 mfii.c
--- sys/dev/pci//mfii.c 24 Oct 2016 05:27:52 -0000      1.28
+++ sys/dev/pci//mfii.c 25 Oct 2016 07:39:12 -0000
@@ -22,9 +22,12 @@
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/device.h>
+#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>
 
@@ -212,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;
@@ -250,11 +260,15 @@ 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 *);
 void           mfii_attach(struct device *, struct device *, void *);
 int            mfii_detach(struct device *, int);
+int            mfii_scsi_ioctl(struct scsi_link *, u_long, caddr_t, int);
 
 struct cfattach mfii_ca = {
        sizeof(struct mfii_softc),
@@ -277,7 +291,7 @@ struct scsi_adapter mfii_switch = {
        scsi_minphys,
        NULL, /* probe */
        NULL, /* unprobe */
-       NULL  /* ioctl */
+       mfii_scsi_ioctl
 };
 
 void           mfii_pd_scsi_cmd(struct scsi_xfer *);
@@ -334,7 +348,26 @@ int                        mfii_scsi_cmd_cdb(struct 
mfii_soft
                            struct scsi_xfer *);
 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)
 
@@ -506,7 +539,8 @@ mfii_attach(struct device *parent, struc
        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);
 
@@ -514,6 +548,16 @@ mfii_attach(struct device *parent, struc
        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);
@@ -621,6 +665,10 @@ mfii_detach(struct device *self, int fla
        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);
@@ -1065,6 +1113,10 @@ mfii_mgmt(struct mfii_softc *sc, struct 
                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,
@@ -1796,3 +1848,792 @@ destroy:
        return (1);
 }
 
+int
+mfii_scsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag)
+{
+       switch (cmd) {
+       case DIOCGCACHE:
+       case DIOCSCACHE:
+               return mfii_scsi_ioctl_cache(link, cmd,
+                   (struct dk_cache *)addr);
+               break;
+       default:
+#if NBIO > 0
+               return mfii_ioctl(link->adapter_softc, cmd, addr);
+#endif
+               break;
+       }
+       return (ENOTTY);
+}
+
+int
+mfii_scsi_ioctl_cache(struct scsi_link *link, u_int cmd, struct dk_cache *dc)
+{
+       struct mfii_softc *sc = (struct mfii_softc *)link->adapter_softc;
+       struct mfi_ld_prop ldp;
+       uint8_t mbox[MFI_MBOX_SIZE];
+       struct mfii_ccb *ccb;
+       int rv, wrenable, rdenable;
+
+       memset(mbox, 0, sizeof(mbox));
+       *((uint16_t *)&mbox[0]) = htole16(link->target);
+       ccb = scsi_io_get(&sc->sc_iopool, 0);
+       rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_GET_PROPERTIES, mbox,
+           &ldp, sizeof(ldp), SCSI_DATA_IN|SCSI_NOSLEEP);
+       scsi_io_put(&sc->sc_iopool, ccb);
+       if (rv != 0)
+               return (rv);
+
+       if (letoh16(sc->sc_info.mci_memory_size) > 0) {
+               wrenable = ISSET(ldp.mlp_cur_cache_policy,
+                   MR_LD_CACHE_ALLOW_WRITE_CACHE)? 1 : 0;
+               rdenable = ISSET(ldp.mlp_cur_cache_policy,
+                   MR_LD_CACHE_ALLOW_READ_CACHE)? 1 : 0;
+       } else {
+               wrenable = ISSET(ldp.mlp_diskcache_policy,
+                   MR_LD_DISK_CACHE_ENABLE)? 1 : 0;
+               rdenable = 0;
+       }
+
+       if (cmd == DIOCGCACHE) {
+               dc->wrcache = wrenable;
+               dc->rdcache = rdenable;
+               return (0);
+       }
+       if (((dc->wrcache) ? 1 : 0) == wrenable &&
+           ((dc->rdcache) ? 1 : 0) == rdenable)
+               return (0);
+
+       mbox[0] = ldp.mlp_ld.mld_target;
+       mbox[1] = ldp.mlp_ld.mld_res;
+       *(uint16_t *)&mbox[2] = ldp.mlp_ld.mld_seq;
+       if (letoh16(sc->sc_info.mci_memory_size) > 0) {
+               if (dc->rdcache)
+                       SET(ldp.mlp_cur_cache_policy,
+                           MR_LD_CACHE_ALLOW_READ_CACHE);
+               else
+                       CLR(ldp.mlp_cur_cache_policy,
+                           MR_LD_CACHE_ALLOW_READ_CACHE);
+               if (dc->wrcache)
+                       SET(ldp.mlp_cur_cache_policy,
+                           MR_LD_CACHE_ALLOW_WRITE_CACHE);
+               else
+                       CLR(ldp.mlp_cur_cache_policy,
+                           MR_LD_CACHE_ALLOW_WRITE_CACHE);
+       } else {
+               if (dc->rdcache)
+                       return (EOPNOTSUPP);
+               if (dc->wrcache)
+                       ldp.mlp_diskcache_policy = MR_LD_DISK_CACHE_ENABLE;
+               else
+                       ldp.mlp_diskcache_policy = MR_LD_DISK_CACHE_DISABLE;
+       }
+
+       ccb = scsi_io_get(&sc->sc_iopool, 0);
+       rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_SET_PROPERTIES, mbox,
+           &ldp, sizeof(ldp), SCSI_DATA_OUT|SCSI_NOSLEEP);
+       scsi_io_put(&sc->sc_iopool, ccb);
+
+       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) {
+                       int tmp = mfc->mfc_size;
+                       free(mfc, M_TEMP, mfc_size);
+                       mfc_size = tmp;
+                       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_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_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->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

Reply via email to