Hi tech@,

This patch adds bio(4) support for mfii(4).
# with "mfii(4): use MFII_FUNCTION_PASSTHRU_IO for MFI commands"

most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).

--- sys/dev/ic/mfireg.h
+++ sys/dev/ic/mfireg.h
@@ -139,6 +139,9 @@
 #define MR_DCMD_CONF_GET                       0x04010000
 #define MR_DCMD_CFG_ADD                                0x04020000
 #define MR_DCMD_CFG_CLEAR                      0x04030000
+#define MR_DCMD_CFG_MAKE_SPARE                 0x04040000
+#define MR_DCMD_CFG_FOREIGN_SCAN               0x04060100
+#define MR_DCMD_CFG_FOREIGN_CLEAR              0x04060500
 #define MR_DCMD_BBU_GET_STATUS                 0x05010000
 #define MR_DCMD_BBU_GET_CAPACITY_INFO          0x05020000
 #define MR_DCMD_BBU_GET_DESIGN_INFO            0x05030000
@@ -1228,3 +1231,13 @@ struct mfi_pr_properties {
        uint32_t                exec_freq;
        uint32_t                clear_freq;
 } __packed;
+
+/* We currently don't know the full details of the following struct */
+struct mfii_foreign_scan_cfg {
+       char data[24];
+};
+
+struct mfii_foreign_scan_info {
+       uint32_t count; /* Number of foreign configs found */
+       struct mfii_foreign_scan_cfg cfgs[8];
+};
--- sys/dev/pci/mfii.c
+++ sys/dev/pci/mfii.c
@@ -22,10 +22,14 @@
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/device.h>
+#include <sys/dkio.h>
 #include <sys/pool.h>
 #include <sys/task.h>
 #include <sys/atomic.h>
+#include <sys/sensors.h>
+#include <sys/rwlock.h>
 
+#include <dev/biovar.h>
 #include <dev/pci/pcidevs.h>
 #include <dev/pci/pcivar.h>
 
@@ -307,7 +311,35 @@ struct mfii_softc {
        struct mfii_pd_softc    *sc_pd;
        struct scsi_iopool      sc_iopool;
 
+       /* save some useful information for logical drives that is missing
+        * in sc_ld_list
+        */
+       struct {
+               uint32_t        ld_present;
+               char            ld_dev[16];     /* device name sd? */
+       }                       sc_ld[MFI_MAX_LD];
+
+       /* scsi ioctl from sd device */
+       int                     (*sc_ioctl)(struct device *, u_long, caddr_t);
+
+       uint32_t                sc_ld_cnt;
+
+       /* bio */
+       struct mfi_conf         *sc_cfg;
        struct mfi_ctrl_info    sc_info;
+       struct mfi_ld_list      sc_ld_list;
+       struct mfi_ld_details   *sc_ld_details; /* array to all logical disks */
+       int                     sc_no_pd; /* used physical disks */
+       int                     sc_ld_sz; /* sizeof sc_ld_details */
+
+       /* mgmt lock */
+       struct rwlock           sc_lock;
+
+       /* sensors */
+       struct ksensordev       sc_sensordev;
+       struct ksensor          *sc_bbu;
+       struct ksensor          *sc_bbu_status;
+       struct ksensor          *sc_sensors;
 };
 
 #ifdef MFII_DEBUG
@@ -355,13 +387,15 @@ struct cfdriver mfii_cd = {
 
 void           mfii_scsi_cmd(struct scsi_xfer *);
 void           mfii_scsi_cmd_done(struct mfii_softc *, struct mfii_ccb *);
+int            mfii_scsi_ioctl(struct scsi_link *, u_long, caddr_t, int);
+int            mfii_ioctl_cache(struct scsi_link *, u_long, struct dk_cache *);
 
 struct scsi_adapter mfii_switch = {
        mfii_scsi_cmd,
        scsi_minphys,
        NULL, /* probe */
        NULL, /* unprobe */
-       NULL  /* ioctl */
+       mfii_scsi_ioctl
 };
 
 void           mfii_pd_scsi_cmd(struct scsi_xfer *);
@@ -409,9 +443,11 @@ int                        mfii_load_mfa(struct mfii_softc 
*, struct mfii_ccb *,
 
 int                    mfii_mfa_poll(struct mfii_softc *, struct mfii_ccb *);
 
-int                    mfii_mgmt(struct mfii_softc *, struct mfii_ccb *,
-                           u_int32_t, const union mfi_mbox *,
-                           void *, size_t, int);
+int                    mfii_mgmt(struct mfii_softc *, uint32_t, uint32_t,
+                           size_t, void *, const union mfi_mbox *);
+int                    mfii_do_mgmt(struct mfii_softc *, struct mfii_ccb *,
+                           uint32_t, uint32_t, size_t, void *,
+                           const union mfi_mbox *);
 void                   mfii_empty_done(struct mfii_softc *, struct mfii_ccb *);
 
 int                    mfii_scsi_cmd_io(struct mfii_softc *,
@@ -445,6 +481,42 @@ void                       mfii_aen_pd_remove(struct 
mfii_softc *,
 void                   mfii_aen_pd_state_change(struct mfii_softc *,
                            const struct mfi_evtarg_pd_state *);
 
+#if NBIO > 0
+int            mfii_ioctl(struct device *, u_long, caddr_t);
+int            mfii_bio_getitall(struct mfii_softc *);
+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 *sc, struct bioc_blink *);
+int            mfii_ioctl_setstate(struct mfii_softc *,
+                   struct bioc_setstate *);
+int            mfii_ioctl_patrol(struct mfii_softc *sc, struct bioc_patrol *);
+int            mfii_bio_hs(struct mfii_softc *, int, int, void *);
+
+#ifndef SMALL_KERNEL
+static const char *mfi_bbu_indicators[] = {
+       "pack missing",
+       "voltage low",
+       "temp high",
+       "charge active",
+       "discharge active",
+       "learn cycle req'd",
+       "learn cycle active",
+       "learn cycle failed",
+       "learn cycle timeout",
+       "I2C errors",
+       "replace pack",
+       "low capacity",
+       "periodic learn req'd"
+};
+
+int            mfii_create_sensors(struct mfii_softc *);
+void           mfii_refresh_sensors(void *);
+int            mfii_bbu(struct mfii_softc *);
+#endif /* SMALL_KERNEL */
+#endif /* NBIO > 0 */
+
 /*
  * mfii boards support asynchronous (and non-polled) completion of
  * dcmds by proxying them through a passthru mpii command that points
@@ -575,7 +647,7 @@ mfii_attach(struct device *parent, struct device *self, 
void *aux)
        pci_intr_handle_t ih;
        struct scsibus_attach_args saa;
        u_int32_t status, scpad2, scpad3;
-       int chain_frame_sz, nsge_in_io, nsge_in_chain;
+       int chain_frame_sz, nsge_in_io, nsge_in_chain, i;
 
        /* init sc */
        sc->sc_iop = mfii_find_iop(aux);
@@ -586,6 +658,8 @@ mfii_attach(struct device *parent, struct device *self, 
void *aux)
        mtx_init(&sc->sc_reply_postq_mtx, IPL_BIO);
        scsi_iopool_init(&sc->sc_iopool, sc, mfii_get_ccb, mfii_put_ccb);
 
+       rw_init(&sc->sc_lock, "mfii_lock");
+
        sc->sc_aen_ccb = NULL;
        task_set(&sc->sc_aen_task, mfii_aen, sc);
 
@@ -716,6 +790,10 @@ mfii_attach(struct device *parent, struct device *self, 
void *aux)
        if (sc->sc_ih == NULL)
                goto free_sgl;
 
+       sc->sc_ld_cnt = sc->sc_info.mci_lds_present;
+       for (i = 0; i < sc->sc_ld_cnt; i++)
+               sc->sc_ld[i].ld_present = 1;
+
        sc->sc_link.openings = sc->sc_max_cmds;
        sc->sc_link.adapter_softc = sc;
        sc->sc_link.adapter = &mfii_switch;
@@ -726,7 +804,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);
 
@@ -739,6 +818,18 @@ 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));
+       else
+               sc->sc_ioctl = mfii_ioctl;
+
+#ifndef SMALL_KERNEL
+       if (mfii_create_sensors(sc) != 0)
+               printf("%s: unable to create sensors\n", DEVNAME(sc));
+#endif
+#endif /* NBIO > 0 */
+
        return;
 intr_disestablish:
        pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
