Hi,

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

Do I understand the rebuilding problem correctly?

- mfii_makegood() and mfii_makespare() are added before sending the
  rebuild command to fix the problem
- The problem doesn't happen with mfi(4)


On Mon, 05 Mar 2018 21:49:16 +0900 (JST)
Naoki Fukaumi <fuka...@soum.co.jp> wrote:
> 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