@@ -777,17 +868,14 @@ mfii_dev_handles_update(struct mfii_softc *sc)
 {
        struct mfii_ld_map *lm;
        uint16_t *dev_handles = NULL;
-       struct mfii_ccb *ccb;
        int i;
        int rv = 0;
 
        lm = malloc(sizeof(*lm), M_TEMP, M_WAITOK|M_ZERO);
-       ccb = scsi_io_get(&sc->sc_iopool, 0);
 
-       rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_MAP_GET_INFO, NULL,
-           lm, sizeof(*lm), SCSI_DATA_IN|SCSI_NOSLEEP);
+       rv = mfii_mgmt(sc, MR_DCMD_LD_MAP_GET_INFO, MFII_DATA_IN, sizeof(*lm),
+           lm, NULL);
 
-       scsi_io_put(&sc->sc_iopool, ccb);
        if (rv != 0) {
                rv = EIO;
                goto free_lm;
@@ -861,6 +949,23 @@ mfii_detach(struct device *self, int flags)
        if (sc->sc_ih == NULL)
                return (0);
 
+#ifndef SMALL_KERNEL
+       if (sc->sc_sensors) {
+               sensordev_deinstall(&sc->sc_sensordev);
+               free(sc->sc_sensors, M_DEVBUF,
+                   sc->sc_ld_cnt * sizeof(struct ksensor));
+       }
+
+       if (sc->sc_bbu) {
+               free(sc->sc_bbu, M_DEVBUF, 4 * sizeof(*sc->sc_bbu));
+       }
+
+       if (sc->sc_bbu_status) {
+               free(sc->sc_bbu_status, M_DEVBUF,
+                   sizeof(*sc->sc_bbu_status) * sizeof(mfi_bbu_indicators));
+       }
+#endif /* SMALL_KERNEL */
+
        mfii_aen_unregister(sc);
        pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
        mfii_dmamem_free(sc, sc->sc_sgl);
@@ -977,9 +1082,10 @@ mfii_aen_register(struct mfii_softc *sc)
        }
 
        memset(&mel, 0, sizeof(mel));
+       mfii_scrub_ccb(ccb);
 
-       rv = mfii_mgmt(sc, ccb, MR_DCMD_CTRL_EVENT_GET_INFO, NULL,
-           &mel, sizeof(mel), SCSI_DATA_IN|SCSI_NOSLEEP);
+       rv = mfii_do_mgmt(sc, ccb, MR_DCMD_CTRL_EVENT_GET_INFO, MFII_DATA_IN,
+           sizeof(mel), &mel, NULL);
        if (rv != 0) {
                scsi_io_put(&sc->sc_iopool, ccb);
                printf("%s: unable to get event info\n", DEVNAME(sc));
@@ -1222,13 +1328,10 @@ mfii_transition_firmware(struct mfii_softc *sc)
 int
 mfii_get_info(struct mfii_softc *sc)
 {
-       struct mfii_ccb *ccb;
        int rv;
 
-       ccb = scsi_io_get(&sc->sc_iopool, 0);
-       rv = mfii_mgmt(sc, ccb, MR_DCMD_CTRL_GET_INFO, NULL,
-           &sc->sc_info, sizeof(sc->sc_info), SCSI_DATA_IN|SCSI_NOSLEEP);
-       scsi_io_put(&sc->sc_iopool, ccb);
+       rv = mfii_mgmt(sc, MR_DCMD_CTRL_GET_INFO, MFII_DATA_IN,
+           sizeof(sc->sc_info), &sc->sc_info, NULL);
 
        if (rv != 0)
                return (rv);
@@ -1511,39 +1614,53 @@ mfii_exec_done(struct mfii_softc *sc, struct mfii_ccb 
*ccb)
 }
 
 int
-mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
-    u_int32_t opc, const union mfi_mbox *mbox, void *buf, size_t len,
-    int flags)
+mfii_mgmt(struct mfii_softc *sc, uint32_t opc, uint32_t dir, size_t len,
+    void *buf, const union mfi_mbox *mbox)
+{
+       struct mfii_ccb *ccb;
+       int rv;
+
+       ccb = scsi_io_get(&sc->sc_iopool, 0);
+       mfii_scrub_ccb(ccb);
+       rv = mfii_do_mgmt(sc, ccb, opc, dir, len, buf, mbox);
+       scsi_io_put(&sc->sc_iopool, ccb);
+
+       return (rv);
+}
+
+int
+mfii_do_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb, uint32_t opc,
+    uint32_t dir, size_t len, void *buf, const union mfi_mbox *mbox)
 {
        struct mpii_msg_scsi_io *io = ccb->ccb_request;
        struct mfii_raid_context *ctx = (struct mfii_raid_context *)(io + 1);
        struct mfii_sge *sge = (struct mfii_sge *)(ctx + 1);
        struct mfi_dcmd_frame *dcmd = ccb->ccb_mfi;
        struct mfi_frame_header *hdr = &dcmd->mdf_header;
-       u_int8_t *dma_buf;
+       uint8_t *dma_buf;
        int rv = 0;
 
        dma_buf = dma_alloc(len, PR_WAITOK);
        if (dma_buf == NULL)
                return (ENOMEM);
 
-       mfii_scrub_ccb(ccb);
        ccb->ccb_data = dma_buf;
        ccb->ccb_len = len;
-       switch (flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
-       case SCSI_DATA_IN:
-               ccb->ccb_direction = MFII_DATA_IN;
+       ccb->ccb_direction = dir;
+       switch (dir) {
+       case MFII_DATA_IN:
                hdr->mfh_flags = htole16(MFI_FRAME_DIR_READ);
                break;
-       case SCSI_DATA_OUT:
-               ccb->ccb_direction = MFII_DATA_OUT;
+       case MFII_DATA_OUT:
                hdr->mfh_flags = htole16(MFI_FRAME_DIR_WRITE);
                memcpy(dma_buf, buf, len);
                break;
+       case MFII_DATA_NONE:
+               hdr->mfh_flags = htole16(MFI_FRAME_DIR_NONE);
+               break;
        }
 
-       if (mfii_load_mfa(sc, ccb, &dcmd->mdf_sgl,
-           ISSET(flags, SCSI_NOSLEEP)) != 0) {
+       if (mfii_load_mfa(sc, ccb, &dcmd->mdf_sgl, 1/*cold*/) != 0) {
                rv = ENOMEM;
                goto done;
        }
@@ -1569,7 +1686,7 @@ mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
        ccb->ccb_req.scsi.flags = MFII_REQ_TYPE_SCSI;
        ccb->ccb_req.scsi.smid = letoh16(ccb->ccb_smid);
 
-       if (ISSET(flags, SCSI_NOSLEEP)) {
+       if (1/*cold*/) {
                /* busy-loop polling with done handler */
                ccb->ccb_cookie = NULL;
                ccb->ccb_done = mfii_empty_done;
@@ -1583,8 +1700,7 @@ mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
 
        if (ccb->ccb_len > 0) {
                bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap,
-                   0, ccb->ccb_dmamap->dm_mapsize,
-                   (ccb->ccb_direction == MFII_DATA_IN) ?
+                   0, ccb->ccb_dmamap->dm_mapsize, (dir == MFII_DATA_IN) ?
                    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
 
                bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap);
@@ -1592,7 +1708,7 @@ mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
 
        rv = hdr->mfh_cmd_status == MFI_STAT_OK ? 0 : 1;
 
-       if (rv == 0 && ccb->ccb_direction == MFII_DATA_IN)
+       if (rv == 0 && dir == MFII_DATA_IN)
                memcpy(buf, dma_buf, len);
 
 done:
@@ -1930,6 +2046,109 @@ mfii_scsi_cmd_done(struct mfii_softc *sc, struct 
mfii_ccb *ccb)
 }
 
 int
+mfii_scsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag)
+{
+       struct mfii_softc       *sc = (struct mfii_softc *)link->adapter_softc;
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_scsi_ioctl\n", DEVNAME(sc));
+
+       switch (cmd) {
+       case DIOCGCACHE:
+       case DIOCSCACHE:
+               return (mfii_ioctl_cache(link, cmd, (struct dk_cache *)addr));
+               break;
+
+       default:
+               if (sc->sc_ioctl)
+                       return (sc->sc_ioctl(link->adapter_softc, cmd, addr));
+               break;
+       }
+
+       return (ENOTTY);
+}
+
+int
+mfii_ioctl_cache(struct scsi_link *link, u_long cmd,  struct dk_cache *dc)
+{
+       struct mfii_softc       *sc = (struct mfii_softc *)link->adapter_softc;
+       int                      rv, wrenable, rdenable;
+       struct mfi_ld_prop       ldp;
+       union mfi_mbox           mbox;
+
+       if (mfii_get_info(sc)) {
+               rv = EIO;
+               goto done;
+       }
+
+       if (!sc->sc_ld[link->target].ld_present) {
+               rv = EIO;
+               goto done;
+       }
+
+       memset(&mbox, 0, sizeof(mbox));
+       mbox.b[0] = link->target;
+       rv = mfii_mgmt(sc, MR_DCMD_LD_GET_PROPERTIES, MFII_DATA_IN, sizeof(ldp),
+           &ldp, &mbox);
+       if (rv != 0)
+               goto done;
+
+       if (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;
+               goto done;
+       } /* else DIOCSCACHE */
+
+       if (((dc->wrcache) ? 1 : 0) == wrenable &&
+           ((dc->rdcache) ? 1 : 0) == rdenable)
+               goto done;
+
+       memset(&mbox, 0, sizeof(mbox));
+       mbox.b[0] = ldp.mlp_ld.mld_target;
+       mbox.b[1] = ldp.mlp_ld.mld_res;
+       mbox.s[1] = ldp.mlp_ld.mld_seq;
+
+       if (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) {
+                       rv = EOPNOTSUPP;
+                       goto done;
+               }
+               if (dc->wrcache)
+                       ldp.mlp_diskcache_policy = MR_LD_DISK_CACHE_ENABLE;
+               else
+                       ldp.mlp_diskcache_policy = MR_LD_DISK_CACHE_DISABLE;
+       }
+
+       rv = mfii_mgmt(sc, MR_DCMD_LD_SET_PROPERTIES, MFII_DATA_OUT,
+           sizeof(ldp), &ldp, &mbox);
+done:
+       return (rv);
+}
+
+int
 mfii_scsi_cmd_io(struct mfii_softc *sc, struct scsi_xfer *xs)
 {
        struct scsi_link *link = xs->sc_link;
@@ -2078,7 +2297,6 @@ int
 mfii_pd_scsi_probe(struct scsi_link *link)
 {
        struct mfii_softc *sc = link->adapter_softc;
-       struct mfii_ccb *ccb;
        struct mfi_pd_details mpd;
        union mfi_mbox mbox;
        int rv;
@@ -2089,10 +2307,8 @@ mfii_pd_scsi_probe(struct scsi_link *link)
        memset(&mbox, 0, sizeof(mbox));
        mbox.s[0] = htole16(link->target);
 
-       ccb = scsi_io_get(&sc->sc_iopool, 0);
-       rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_INFO, &mbox, &mpd, sizeof(mpd),
-           SCSI_DATA_IN|SCSI_NOSLEEP);
-       scsi_io_put(&sc->sc_iopool, ccb);
+       rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof(mpd), &mpd,
+           &mbox);
        if (rv != 0)
                return (EIO);
 
@@ -2427,3 +2643,1162 @@ destroy:
        return (1);
 }
 
+#if NBIO > 0
+int
+mfii_ioctl(struct device *dev, u_long cmd, caddr_t addr)
+{
+       struct mfii_softc       *sc = (struct mfii_softc *)dev;
+       int error = 0;
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl ", DEVNAME(sc));
+
+       rw_enter_write(&sc->sc_lock);
+
+       switch (cmd) {
+       case BIOCINQ:
+               DNPRINTF(MFII_D_IOCTL, "inq\n");
+               error = mfii_ioctl_inq(sc, (struct bioc_inq *)addr);
+               break;
+
+       case BIOCVOL:
+               DNPRINTF(MFII_D_IOCTL, "vol\n");
+               error = mfii_ioctl_vol(sc, (struct bioc_vol *)addr);
+               break;
+
+       case BIOCDISK:
+               DNPRINTF(MFII_D_IOCTL, "disk\n");
+               error = mfii_ioctl_disk(sc, (struct bioc_disk *)addr);
+               break;
+
+       case BIOCALARM:
+               DNPRINTF(MFII_D_IOCTL, "alarm\n");
+               error = mfii_ioctl_alarm(sc, (struct bioc_alarm *)addr);
+               break;
+
+       case BIOCBLINK:
+               DNPRINTF(MFII_D_IOCTL, "blink\n");
+               error = mfii_ioctl_blink(sc, (struct bioc_blink *)addr);
+               break;
+
+       case BIOCSETSTATE:
+               DNPRINTF(MFII_D_IOCTL, "setstate\n");
+               error = mfii_ioctl_setstate(sc, (struct bioc_setstate *)addr);
+               break;
+
+       case BIOCPATROL:
+               DNPRINTF(MFII_D_IOCTL, "patrol\n");
+               error = mfii_ioctl_patrol(sc, (struct bioc_patrol *)addr);
+               break;
+
+       default:
+               DNPRINTF(MFII_D_IOCTL, " invalid ioctl\n");
+               error = EINVAL;
+       }
+
+       rw_exit_write(&sc->sc_lock);
+
+       return (error);
+}
+
+int
+mfii_bio_getitall(struct mfii_softc *sc)
+{
+       int                     i, d, rv = EINVAL;
+       size_t                  size;
+       union mfi_mbox          mbox;
+       struct mfi_conf         *cfg = NULL;
+       struct mfi_ld_details   *ld_det = NULL;
+
+       /* get info */
+       if (mfii_get_info(sc)) {
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_get_info failed\n",
+                   DEVNAME(sc));
+               goto done;
+       }
+
+       /* send single element command to retrieve size for full structure */
+       cfg = malloc(sizeof *cfg, M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (cfg == NULL)
+               goto done;
+       if (mfii_mgmt(sc, MR_DCMD_CONF_GET, MFII_DATA_IN, sizeof *cfg, cfg,
+           NULL)) {
+               free(cfg, M_DEVBUF, sizeof *cfg);
+               goto done;
+       }
+
+       size = cfg->mfc_size;
+       free(cfg, M_DEVBUF, sizeof *cfg);
+
+       /* memory for read config */
+       cfg = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (cfg == NULL)
+               goto done;
+       if (mfii_mgmt(sc, MR_DCMD_CONF_GET, MFII_DATA_IN, size, cfg, NULL)) {
+               free(cfg, M_DEVBUF, size);
+               goto done;
+       }
+
+       /* replace current pointer with new one */
+       if (sc->sc_cfg)
+               free(sc->sc_cfg, M_DEVBUF, 0);
+       sc->sc_cfg = cfg;
+
+       /* get all ld info */
+       if (mfii_mgmt(sc, MR_DCMD_LD_GET_LIST, MFII_DATA_IN,
+           sizeof(sc->sc_ld_list), &sc->sc_ld_list, NULL))
+               goto done;
+
+       /* get memory for all ld structures */
+       size = cfg->mfc_no_ld * sizeof(struct mfi_ld_details);
+       if (sc->sc_ld_sz != size) {
+               if (sc->sc_ld_details)
+                       free(sc->sc_ld_details, M_DEVBUF, 0);
+
+               ld_det = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+               if (ld_det == NULL)
+                       goto done;
+               sc->sc_ld_sz = size;
+               sc->sc_ld_details = ld_det;
+       }
+
+       /* find used physical disks */
+       size = sizeof(struct mfi_ld_details);
+       for (i = 0, d = 0; i < cfg->mfc_no_ld; i++) {
+               memset(&mbox, 0, sizeof(mbox));
+               mbox.b[0] = sc->sc_ld_list.mll_list[i].mll_ld.mld_target;
+               if (mfii_mgmt(sc, MR_DCMD_LD_GET_INFO, MFII_DATA_IN, size,
+                   &sc->sc_ld_details[i], &mbox))
+                       goto done;
+
+               d += sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_no_drv_per_span *
+                   sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_span_depth;
+       }
+       sc->sc_no_pd = d;
+
+       rv = 0;
+done:
+       return (rv);
+}
+
+int
+mfii_ioctl_inq(struct mfii_softc *sc, struct bioc_inq *bi)
+{
+       int                     rv = EINVAL;
+       struct mfi_conf         *cfg = NULL;
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_inq\n", DEVNAME(sc));
+
+       if (mfii_bio_getitall(sc)) {
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_bio_getitall failed\n",
+                   DEVNAME(sc));
+               goto done;
+       }
+
+       /* count unused disks as volumes */
+       if (sc->sc_cfg == NULL)
+               goto done;
+       cfg = sc->sc_cfg;
+
+       bi->bi_nodisk = sc->sc_info.mci_pd_disks_present;
+       bi->bi_novol = cfg->mfc_no_ld + cfg->mfc_no_hs;
+#if notyet
+       bi->bi_novol = cfg->mfc_no_ld + cfg->mfc_no_hs +
+           (bi->bi_nodisk - sc->sc_no_pd);
+#endif
+       /* tell bio who we are */
+       strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev));
+
+       rv = 0;
+done:
+       return (rv);
+}
+
+int
+mfii_ioctl_vol(struct mfii_softc *sc, struct bioc_vol *bv)
+{
+       int                     i, per, rv = EINVAL;
+       struct scsi_link        *link;
+       struct device           *dev;
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_vol %#x\n",
+           DEVNAME(sc), bv->bv_volid);
+
+       /* we really could skip and expect that inq took care of it */
+       if (mfii_bio_getitall(sc)) {
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_bio_getitall failed\n",
+                   DEVNAME(sc));
+               goto done;
+       }
+
+       if (bv->bv_volid >= sc->sc_ld_list.mll_no_ld) {
+               /* go do hotspares & unused disks */
+               rv = mfii_bio_hs(sc, bv->bv_volid, MFI_MGMT_VD, bv);
+               goto done;
+       }
+
+       i = bv->bv_volid;
+       link = scsi_get_link(sc->sc_scsibus, i, 0);
+       if (link != NULL && link->device_softc != NULL) {
+               dev = link->device_softc;
+               strlcpy(bv->bv_dev, dev->dv_xname, sizeof(bv->bv_dev));
+       }
+
+       switch(sc->sc_ld_list.mll_list[i].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;
+               DNPRINTF(MFII_D_IOCTL, "%s: invalid logical disk state %#x\n",
+                   DEVNAME(sc),
+                   sc->sc_ld_list.mll_list[i].mll_state);
+       }
+
+       /* additional status can modify MFI status */
+       switch (sc->sc_ld_details[i].mld_progress.mlp_in_prog) {
+       case MFI_LD_PROG_CC:
+       case MFI_LD_PROG_BGI:
+               bv->bv_status = BIOC_SVSCRUB;
+               per = (int)sc->sc_ld_details[i].mld_progress.mlp_cc.mp_progress;
+               bv->bv_percent = (per * 100) / 0xffff;
+               bv->bv_seconds =
+                   sc->sc_ld_details[i].mld_progress.mlp_cc.mp_elapsed_seconds;
+               break;
+
+       case MFI_LD_PROG_FGI:
+       case MFI_LD_PROG_RECONSTRUCT:
+               /* nothing yet */
+               break;
+       }
+
+       if (sc->sc_ld_details[i].mld_cfg.mlc_prop.mlp_cur_cache_policy & 0x01)
+               bv->bv_cache = BIOC_CVWRITEBACK;
+       else
+               bv->bv_cache = BIOC_CVWRITETHROUGH;
+
+       /*
+        * The RAID levels are determined per the SNIA DDF spec, this is only
+        * a subset that is valid for the MFI controller.
+        */
+       bv->bv_level = sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_pri_raid;
+       if (sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_sec_raid ==
+           MFI_DDF_SRL_SPANNED)
+               bv->bv_level *= 10;
+
+       bv->bv_nodisk = 
sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_no_drv_per_span *
+           sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_span_depth;
+
+       bv->bv_size = sc->sc_ld_details[i].mld_size * 512; /* bytes per block */
+
+       rv = 0;
+done:
+       return (rv);
+}
+
+int
+mfii_ioctl_disk(struct mfii_softc *sc, struct bioc_disk *bd)
+{
+       struct mfi_conf         *cfg;
+       struct mfi_array        *ar;
+       struct mfi_ld_cfg       *ld;
+       struct mfi_pd_details   *pd;
+       struct mfi_pd_list      *pl;
+       struct mfi_pd_progress  *mfp;
+       struct mfi_progress     *mp;
+       struct scsi_inquiry_data *inqbuf;
+       char                    vend[8+16+4+1], *vendp;
+       int                     i, rv = EINVAL;
+       int                     arr, vol, disk, span;
+       union mfi_mbox          mbox;
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_disk %#x\n",
+           DEVNAME(sc), bd->bd_diskid);
+
+       /* we really could skip and expect that inq took care of it */
+       if (mfii_bio_getitall(sc)) {
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_bio_getitall failed\n",
+                   DEVNAME(sc));
+               return (rv);
+       }
+       cfg = sc->sc_cfg;
+
+       pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+       pl = malloc(sizeof *pl, M_DEVBUF, M_WAITOK);
+
+       ar = cfg->mfc_array;
+       vol = bd->bd_volid;
+       if (vol >= cfg->mfc_no_ld) {
+               /* do hotspares */
+               rv = mfii_bio_hs(sc, bd->bd_volid, MFI_MGMT_SD, bd);
+               goto freeme;
+       }
+
+       /* calculate offset to ld structure */
+       ld = (struct mfi_ld_cfg *)(
+           ((uint8_t *)cfg) + offsetof(struct mfi_conf, mfc_array) +
+           cfg->mfc_array_size * cfg->mfc_no_array);
+
+       /* use span 0 only when raid group is not spanned */
+       if (ld[vol].mlc_parm.mpa_span_depth > 1)
+               span = bd->bd_diskid / ld[vol].mlc_parm.mpa_no_drv_per_span;
+       else
+               span = 0;
+       arr = ld[vol].mlc_span[span].mls_index;
+
+       /* offset disk into pd list */
+       disk = bd->bd_diskid % ld[vol].mlc_parm.mpa_no_drv_per_span;
+
+       if (ar[arr].pd[disk].mar_pd.mfp_id == 0xffffU) {
+               /* disk is missing but succeed command */
+               bd->bd_status = BIOC_SDFAILED;
+               rv = 0;
+
+               /* try to find an unused disk for the target to rebuild */
+               if (mfii_mgmt(sc, MR_DCMD_PD_GET_LIST, MFII_DATA_IN,
+                   sizeof *pl, pl, NULL))
+                       goto freeme;
+
+               for (i = 0; i < pl->mpl_no_pd; i++) {
+                       if (pl->mpl_address[i].mpa_scsi_type != 0)
+                               continue;
+
+                       memset(&mbox, 0, sizeof(mbox));
+                       mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+                       if (mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+                           sizeof *pd, pd, &mbox))
+                               continue;
+
+                       if (pd->mpd_fw_state == MFI_PD_UNCONFIG_GOOD ||
+                           pd->mpd_fw_state == MFI_PD_UNCONFIG_BAD)
+                               break;
+               }
+
+               if (i == pl->mpl_no_pd)
+                       goto freeme;
+       } else {
+               memset(&mbox, 0, sizeof(mbox));
+               mbox.s[0] = ar[arr].pd[disk].mar_pd.mfp_id;
+               if (mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+                   sizeof *pd, pd, &mbox)) {
+                       bd->bd_status = BIOC_SDINVALID;
+                       goto freeme;
+               }
+       }
+
+       /* get the remaining fields */
+       bd->bd_channel = pd->mpd_enc_idx;
+       bd->bd_target = pd->mpd_enc_slot;
+
+       /* get status */
+       switch (pd->mpd_fw_state){
+       case MFI_PD_UNCONFIG_GOOD:
+       case MFI_PD_UNCONFIG_BAD:
+               bd->bd_status = BIOC_SDUNUSED;
+               break;
+
+       case MFI_PD_HOTSPARE: /* XXX dedicated hotspare part of array? */
+               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:
+       default:
+               bd->bd_status = BIOC_SDINVALID;
+               break;
+       }
+
+       bd->bd_size = pd->mpd_size * 512; /* bytes per block */
+
+       inqbuf = (struct scsi_inquiry_data *)&pd->mpd_inq_data;
+       vendp = inqbuf->vendor;
+       memcpy(vend, vendp, sizeof vend - 1);
+       vend[sizeof vend - 1] = '\0';
+       strlcpy(bd->bd_vendor, vend, sizeof(bd->bd_vendor));
+
+       /* XXX find a way to retrieve serial nr from drive */
+       /* XXX find a way to get bd_procdev */
+
+       mfp = &pd->mpd_progress;
+       if (mfp->mfp_in_prog & MFI_PD_PROG_PR) {
+               mp = &mfp->mfp_patrol_read;
+               bd->bd_patrol.bdp_percent = (mp->mp_progress * 100) / 0xffff;
+               bd->bd_patrol.bdp_seconds = mp->mp_elapsed_seconds;
+       }
+
+       rv = 0;
+freeme:
+       free(pd, M_DEVBUF, sizeof *pd);
+       free(pl, M_DEVBUF, sizeof *pl);
+
+       return (rv);
+}
+
+int
+mfii_ioctl_alarm(struct mfii_softc *sc, struct bioc_alarm *ba)
+{
+       uint32_t                opc, dir = MFII_DATA_NONE;
+       int                     rv = 0;
+       int8_t                  ret;
+
+       switch(ba->ba_opcode) {
+       case BIOC_SADISABLE:
+               opc = MR_DCMD_SPEAKER_DISABLE;
+               break;
+
+       case BIOC_SAENABLE:
+               opc = MR_DCMD_SPEAKER_ENABLE;
+               break;
+
+       case BIOC_SASILENCE:
+               opc = MR_DCMD_SPEAKER_SILENCE;
+               break;
+
+       case BIOC_GASTATUS:
+               opc = MR_DCMD_SPEAKER_GET;
+               dir = MFII_DATA_IN;
+               break;
+
+       case BIOC_SATEST:
+               opc = MR_DCMD_SPEAKER_TEST;
+               break;
+
+       default:
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_alarm biocalarm invalid "
+                   "opcode %x\n", DEVNAME(sc), ba->ba_opcode);
+               return (EINVAL);
+       }
+
+       if (mfii_mgmt(sc, opc, dir, sizeof(ret), &ret, NULL))
+               rv = EINVAL;
+       else
+               if (ba->ba_opcode == BIOC_GASTATUS)
+                       ba->ba_status = ret;
+               else
+                       ba->ba_status = 0;
+
+       return (rv);
+}
+
+int
+mfii_ioctl_blink(struct mfii_softc *sc, struct bioc_blink *bb)
+{
+       int                     i, found, rv = EINVAL;
+       union mfi_mbox          mbox;
+       uint32_t                cmd;
+       struct mfi_pd_list      *pd;
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_blink %x\n", DEVNAME(sc),
+           bb->bb_status);
+
+       /* channel 0 means not in an enclosure so can't be blinked */
+       if (bb->bb_channel == 0)
+               return (EINVAL);
+
+       pd = malloc(sizeof(*pd), M_DEVBUF, M_WAITOK);
+
+       if (mfii_mgmt(sc, MR_DCMD_PD_GET_LIST, MFII_DATA_IN,
+           sizeof(*pd), pd, NULL))
+               goto done;
+
+       for (i = 0, found = 0; i < pd->mpl_no_pd; i++)
+               if (bb->bb_channel == pd->mpl_address[i].mpa_enc_index &&
+                   bb->bb_target == pd->mpl_address[i].mpa_enc_slot) {
+                       found = 1;
+                       break;
+               }
+
+       if (!found)
+               goto done;
+
+       memset(&mbox, 0, sizeof(mbox));
+       mbox.s[0] = pd->mpl_address[i].mpa_pd_id;
+
+       switch (bb->bb_status) {
+       case BIOC_SBUNBLINK:
+               cmd = MR_DCMD_PD_UNBLINK;
+               break;
+
+       case BIOC_SBBLINK:
+               cmd = MR_DCMD_PD_BLINK;
+               break;
+
+       case BIOC_SBALARM:
+       default:
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_blink biocblink invalid "
+                   "opcode %x\n", DEVNAME(sc), bb->bb_status);
+               goto done;
+       }
+
+
+       if (mfii_mgmt(sc, cmd, MFII_DATA_NONE, 0, NULL, &mbox))
+               goto done;
+
+       rv = 0;
+done:
+       free(pd, M_DEVBUF, sizeof *pd);
+       return (rv);
+}
+
+static int
+mfii_makegood(struct mfii_softc *sc, uint16_t pd_id)
+{
+       struct mfii_foreign_scan_info *fsi;
+       struct mfi_pd_details   *pd;
+       union mfi_mbox          mbox;
+       int                     rv;
+
+       fsi = malloc(sizeof *fsi, M_DEVBUF, M_WAITOK);
+       pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+
+       memset(&mbox, 0, sizeof mbox);
+       mbox.s[0] = pd_id;
+       rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof(*pd), pd,
+           &mbox);
+       if (rv != 0)
+               goto done;
+
+       if (pd->mpd_fw_state == MFI_PD_UNCONFIG_BAD) {
+               mbox.s[0] = pd_id;
+               mbox.s[1] = pd->mpd_pd.mfp_seq;
+               mbox.b[4] = MFI_PD_UNCONFIG_GOOD;
+               rv = mfii_mgmt(sc, MR_DCMD_PD_SET_STATE, MFII_DATA_NONE, 0,
+                   NULL, &mbox);
+               if (rv != 0)
+                       goto done;
+       }
+
+       memset(&mbox, 0, sizeof mbox);
+       mbox.s[0] = pd_id;
+       rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof(*pd), pd,
+           &mbox);
+       if (rv != 0)
+               goto done;
+
+       if (pd->mpd_ddf_state & MFI_DDF_FOREIGN) {
+               rv = mfii_mgmt(sc, MR_DCMD_CFG_FOREIGN_SCAN, MFII_DATA_IN,
+                   sizeof(*fsi), fsi, NULL);
+               if (rv != 0)
+                       goto done;
+
+               if (fsi->count > 0) {
+                       rv = mfii_mgmt(sc, MR_DCMD_CFG_FOREIGN_CLEAR,
+                           MFII_DATA_NONE, 0, NULL, NULL);
+                       if (rv != 0)
+                               goto done;
+               }
+       }
+
+       memset(&mbox, 0, sizeof mbox);
+       mbox.s[0] = pd_id;
+       rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof *pd, pd,
+           &mbox);
+       if (rv != 0)
+               goto done;
+
+       if (pd->mpd_fw_state != MFI_PD_UNCONFIG_GOOD ||
+           pd->mpd_ddf_state & MFI_DDF_FOREIGN)
+               rv = ENXIO;
+
+done:
+       free(fsi, M_DEVBUF, sizeof *fsi);
+       free(pd, M_DEVBUF, sizeof *pd);
+
+       return (rv);
+}
+
+static int
+mfii_makespare(struct mfii_softc *sc, uint16_t pd_id)
+{
+       struct mfi_hotspare     *hs;
+       struct mfi_pd_details   *pd;
+       union mfi_mbox          mbox;
+       size_t                  size;
+       int                     rv = EINVAL;
+
+       /* we really could skip and expect that inq took care of it */
+       if (mfii_bio_getitall(sc)) {
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_bio_getitall failed\n",
+                   DEVNAME(sc));
+               return (rv);
+       }
+       size = sizeof *hs + sizeof(uint16_t) * sc->sc_cfg->mfc_no_array;
+
+       hs = malloc(size, M_DEVBUF, M_WAITOK);
+       pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+
+       memset(&mbox, 0, sizeof mbox);
+       mbox.s[0] = pd_id;
+       rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof *pd, pd,
+           &mbox);
+       if (rv != 0)
+               goto done;
+
+       memset(hs, 0, size);
+       hs->mhs_pd.mfp_id = pd->mpd_pd.mfp_id;
+       hs->mhs_pd.mfp_seq = pd->mpd_pd.mfp_seq;
+       rv = mfii_mgmt(sc, MR_DCMD_CFG_MAKE_SPARE, MFII_DATA_OUT, size, hs,
+           NULL);
+
+done:
+       free(hs, M_DEVBUF, size);
+       free(pd, M_DEVBUF, sizeof *pd);
+
+       return (rv);
+}
+
+int
+mfii_ioctl_setstate(struct mfii_softc *sc, struct bioc_setstate *bs)
+{
+       struct mfi_pd_details   *pd;
+       struct mfi_pd_list      *pl;
+       int                     i, found, rv = EINVAL;
+       union mfi_mbox          mbox;
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_setstate %x\n", DEVNAME(sc),
+           bs->bs_status);
+
+       pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+       pl = malloc(sizeof *pl, M_DEVBUF, M_WAITOK);
+
+       if (mfii_mgmt(sc, MR_DCMD_PD_GET_LIST, MFII_DATA_IN,
+           sizeof *pl, pl, NULL))
+               goto done;
+
+       for (i = 0, found = 0; i < pl->mpl_no_pd; i++)
+               if (bs->bs_channel == pl->mpl_address[i].mpa_enc_index &&
+                   bs->bs_target == pl->mpl_address[i].mpa_enc_slot) {
+                       found = 1;
+                       break;
+               }
+
+       if (!found)
+               goto done;
+
+       memset(&mbox, 0, sizeof(mbox));
+       mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+
+       if (mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+           sizeof *pd, pd, &mbox))
+               goto done;
+
+       mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+       mbox.s[1] = pd->mpd_pd.mfp_seq;
+
+       switch (bs->bs_status) {
+       case BIOC_SSONLINE:
+               mbox.b[4] = MFI_PD_ONLINE;
+               break;
+
+       case BIOC_SSOFFLINE:
+               mbox.b[4] = MFI_PD_OFFLINE;
+               break;
+
+       case BIOC_SSHOTSPARE:
+               mbox.b[4] = MFI_PD_HOTSPARE;
+               break;
+
+       case BIOC_SSREBUILD:
+               if (pd->mpd_fw_state != MFI_PD_OFFLINE) {
+                       if ((rv = mfii_makegood(sc,
+                           pl->mpl_address[i].mpa_pd_id)))
+                               goto done;
+
+                       if ((rv = mfii_makespare(sc,
+                           pl->mpl_address[i].mpa_pd_id)))
+                               goto done;
+
+                       memset(&mbox, 0, sizeof(mbox));
+                       mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+                       rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+                           sizeof *pd, pd, &mbox);
+                       if (rv != 0)
+                               goto done;
+
+                       /* rebuilding might be started by mfii_makespare() */
+                       if (pd->mpd_fw_state == MFI_PD_REBUILD) {
+                               rv = 0;
+                               goto done;
+                       }
+
+                       mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+                       mbox.s[1] = pd->mpd_pd.mfp_seq;
+               }
+               mbox.b[4] = MFI_PD_REBUILD;
+               break;
+
+       default:
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_setstate invalid "
+                   "opcode %x\n", DEVNAME(sc), bs->bs_status);
+               goto done;
+       }
+
+
+       rv = mfii_mgmt(sc, MR_DCMD_PD_SET_STATE, MFII_DATA_NONE, 0, NULL,
+           &mbox);
+done:
+       free(pd, M_DEVBUF, sizeof *pd);
+       free(pl, M_DEVBUF, sizeof *pl);
+       return (rv);
+}
+
+int
+mfii_ioctl_patrol(struct mfii_softc *sc, struct bioc_patrol *bp)
+{
+       uint32_t                opc, dir = MFII_DATA_NONE;
+       int                     rv = 0;
+       struct mfi_pr_properties prop;
+       struct mfi_pr_status    status;
+       uint32_t                time, exec_freq;
+
+       switch (bp->bp_opcode) {
+       case BIOC_SPSTOP:
+       case BIOC_SPSTART:
+               if (bp->bp_opcode == BIOC_SPSTART)
+                       opc = MR_DCMD_PR_START;
+               else
+                       opc = MR_DCMD_PR_STOP;
+               dir = MFII_DATA_IN;
+               if (mfii_mgmt(sc, opc, dir, 0, NULL, NULL))
+                       return (EINVAL);
+               break;
+
+       case BIOC_SPMANUAL:
+       case BIOC_SPDISABLE:
+       case BIOC_SPAUTO:
+               /* Get device's time. */
+               opc = MR_DCMD_TIME_SECS_GET;
+               dir = MFII_DATA_IN;
+               if (mfii_mgmt(sc, opc, dir, sizeof(time), &time, NULL))
+                       return (EINVAL);
+
+               opc = MR_DCMD_PR_GET_PROPERTIES;
+               dir = MFII_DATA_IN;
+               if (mfii_mgmt(sc, opc, dir, sizeof(prop), &prop, NULL))
+                       return (EINVAL);
+
+               switch (bp->bp_opcode) {
+               case BIOC_SPMANUAL:
+                       prop.op_mode = MFI_PR_OPMODE_MANUAL;
+                       break;
+               case BIOC_SPDISABLE:
+                       prop.op_mode = MFI_PR_OPMODE_DISABLED;
+                       break;
+               case BIOC_SPAUTO:
+                       if (bp->bp_autoival != 0) {
+                               if (bp->bp_autoival == -1)
+                                       /* continuously */
+                                       exec_freq = 0xffffffffU;
+                               else if (bp->bp_autoival > 0)
+                                       exec_freq = bp->bp_autoival;
+                               else
+                                       return (EINVAL);
+                               prop.exec_freq = exec_freq;
+                       }
+                       if (bp->bp_autonext != 0) {
+                               if (bp->bp_autonext < 0)
+                                       return (EINVAL);
+                               else
+                                       prop.next_exec = time + bp->bp_autonext;
+                       }
+                       prop.op_mode = MFI_PR_OPMODE_AUTO;
+                       break;
+               }
+
+               opc = MR_DCMD_PR_SET_PROPERTIES;
+               dir = MFII_DATA_OUT;
+               if (mfii_mgmt(sc, opc, dir, sizeof(prop), &prop, NULL))
+                       return (EINVAL);
+
+               break;
+
+       case BIOC_GPSTATUS:
+               opc = MR_DCMD_PR_GET_PROPERTIES;
+               dir = MFII_DATA_IN;
+               if (mfii_mgmt(sc, opc, dir, sizeof(prop), &prop, NULL))
+                       return (EINVAL);
+
+               opc = MR_DCMD_PR_GET_STATUS;
+               dir = MFII_DATA_IN;
+               if (mfii_mgmt(sc, opc, dir, sizeof(status), &status, NULL))
+                       return (EINVAL);
+
+               /* Get device's time. */
+               opc = MR_DCMD_TIME_SECS_GET;
+               dir = MFII_DATA_IN;
+               if (mfii_mgmt(sc, opc, dir, sizeof(time), &time, NULL))
+                       return (EINVAL);
+
+               switch (prop.op_mode) {
+               case MFI_PR_OPMODE_AUTO:
+                       bp->bp_mode = BIOC_SPMAUTO;
+                       bp->bp_autoival = prop.exec_freq;
+                       bp->bp_autonext = 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;
+               default:
+                       printf("%s: unknown patrol mode %d\n",
+                           DEVNAME(sc), prop.op_mode);
+                       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;
+               default:
+                       printf("%s: unknown patrol state %d\n",
+                           DEVNAME(sc), status.state);
+                       break;
+               }
+
+               break;
+
+       default:
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_patrol biocpatrol 
invalid "
+                   "opcode %x\n", DEVNAME(sc), bp->bp_opcode);
+               return (EINVAL);
+       }
+
+       return (rv);
+}
+
+int
+mfii_bio_hs(struct mfii_softc *sc, int volid, int type, void *bio_hs)
+{
+       struct mfi_conf         *cfg;
+       struct mfi_hotspare     *hs;
+       struct mfi_pd_details   *pd;
+       struct bioc_disk        *sdhs;
+       struct bioc_vol         *vdhs;
+       struct scsi_inquiry_data *inqbuf;
+       char                    vend[8+16+4+1], *vendp;
+       int                     i, rv = EINVAL;
+       uint32_t                size;
+       union mfi_mbox          mbox;
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_vol_hs %d\n", DEVNAME(sc), volid);
+
+       if (!bio_hs)
+               return (EINVAL);
+
+       pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+
+       /* send single element command to retrieve size for full structure */
+       cfg = malloc(sizeof *cfg, M_DEVBUF, M_WAITOK);
+       if (mfii_mgmt(sc, MR_DCMD_CONF_GET, MFII_DATA_IN, sizeof *cfg, cfg, 
NULL))
+               goto freeme;
+
+       size = cfg->mfc_size;
+       free(cfg, M_DEVBUF, sizeof *cfg);
+
+       /* memory for read config */
+       cfg = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO);
+       if (mfii_mgmt(sc, MR_DCMD_CONF_GET, MFII_DATA_IN, size, cfg, NULL))
+               goto freeme;
+
+       /* calculate offset to hs structure */
+       hs = (struct mfi_hotspare *)(
+           ((uint8_t *)cfg) + offsetof(struct mfi_conf, mfc_array) +
+           cfg->mfc_array_size * cfg->mfc_no_array +
+           cfg->mfc_ld_size * cfg->mfc_no_ld);
+
+       if (volid < cfg->mfc_no_ld)
+               goto freeme; /* not a hotspare */
+
+       if (volid > (cfg->mfc_no_ld + cfg->mfc_no_hs))
+               goto freeme; /* not a hotspare */
+
+       /* offset into hotspare structure */
+       i = volid - cfg->mfc_no_ld;
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_vol_hs i %d volid %d no_ld %d no_hs %d 
"
+           "hs %p cfg %p id %02x\n", DEVNAME(sc), i, volid, cfg->mfc_no_ld,
+           cfg->mfc_no_hs, hs, cfg, hs[i].mhs_pd.mfp_id);
+
+       /* get pd fields */
+       memset(&mbox, 0, sizeof(mbox));
+       mbox.s[0] = hs[i].mhs_pd.mfp_id;
+       if (mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+           sizeof *pd, pd, &mbox)) {
+               DNPRINTF(MFII_D_IOCTL, "%s: mfii_vol_hs illegal PD\n",
+                   DEVNAME(sc));
+               goto freeme;
+       }
+
+       switch (type) {
+       case MFI_MGMT_VD:
+               vdhs = bio_hs;
+               vdhs->bv_status = BIOC_SVONLINE;
+               vdhs->bv_size = pd->mpd_size / 2 * 1024; /* XXX why? */
+               vdhs->bv_level = -1; /* hotspare */
+               vdhs->bv_nodisk = 1;
+               break;
+
+       case MFI_MGMT_SD:
+               sdhs = bio_hs;
+               sdhs->bd_status = BIOC_SDHOTSPARE;
+               sdhs->bd_size = pd->mpd_size / 2 * 1024; /* XXX why? */
+               sdhs->bd_channel = pd->mpd_enc_idx;
+               sdhs->bd_target = pd->mpd_enc_slot;
+               inqbuf = (struct scsi_inquiry_data *)&pd->mpd_inq_data;
+               vendp = inqbuf->vendor;
+               memcpy(vend, vendp, sizeof vend - 1);
+               vend[sizeof vend - 1] = '\0';
+               strlcpy(sdhs->bd_vendor, vend, sizeof(sdhs->bd_vendor));
+               break;
+
+       default:
+               goto freeme;
+       }
+
+       DNPRINTF(MFII_D_IOCTL, "%s: mfii_vol_hs 6\n", DEVNAME(sc));
+       rv = 0;
+freeme:
+       free(pd, M_DEVBUF, sizeof *pd);
+       free(cfg, M_DEVBUF, 0);
+
+       return (rv);
+}
+
+#ifndef SMALL_KERNEL
+
+#define MFI_BBU_SENSORS 4
+
+int
+mfii_bbu(struct mfii_softc *sc)
+{
+       struct mfi_bbu_status bbu;
+       u_int32_t status;
+       u_int32_t mask;
+       u_int32_t soh_bad;
+       int i;
+
+       if (mfii_mgmt(sc, MR_DCMD_BBU_GET_STATUS, MFII_DATA_IN,
+           sizeof(bbu), &bbu, NULL) != 0) {
+               for (i = 0; i < MFI_BBU_SENSORS; i++) {
+                       sc->sc_bbu[i].value = 0;
+                       sc->sc_bbu[i].status = SENSOR_S_UNKNOWN;
+               }
+               for (i = 0; i < nitems(mfi_bbu_indicators); i++) {
+                       sc->sc_bbu_status[i].value = 0;
+                       sc->sc_bbu_status[i].status = SENSOR_S_UNKNOWN;
+               }
+               return (-1);
+       }
+
+       switch (bbu.battery_type) {
+       case MFI_BBU_TYPE_IBBU:
+               mask = MFI_BBU_STATE_BAD_IBBU;
+               soh_bad = 0;
+               break;
+       case MFI_BBU_TYPE_BBU:
+               mask = MFI_BBU_STATE_BAD_BBU;
+               soh_bad = (bbu.detail.bbu.is_SOH_good == 0);
+               break;
+
+       case MFI_BBU_TYPE_NONE:
+       default:
+               sc->sc_bbu[0].value = 0;
+               sc->sc_bbu[0].status = SENSOR_S_CRIT;
+               for (i = 1; i < MFI_BBU_SENSORS; i++) {
+                       sc->sc_bbu[i].value = 0;
+                       sc->sc_bbu[i].status = SENSOR_S_UNKNOWN;
+               }
+               for (i = 0; i < nitems(mfi_bbu_indicators); i++) {
+                       sc->sc_bbu_status[i].value = 0;
+                       sc->sc_bbu_status[i].status = SENSOR_S_UNKNOWN;
+               }
+               return (0);
+       }
+
+       status = letoh32(bbu.fw_status);
+
+       sc->sc_bbu[0].value = ((status & mask) || soh_bad) ? 0 : 1;
+       sc->sc_bbu[0].status = ((status & mask) || soh_bad) ? SENSOR_S_CRIT :
+           SENSOR_S_OK;
+
+       sc->sc_bbu[1].value = letoh16(bbu.voltage) * 1000;
+       sc->sc_bbu[2].value = (int16_t)letoh16(bbu.current) * 1000;
+       sc->sc_bbu[3].value = letoh16(bbu.temperature) * 1000000 + 273150000;
+       for (i = 1; i < MFI_BBU_SENSORS; i++)
+               sc->sc_bbu[i].status = SENSOR_S_UNSPEC;
+
+       for (i = 0; i < nitems(mfi_bbu_indicators); i++) {
+               sc->sc_bbu_status[i].value = (status & (1 << i)) ? 1 : 0;
+               sc->sc_bbu_status[i].status = SENSOR_S_UNSPEC;
+       }
+
+       return (0);
+}
+
+int
+mfii_create_sensors(struct mfii_softc *sc)
+{
+       struct device           *dev;
+       struct scsi_link        *link;
+       int                     i;
+
+       strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
+           sizeof(sc->sc_sensordev.xname));
+
+       if (ISSET(letoh32(sc->sc_info.mci_adapter_ops ), MFI_INFO_AOPS_BBU)) {
+               sc->sc_bbu = mallocarray(4, sizeof(*sc->sc_bbu),
+                   M_DEVBUF, M_WAITOK | M_ZERO);
+
+               sc->sc_bbu[0].type = SENSOR_INDICATOR;
+               sc->sc_bbu[0].status = SENSOR_S_UNKNOWN;
+               strlcpy(sc->sc_bbu[0].desc, "bbu ok",
+                   sizeof(sc->sc_bbu[0].desc));
+               sensor_attach(&sc->sc_sensordev, &sc->sc_bbu[0]);
+
+               sc->sc_bbu[1].type = SENSOR_VOLTS_DC;
+               sc->sc_bbu[1].status = SENSOR_S_UNSPEC;
+               sc->sc_bbu[2].type = SENSOR_AMPS;
+               sc->sc_bbu[2].status = SENSOR_S_UNSPEC;
+               sc->sc_bbu[3].type = SENSOR_TEMP;
+               sc->sc_bbu[3].status = SENSOR_S_UNSPEC;
+               for (i = 1; i < MFI_BBU_SENSORS; i++) {
+                       strlcpy(sc->sc_bbu[i].desc, "bbu",
+                           sizeof(sc->sc_bbu[i].desc));
+                       sensor_attach(&sc->sc_sensordev, &sc->sc_bbu[i]);
+               }
+
+               sc->sc_bbu_status = malloc(sizeof(*sc->sc_bbu_status) *
+                   sizeof(mfi_bbu_indicators), M_DEVBUF, M_WAITOK | M_ZERO);
+
+               for (i = 0; i < nitems(mfi_bbu_indicators); i++) {
+                       sc->sc_bbu_status[i].type = SENSOR_INDICATOR;
+                       sc->sc_bbu_status[i].status = SENSOR_S_UNSPEC;
+                       strlcpy(sc->sc_bbu_status[i].desc,
+                           mfi_bbu_indicators[i],
+                           sizeof(sc->sc_bbu_status[i].desc));
+
+                       sensor_attach(&sc->sc_sensordev, &sc->sc_bbu_status[i]);
+               }
+       }
+
+       sc->sc_sensors = mallocarray(sc->sc_ld_cnt, sizeof(struct ksensor),
+           M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (sc->sc_sensors == NULL)
+               return (1);
+
+       for (i = 0; i < sc->sc_ld_cnt; i++) {
+               link = scsi_get_link(sc->sc_scsibus, i, 0);
+               if (link == NULL)
+                       goto bad;
+
+               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 bad;
+
+       sensordev_install(&sc->sc_sensordev);
+
+       return (0);
+
+bad:
+       free(sc->sc_sensors, M_DEVBUF,
+           sc->sc_ld_cnt * sizeof(struct ksensor));
+
+       return (1);
+}
+
+void
+mfii_refresh_sensors(void *arg)
+{
+       struct mfii_softc       *sc = arg;
+       int                     i, rv;
+       struct bioc_vol         bv;
+
+       if (sc->sc_bbu != NULL && mfii_bbu(sc) != 0)
+               return;
+
+       for (i = 0; i < sc->sc_ld_cnt; i++) {
+               bzero(&bv, sizeof(bv));
+               bv.bv_volid = i;
+
+               rw_enter_write(&sc->sc_lock);
+               rv = mfii_ioctl_vol(sc, &bv);
+               rw_exit_write(&sc->sc_lock);
+
+               if (rv != 0)
+                       return;
+
+               switch(bv.bv_status) {
+               case BIOC_SVOFFLINE:
+                       sc->sc_sensors[i].value = SENSOR_DRIVE_FAIL;
+                       sc->sc_sensors[i].status = SENSOR_S_CRIT;
+                       break;
+
+               case BIOC_SVDEGRADED:
+                       sc->sc_sensors[i].value = SENSOR_DRIVE_PFAIL;
+                       sc->sc_sensors[i].status = SENSOR_S_WARN;
+                       break;
+
+               case BIOC_SVSCRUB:
+               case BIOC_SVONLINE:
+                       sc->sc_sensors[i].value = SENSOR_DRIVE_ONLINE;
+                       sc->sc_sensors[i].status = SENSOR_S_OK;
+                       break;
+
+               case BIOC_SVINVALID:
+                       /* FALLTRHOUGH */
+               default:
+                       sc->sc_sensors[i].value = 0; /* unknown */
+                       sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
+                       break;
+               }
+       }
+}
+#endif /* SMALL_KERNEL */
+#endif /* NBIO > 0 */

--
FUKAUMI Naoki

Reply via email to