Index: ic/mfireg.h =================================================================== RCS file: /cvs/src/sys/dev/ic/mfireg.h,v retrieving revision 1.45 diff -u -p -r1.45 mfireg.h --- ic/mfireg.h 14 Aug 2016 04:08:03 -0000 1.45 +++ ic/mfireg.h 1 Nov 2016 04:09:27 -0000 @@ -78,14 +78,18 @@ /* mfi Frame flags */ #define MFI_FRAME_POST_IN_REPLY_QUEUE 0x0000 #define MFI_FRAME_DONT_POST_IN_REPLY_QUEUE 0x0001 + #define MFI_FRAME_SGL32 0x0000 #define MFI_FRAME_SGL64 0x0002 + #define MFI_FRAME_SENSE32 0x0000 #define MFI_FRAME_SENSE64 0x0004 + #define MFI_FRAME_DIR_NONE 0x0000 #define MFI_FRAME_DIR_WRITE 0x0008 #define MFI_FRAME_DIR_READ 0x0010 #define MFI_FRAME_DIR_BOTH 0x0018 + #define MFI_FRAME_IEEE 0x0020 /* mfi command opcodes */ @@ -295,6 +299,12 @@ struct mfi_sense { uint8_t mse_data[MFI_SENSE_SIZE]; } __packed; +union mfi_mbox { + uint8_t b[MFI_MBOX_SIZE]; + uint16_t s[6]; + uint32_t w[3]; +} __packed __aligned(4); + /* scatter gather elements */ struct mfi_sg32 { uint32_t addr; @@ -778,7 +788,7 @@ struct mfi_ld_list { } mll_list[MFI_MAX_LD]; } __packed; -/* logicl disk details from MR_DCMD_LD_GET_INFO */ +/* logicl disk details from MR_DCMD_LD_GET_PROPERTIES */ struct mfi_ld_prop { struct mfi_ld mlp_ld; char mlp_name[16]; Index: pci/mfii.c =================================================================== RCS file: /cvs/src/sys/dev/pci/mfii.c,v retrieving revision 1.28 diff -u -p -r1.28 mfii.c --- pci/mfii.c 24 Oct 2016 05:27:52 -0000 1.28 +++ pci/mfii.c 1 Nov 2016 04:09:27 -0000 @@ -22,9 +22,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -212,6 +214,13 @@ struct mfii_iop { u_int8_t sge_flag_eol; }; +struct mfii_cfg { + struct mfi_conf *cfg; + struct mfi_array *cfg_array; + struct mfi_ld_cfg *cfg_ld; + struct mfi_hotspare *cfg_hs; +}; + struct mfii_softc { struct device sc_dev; const struct mfii_iop *sc_iop; @@ -250,11 +259,15 @@ struct mfii_softc { struct scsi_iopool sc_iopool; struct mfi_ctrl_info sc_info; + + struct ksensor *sc_sensors; + struct ksensordev sc_sensordev; }; int mfii_match(struct device *, void *, void *); void mfii_attach(struct device *, struct device *, void *); int mfii_detach(struct device *, int); +int mfii_scsi_ioctl(struct scsi_link *, u_long, caddr_t, int); struct cfattach mfii_ca = { sizeof(struct mfii_softc), @@ -277,7 +290,7 @@ struct scsi_adapter mfii_switch = { scsi_minphys, NULL, /* probe */ NULL, /* unprobe */ - NULL /* ioctl */ + mfii_scsi_ioctl }; void mfii_pd_scsi_cmd(struct scsi_xfer *); @@ -334,7 +347,26 @@ int mfii_scsi_cmd_cdb(struct mfii_soft struct scsi_xfer *); int mfii_pd_scsi_cmd_cdb(struct mfii_softc *, struct scsi_xfer *); - +int mfii_scsi_ioctl_cache(struct scsi_link *, u_int, + struct dk_cache *); +#if NBIO > 0 +int mfii_ioctl(struct device *, u_long, caddr_t); +int mfii_fill_cfg(struct mfii_softc *, struct mfii_cfg *); +int mfii_ioctl_inq(struct mfii_softc *, struct bioc_inq *); +int mfii_ioctl_vol(struct mfii_softc *, struct bioc_vol *); +int mfii_ioctl_disk(struct mfii_softc *, + struct bioc_disk *); +int mfii_ioctl_alarm(struct mfii_softc *, + struct bioc_alarm *); +int mfii_ioctl_blink(struct mfii_softc *, + struct bioc_blink *); +int mfii_ioctl_setstate(struct mfii_softc *, + struct bioc_setstate *); +int mfii_ioctl_patrol(struct mfii_softc *, + struct bioc_patrol *); +int mfii_create_sensors(struct mfii_softc *); +void mfii_refresh_sensors(void *); +#endif #define mfii_fw_state(_sc) mfii_read((_sc), MFI_OSP) @@ -506,7 +538,8 @@ mfii_attach(struct device *parent, struc memset(&saa, 0, sizeof(saa)); saa.saa_sc_link = &sc->sc_link; - config_found(&sc->sc_dev, &saa, scsiprint); + sc->sc_scsibus = (struct scsibus_softc *) + config_found(&sc->sc_dev, &saa, scsiprint); mfii_syspd(sc); @@ -514,6 +547,16 @@ mfii_attach(struct device *parent, struc mfii_write(sc, MFI_OSTS, 0xffffffff); mfii_write(sc, MFI_OMSK, ~MFII_OSTS_INTR_VALID); +#if NBIO > 0 + if (bio_register(&sc->sc_dev, mfii_ioctl) != 0) + panic("%s: controller registration failed", DEVNAME(sc)); + +#ifndef SMALL_KERNEL + if (mfii_create_sensors(sc) != 0) + printf("%s: unable to create sensors\n", DEVNAME(sc)); +#endif +#endif /* NBIO > 0 */ + return; free_sgl: mfii_dmamem_free(sc, sc->sc_sgl); @@ -621,6 +664,10 @@ mfii_detach(struct device *self, int fla if (sc->sc_ih == NULL) return (0); + if (sc->sc_sensors) { + sensordev_deinstall(&sc->sc_sensordev); + free(sc->sc_sensors, M_DEVBUF, sc->sc_info.mci_lds_present); + } pci_intr_disestablish(sc->sc_pc, sc->sc_ih); mfii_dmamem_free(sc, sc->sc_sgl); mfii_dmamem_free(sc, sc->sc_requests); @@ -945,6 +992,10 @@ mfii_mfa_poll(struct mfii_softc *sc, str if (hdr->mfh_cmd_status != 0xff) break; + bus_dmamap_sync(sc->sc_dmat, MFII_DMA_MAP(sc->sc_requests), + ccb->ccb_request_offset, MFII_REQUEST_SIZE, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + if (to++ > 5000) { /* XXX 5 seconds busywait sucks */ printf("%s: timeout on ccb %d\n", DEVNAME(sc), ccb->ccb_smid); @@ -953,10 +1004,6 @@ mfii_mfa_poll(struct mfii_softc *sc, str break; } - bus_dmamap_sync(sc->sc_dmat, MFII_DMA_MAP(sc->sc_requests), - ccb->ccb_request_offset, MFII_REQUEST_SIZE, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - delay(1000); } @@ -1065,6 +1112,10 @@ mfii_mgmt(struct mfii_softc *sc, struct hdr->mfh_flags = htole16(MFI_FRAME_DIR_WRITE); memcpy(dma_buf, buf, len); break; + default: + ccb->ccb_direction = MFII_DATA_NONE; + hdr->mfh_flags = htole16(MFI_FRAME_DIR_NONE); + break; } if (mfii_load_mfa(sc, ccb, &dcmd->mdf_sgl, @@ -1078,11 +1129,42 @@ mfii_mgmt(struct mfii_softc *sc, struct hdr->mfh_data_len = htole32(len); hdr->mfh_sg_count = ccb->ccb_dmamap->dm_nsegs; - dcmd->mdf_opcode = opc; + dcmd->mdf_opcode = htole32(opc); /* handle special opcodes */ if (mbox != NULL) memcpy(dcmd->mdf_mbox, mbox, MFI_MBOX_SIZE); + if (ISSET(flags, SCSI_DATA_OUT)) { + printf("%s: dcmd ccb %d\n", DEVNAME(sc), ccb->ccb_smid); + printf("%s: cmd %02x senselen %u cstat %02x scstat %02x\n", + DEVNAME(sc), hdr->mfh_cmd, hdr->mfh_sense_len, + hdr->mfh_cmd_status, hdr->mfh_scsi_status); + printf("%s: tgt %u lun %u cdblen %u sg count %u\n", + DEVNAME(sc), hdr->mfh_target_id, hdr->mfh_lun_id, + hdr->mfh_cdb_len, hdr->mfh_sg_count); + printf("%s: ctx %08x pad0 %08x\n", DEVNAME(sc), + hdr->mfh_context, hdr->mfh_pad0); + printf("%s: flags %04x timeout %u\n", DEVNAME(sc), + hdr->mfh_flags, hdr->mfh_timeout); + printf("%s: data len %u\n", DEVNAME(sc), hdr->mfh_data_len); + + printf("%s: opcode %08x\n", DEVNAME(sc), dcmd->mdf_opcode); + printf("%s: %02x%02x%02x%02x" + " %02x%02x%02x%02x" + " %02x%02x%02x%02x" "\n", DEVNAME(sc), + dcmd->mdf_mbox[0], dcmd->mdf_mbox[1], + dcmd->mdf_mbox[2], dcmd->mdf_mbox[3], + + dcmd->mdf_mbox[4], dcmd->mdf_mbox[5], + dcmd->mdf_mbox[6], dcmd->mdf_mbox[7], + + dcmd->mdf_mbox[8], dcmd->mdf_mbox[9], + dcmd->mdf_mbox[10], dcmd->mdf_mbox[11]); + + printf("%s: addr %08x len %u\n", DEVNAME(sc), + dcmd->mdf_sgl.sg32[0].addr, dcmd->mdf_sgl.sg32[0].len); + } + if (ISSET(flags, SCSI_NOSLEEP)) mfii_mfa_poll(sc, ccb); else { @@ -1098,6 +1180,13 @@ mfii_mgmt(struct mfii_softc *sc, struct memcpy(buf, dma_buf, len); } + + if (hdr->mfh_cmd_status != MFI_STAT_OK) + printf("%s: cmd=0x%08x failed status=%02x (fw %08x)\n", + DEVNAME(sc), opc, hdr->mfh_cmd_status, + mfii_fw_state(sc)); + + done: dma_free(dma_buf, len); @@ -1796,3 +1885,826 @@ destroy: return (1); } +int +mfii_scsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag) +{ + switch (cmd) { + case DIOCGCACHE: + case DIOCSCACHE: + return mfii_scsi_ioctl_cache(link, cmd, + (struct dk_cache *)addr); + break; + default: +#if NBIO > 0 + return mfii_ioctl(link->adapter_softc, cmd, addr); +#endif + break; + } + return (ENOTTY); +} + +int +mfii_scsi_ioctl_cache(struct scsi_link *link, u_int cmd, struct dk_cache *dc) +{ + struct mfii_softc *sc = (struct mfii_softc *)link->adapter_softc; + struct mfi_ld_prop ldp; + union mfi_mbox mbox; + struct mfii_ccb *ccb; + int rv, wrenable, rdenable; + + + memset(&mbox, 0, sizeof(mbox)); + mbox.b[0] = link->target; + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_GET_PROPERTIES, mbox.b, + &ldp, sizeof(ldp), SCSI_DATA_IN|SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + return (rv); + + if (letoh16(sc->sc_info.mci_memory_size) > 0) { + wrenable = ISSET(ldp.mlp_cur_cache_policy, + MR_LD_CACHE_ALLOW_WRITE_CACHE)? 1 : 0; + rdenable = ISSET(ldp.mlp_cur_cache_policy, + MR_LD_CACHE_ALLOW_READ_CACHE)? 1 : 0; + } else { + wrenable = ISSET(ldp.mlp_diskcache_policy, + MR_LD_DISK_CACHE_ENABLE)? 1 : 0; + rdenable = 0; + } + + if (cmd == DIOCGCACHE) { + dc->wrcache = wrenable; + dc->rdcache = rdenable; + return (0); + } + if (((dc->wrcache) ? 1 : 0) == wrenable && + ((dc->rdcache) ? 1 : 0) == rdenable) + return (0); + + printf("%s: target %u\n", DEVNAME(sc), link->target); + printf("%s: ld target %u seq %u\n", DEVNAME(sc), ldp.mlp_ld.mld_target, + letoh16(ldp.mlp_ld.mld_seq)); + printf("%s: name \"%s\"\n", DEVNAME(sc), ldp.mlp_name); + printf("%s: cache policy %x\n", DEVNAME(sc), ldp.mlp_cache_policy); + printf("%s: access policy %x\n", DEVNAME(sc), ldp.mlp_acces_policy); + printf("%s: disk cache policy %x\n", DEVNAME(sc), + ldp.mlp_diskcache_policy); + printf("%s: current cache policy %x\n", DEVNAME(sc), + ldp.mlp_cur_cache_policy); + printf("%s: disable bgi %x\n", DEVNAME(sc), + ldp.mlp_disable_bgi); + + if (link->target == 0) + return (0); + + 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 (letoh16(sc->sc_info.mci_memory_size) > 0) { + if (dc->rdcache) + SET(ldp.mlp_cache_policy, + MR_LD_CACHE_ALLOW_READ_CACHE); + else + CLR(ldp.mlp_cache_policy, + MR_LD_CACHE_ALLOW_READ_CACHE); + if (dc->wrcache) + SET(ldp.mlp_cache_policy, + MR_LD_CACHE_ALLOW_WRITE_CACHE); + else + CLR(ldp.mlp_cache_policy, + MR_LD_CACHE_ALLOW_WRITE_CACHE); + } else { + if (dc->rdcache) + return (EOPNOTSUPP); + if (dc->wrcache) + ldp.mlp_diskcache_policy = MR_LD_DISK_CACHE_ENABLE; + else + ldp.mlp_diskcache_policy = MR_LD_DISK_CACHE_DISABLE; + } + + printf("%s: ld target %u seq %u\n", DEVNAME(sc), ldp.mlp_ld.mld_target, + letoh16(ldp.mlp_ld.mld_seq)); + printf("%s: cache policy %x\n", DEVNAME(sc), ldp.mlp_cache_policy); + printf("%s: access policy %x\n", DEVNAME(sc), ldp.mlp_acces_policy); + printf("%s: disk cache policy %x\n", DEVNAME(sc), + ldp.mlp_diskcache_policy); + printf("%s: current cache policy %x\n", DEVNAME(sc), + ldp.mlp_cur_cache_policy); + printf("%s: disable bgi %x\n", DEVNAME(sc), + ldp.mlp_disable_bgi); + + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_SET_PROPERTIES, mbox.b, + &ldp, sizeof(ldp), SCSI_DATA_OUT|SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + + return (rv); +} + +#if NBIO > 0 +int +mfii_ioctl(struct device *dev, u_long cmd, caddr_t addr) +{ + struct mfii_softc *sc = (struct mfii_softc *)dev; + int rv = ENOTTY; + + switch (cmd) { + case BIOCINQ: + rv = mfii_ioctl_inq(sc, (struct bioc_inq *)addr); + break; + case BIOCVOL: + rv = mfii_ioctl_vol(sc, (struct bioc_vol *)addr); + break; + case BIOCDISK: + rv = mfii_ioctl_disk(sc, (struct bioc_disk *)addr); + break; + case BIOCALARM: + rv = mfii_ioctl_alarm(sc, (struct bioc_alarm *)addr); + break; + case BIOCBLINK: + rv = mfii_ioctl_blink(sc, (struct bioc_blink *)addr); + break; + case BIOCSETSTATE: + rv = mfii_ioctl_setstate(sc, (struct bioc_setstate *)addr); + break; + case BIOCPATROL: + rv = mfii_ioctl_patrol(sc, (struct bioc_patrol *)addr); + break; + } + + return (rv); +} + +int +mfii_fill_cfg(struct mfii_softc *sc, struct mfii_cfg *cfg) +{ + int rv, mfc_size; + struct mfi_conf *mfc; + struct mfii_ccb *ccb; + + mfc_size = sizeof(*mfc); + again: + mfc = malloc(mfc_size, M_TEMP, M_WAITOK | M_ZERO); + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_CONF_GET, NULL, + mfc, mfc_size, SCSI_DATA_IN|SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv == 0) { + mfc->mfc_size = letoh32(mfc->mfc_size); + mfc->mfc_no_array = letoh16(mfc->mfc_no_array); + mfc->mfc_array_size = letoh16(mfc->mfc_array_size); + mfc->mfc_no_ld = letoh16(mfc->mfc_no_ld); + mfc->mfc_ld_size = letoh16(mfc->mfc_ld_size); + mfc->mfc_no_hs = letoh16(mfc->mfc_no_hs); + mfc->mfc_hs_size = letoh16(mfc->mfc_hs_size); + + if (mfc_size < mfc->mfc_size) { + int tmp = mfc->mfc_size; + free(mfc, M_TEMP, mfc_size); + mfc_size = tmp; + goto again; + } + /* remember allocated size for free() */ + mfc->mfc_size = mfc_size; + + cfg->cfg = mfc; + cfg->cfg_array = (struct mfi_array *)((caddr_t)mfc + + offsetof(struct mfi_conf, mfc_array)); + cfg->cfg_ld = (struct mfi_ld_cfg *)((caddr_t)cfg->cfg_array + + mfc->mfc_array_size * mfc->mfc_no_array); + cfg->cfg_hs = (struct mfi_hotspare *)((caddr_t)cfg->cfg_ld + + mfc->mfc_ld_size * mfc->mfc_no_ld); + + return (0); + } + + free(mfc, M_TEMP, mfc_size); + return (rv); +} + +int +mfii_ioctl_inq(struct mfii_softc *sc, struct bioc_inq *bi) +{ + int rv; + struct mfii_cfg cfg = { .cfg = NULL }; + + rv = mfii_fill_cfg(sc, &cfg); + if (rv != 0) + return (rv); + + bi->bi_novol = cfg.cfg->mfc_no_ld + cfg.cfg->mfc_no_hs; + bi->bi_nodisk = letoh16(sc->sc_info.mci_pd_disks_present); + strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev)); + + if (cfg.cfg != NULL) + free(cfg.cfg, M_TEMP, cfg.cfg->mfc_size); + + return (0); +} + +int +mfii_ioctl_vol(struct mfii_softc *sc, struct bioc_vol *bv) +{ + int rv; + struct mfii_cfg cfg = { .cfg = NULL }; + struct mfi_ld_cfg *ld; + struct mfi_ld_list *list = NULL; + struct scsi_link *link; + struct mfii_ccb *ccb; + uint8_t mbox[MFI_MBOX_SIZE]; + + if ((link = scsi_get_link(sc->sc_scsibus, bv->bv_volid, 0)) != NULL && + link->device_softc != NULL) { + struct device *dev = link->device_softc; + strlcpy(bv->bv_dev, dev->dv_xname, sizeof(bv->bv_dev)); + } + rv = mfii_fill_cfg(sc, &cfg); + if (rv != 0) + goto done; + + if (bv->bv_volid >= cfg.cfg->mfc_no_ld) { + int hsid; + struct mfi_pd_details *pd; + + hsid = bv->bv_volid - cfg.cfg->mfc_no_ld; + if (hsid >= cfg.cfg->mfc_no_hs) + return (EINVAL); + + pd = malloc(sizeof(*pd), M_TEMP, M_WAITOK | M_ZERO); + ccb = scsi_io_get(&sc->sc_iopool, 0); + memset(mbox, 0, sizeof(mbox)); + *((uint16_t *)&mbox[0]) = cfg.cfg_hs[hsid].mhs_pd.mfp_id; + rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_INFO, mbox, + pd, sizeof(*pd), SCSI_DATA_IN|SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv == 0) { + bv->bv_status = BIOC_SVONLINE; + bv->bv_size = letoh64(pd->mpd_size) * 512; + bv->bv_level = -1; + bv->bv_nodisk = 1; + } + free(pd, M_TEMP, sizeof(*pd)); + + goto done; + } + + list = malloc(sizeof(*list), M_TEMP, M_WAITOK | M_ZERO); + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_GET_LIST, NULL, + list, sizeof(*list), SCSI_DATA_IN|SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + goto done; + + if (bv->bv_volid >= letoh32(list->mll_no_ld)) { + rv = EINVAL; + goto done; + } + + switch (list->mll_list[bv->bv_volid].mll_state) { + case MFI_LD_OFFLINE: + bv->bv_status = BIOC_SVOFFLINE; + break; + case MFI_LD_PART_DEGRADED: + case MFI_LD_DEGRADED: + bv->bv_status = BIOC_SVDEGRADED; + break; + case MFI_LD_ONLINE: + bv->bv_status = BIOC_SVONLINE; + break; + default: + bv->bv_status = BIOC_SVINVALID; + break; + } + bv->bv_size = letoh64(list->mll_list[bv->bv_volid].mll_size) * 512; + + ld = cfg.cfg_ld + bv->bv_volid; + bv->bv_cache = + (ld->mlc_prop.mlp_cur_cache_policy & MR_LD_CACHE_WRITE_BACK) + ? BIOC_CVWRITEBACK : BIOC_CVWRITETHROUGH; + + switch (ld->mlc_parm.mpa_pri_raid) { + case MFI_DDF_PRL_RAID0: + bv->bv_level = 0; + break; + case MFI_DDF_PRL_RAID1: + case MFI_DDF_PRL_RAID1E: + bv->bv_level = 1; + break; + case MFI_DDF_PRL_RAID3: + bv->bv_level = 3; + break; + case MFI_DDF_PRL_RAID4: + bv->bv_level = 4; + break; + case MFI_DDF_PRL_RAID5: + case MFI_DDF_PRL_RAID5E: + case MFI_DDF_PRL_RAID5EE: + bv->bv_level = 5; + break; + case MFI_DDF_PRL_RAID6: + bv->bv_level = 6; + break; + case MFI_DDF_PRL_JBOD: + case MFI_DDF_PRL_CONCAT: + default: + bv->bv_level = 0; + break; + } + bv->bv_nodisk = + ld->mlc_parm.mpa_no_drv_per_span * ld->mlc_parm.mpa_span_depth; + done: + free(list, M_TEMP, sizeof(*list)); + if (cfg.cfg != NULL) + free(cfg.cfg, M_TEMP, cfg.cfg->mfc_size); + + return (rv); +} + +int +mfii_ioctl_disk(struct mfii_softc *sc, struct bioc_disk *bd) +{ + int rv, spanidx, diskidx, arrayidx, pdidx; + struct mfii_cfg cfg = { .cfg = NULL }; + struct mfi_ld_cfg *ld; + struct mfii_ccb *ccb; + struct scsi_inquiry_data *inq; + struct mfi_pd_details *pd_det = NULL; + uint8_t mbox[MFI_MBOX_SIZE]; + + rv = mfii_fill_cfg(sc, &cfg); + if (rv != 0) + goto done; + + if (bd->bd_volid >= cfg.cfg->mfc_no_ld) { + int hsid = bd->bd_volid - cfg.cfg->mfc_no_ld; + if (hsid >= cfg.cfg->mfc_no_hs) { + rv = EINVAL; + goto done; + } + pdidx = letoh16(cfg.cfg_hs[hsid].mhs_pd.mfp_id); + } else { + ld = cfg.cfg_ld + bd->bd_volid; + if (ld->mlc_parm.mpa_no_drv_per_span == 0) { + /* avoid dividing by 0 */ + rv = EIO; + goto done; + } + spanidx = bd->bd_diskid / ld->mlc_parm.mpa_no_drv_per_span; + diskidx = bd->bd_diskid % ld->mlc_parm.mpa_no_drv_per_span; + if (spanidx < 0 || MFI_MAX_SPAN <= spanidx) { + rv = EINVAL; + goto done; + } + arrayidx = letoh16(ld->mlc_span[spanidx].mls_index); + if (arrayidx < 0 || cfg.cfg->mfc_no_array <= arrayidx) { + rv = EINVAL; + goto done; + } + pdidx = letoh16( + cfg.cfg->mfc_array[arrayidx].pd[diskidx].mar_pd.mfp_id); + } + + memset(mbox, 0, sizeof(mbox)); + *((uint16_t *)&mbox[0]) = htole16(pdidx); + + pd_det = malloc(sizeof(*pd_det), M_TEMP, M_WAITOK | M_ZERO); + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_INFO, mbox, + pd_det, sizeof(*pd_det), SCSI_DATA_IN|SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + goto done; + + bd->bd_channel = pd_det->mpd_enc_idx; + bd->bd_target = pd_det->mpd_enc_slot; + + switch (letoh16(pd_det->mpd_fw_state)) { + case MFI_PD_UNCONFIG_GOOD: + bd->bd_status = BIOC_SDUNUSED; + break; + case MFI_PD_UNCONFIG_BAD: + bd->bd_status = BIOC_SDINVALID; + break; + case MFI_PD_HOTSPARE: + bd->bd_status = BIOC_SDHOTSPARE; + break; + case MFI_PD_OFFLINE: + bd->bd_status = BIOC_SDOFFLINE; + break; + case MFI_PD_FAILED: + bd->bd_status = BIOC_SDFAILED; + break; + case MFI_PD_REBUILD: + bd->bd_status = BIOC_SDREBUILD; + break; + case MFI_PD_ONLINE: + bd->bd_status = BIOC_SDONLINE; + break; + case MFI_PD_COPYBACK: + case MFI_PD_SYSTEM: + bd->bd_status = BIOC_SDINVALID; + break; + } + bd->bd_size = letoh64(pd_det->mpd_size) * 512; + + inq = (struct scsi_inquiry_data *)pd_det->mpd_inq_data; + + memset(bd->bd_vendor, 0, sizeof(bd->bd_vendor)); + memcpy(bd->bd_vendor, inq->vendor, + MIN(sizeof(bd->bd_vendor) - 1, sizeof(inq->vendor))); + + rv = 0; + done: + free(pd_det, M_TEMP, sizeof(*pd_det)); + if (cfg.cfg != NULL) + free(cfg.cfg, M_TEMP, cfg.cfg->mfc_size); + + return (rv); +} + +int +mfii_ioctl_setstate(struct mfii_softc *sc, struct bioc_setstate *bs) +{ + int rv, i; + struct mfii_ccb *ccb; + struct mfi_pd_list *list = NULL; + struct mfi_pd_details *pd = NULL; + uint8_t mbox[MFI_MBOX_SIZE]; + + list = malloc(sizeof(*list), M_TEMP, M_WAITOK | M_ZERO); + pd = malloc(sizeof(*pd), M_TEMP, M_WAITOK | M_ZERO); + + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_LIST, NULL, + list, sizeof(*list), SCSI_DATA_IN | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + goto done; + + for (i = 0; i < letoh16(list->mpl_no_pd); i++) + if (list->mpl_address[i].mpa_enc_index == bs->bs_channel && + list->mpl_address[i].mpa_enc_slot == bs->bs_target) + break; + if (i >= letoh16(list->mpl_no_pd)) { + rv = EINVAL; + goto done; + } + + memset(mbox, 0, sizeof(mbox)); + *((uint16_t *)&mbox[0]) = list->mpl_address[i].mpa_pd_id; + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_INFO, mbox, + pd, sizeof(*pd), SCSI_DATA_IN | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + goto done; + + memset(mbox, 0, sizeof(mbox)); + *((uint16_t *)&mbox[0]) = pd->mpd_pd.mfp_id; + *((uint16_t *)&mbox[2]) = pd->mpd_pd.mfp_seq; + + switch (bs->bs_status) { + case BIOC_SSONLINE: + *((uint16_t *)&mbox[4]) = htole16(MFI_PD_ONLINE); + break; + case BIOC_SSOFFLINE: + *((uint16_t *)&mbox[4]) = htole16(MFI_PD_OFFLINE); + break; + case BIOC_SSHOTSPARE: + *((uint16_t *)&mbox[4]) = htole16(MFI_PD_HOTSPARE); + break; + case BIOC_SSREBUILD: + *((uint16_t *)&mbox[4]) = htole16(MFI_PD_REBUILD); + break; + default: + rv = EINVAL; + goto done; + } + + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_SET_STATE, mbox, + NULL, 0, SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + + done: + free(list, M_TEMP, sizeof(*list)); + free(pd, M_TEMP, sizeof(*pd)); + + return (rv); +} + +int +mfii_ioctl_alarm(struct mfii_softc *sc, struct bioc_alarm *ba) +{ + struct mfii_ccb *ccb; + u_char spkr; + int rv, cmd, flags = 0; + + if (!ISSET(letoh32(sc->sc_info.mci_hw_present), MFI_INFO_HW_ALARM)) + return (ENXIO); + + switch (ba->ba_status) { + case BIOC_SADISABLE: + cmd = MR_DCMD_SPEAKER_DISABLE; + break; + case BIOC_SAENABLE: + cmd = MR_DCMD_SPEAKER_ENABLE; + break; + case BIOC_SASILENCE: + cmd = MR_DCMD_SPEAKER_SILENCE; + break; + case BIOC_GASTATUS: + cmd = MR_DCMD_SPEAKER_GET; + flags = SCSI_DATA_IN; + break; + case BIOC_SATEST: + cmd = MR_DCMD_SPEAKER_TEST; + break; + default: + return (EINVAL); + } + + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, cmd, NULL, + &spkr, sizeof(spkr), flags | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + return (rv); + + ba->ba_status = (ba->ba_status == BIOC_GASTATUS)? spkr : 0; + + return (rv); +} + +int +mfii_ioctl_blink(struct mfii_softc *sc, struct bioc_blink *bb) +{ + struct mfi_pd_list *list = NULL; + struct mfii_ccb *ccb; + uint8_t mbox[MFI_MBOX_SIZE]; + int rv, i, cmd; +//mfi_ioctl_blink + list = malloc(sizeof(*list), M_TEMP, M_WAITOK | M_ZERO); + + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_LIST, NULL, + list, sizeof(*list), SCSI_DATA_IN | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + goto done; + + for (i = 0; i < letoh16(list->mpl_no_pd); i++) + if (list->mpl_address[i].mpa_enc_index == bb->bb_channel && + list->mpl_address[i].mpa_enc_slot == bb->bb_target) + break; + if (i >= letoh16(list->mpl_no_pd)) { + rv = EINVAL; + goto done; + } + + memset(mbox, 0, sizeof(mbox)); + *((uint16_t *)&mbox[0]) = list->mpl_address[i].mpa_pd_id; + + switch (bb->bb_status) { + case BIOC_SBUNBLINK: + cmd = MR_DCMD_PD_UNBLINK; + break; + case BIOC_SBBLINK: + case BIOC_SBALARM: + cmd = MR_DCMD_PD_BLINK; + break; + default: + rv = EINVAL; + goto done; + } + + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, cmd, mbox, NULL, 0, SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + + done: + free(list, M_TEMP, sizeof(*list)); + + return (rv); +} + +int +mfii_ioctl_patrol(struct mfii_softc *sc, struct bioc_patrol *bp) +{ + int rv = EINVAL, cmd; + struct mfii_ccb *ccb; + struct mfi_pr_properties prop; + struct mfi_pr_status status; + uint32_t time; + + switch (bp->bp_opcode) { + case BIOC_SPSTOP: + case BIOC_SPSTART: + cmd = (bp->bp_opcode == BIOC_SPSTART) + ? MR_DCMD_PR_START : MR_DCMD_PR_STOP; + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, cmd, NULL, NULL, 0, SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + break; + + case BIOC_GPSTATUS: + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_GET_PROPERTIES, NULL, + &prop, sizeof(prop), SCSI_DATA_IN | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + break; + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_GET_STATUS, NULL, + &status, sizeof(status), SCSI_DATA_IN | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + break; + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_TIME_SECS_GET, NULL, + &time, sizeof(time), SCSI_DATA_IN | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + break; + time = letoh32(time); + + switch (prop.op_mode) { + case MFI_PR_OPMODE_AUTO: + bp->bp_mode = BIOC_SPMAUTO; + bp->bp_autoival = letoh32(prop.exec_freq); + bp->bp_autonext = letoh32(prop.next_exec); + bp->bp_autonow = time; + break; + case MFI_PR_OPMODE_MANUAL: + bp->bp_mode = BIOC_SPMMANUAL; + break; + case MFI_PR_OPMODE_DISABLED: + bp->bp_mode = BIOC_SPMDISABLED; + break; + } + + switch (status.state) { + case MFI_PR_STATE_STOPPED: + bp->bp_status = BIOC_SPSSTOPPED; + break; + case MFI_PR_STATE_READY: + bp->bp_status = BIOC_SPSREADY; + break; + case MFI_PR_STATE_ACTIVE: + bp->bp_status = BIOC_SPSACTIVE; + break; + case MFI_PR_STATE_ABORTED: + bp->bp_status = BIOC_SPSABORTED; + break; + } + break; + + case BIOC_SPDISABLE: + case BIOC_SPMANUAL: + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_GET_PROPERTIES, NULL, + &prop, sizeof(prop), SCSI_DATA_IN | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + break; + prop.op_mode = (bp->bp_opcode == BIOC_SPDISABLE) + ? MFI_PR_OPMODE_DISABLED : MFI_PR_OPMODE_MANUAL; + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_SET_PROPERTIES, NULL, + &prop, sizeof(prop), SCSI_DATA_OUT | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + break; + + case BIOC_SPAUTO: + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_GET_PROPERTIES, NULL, + &prop, sizeof(prop), SCSI_DATA_IN | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + break; + prop.op_mode = MFI_PR_OPMODE_AUTO; + + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_TIME_SECS_GET, NULL, + &time, sizeof(time), SCSI_DATA_IN | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + if (rv != 0) + break; + time = letoh32(time); + if (bp->bp_autoival != 0) { + if (bp->bp_autoival == -1) + prop.exec_freq = htole32(0xffffffffUL); + else if (bp->bp_autoival > 0) + prop.exec_freq = htole32(bp->bp_autoival); + else { + rv = EINVAL; + break; + } + } + if (bp->bp_autonext != 0) { + if (bp->bp_autonext > 0) + prop.next_exec = + htole32(time + bp->bp_autonext); + else { + rv = EINVAL; + break; + } + } + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_PR_SET_PROPERTIES, NULL, + &prop, sizeof(prop), SCSI_DATA_OUT | SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + break; + } + + return (rv); +} + +#ifndef SMALL_KERNEL +int +mfii_create_sensors(struct mfii_softc *sc) +{ + int i, no_ld; + struct device *dev; + struct scsi_link *link; + + no_ld = letoh16(sc->sc_info.mci_lds_present); + + strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), + sizeof(sc->sc_sensordev.xname)); + + sc->sc_sensors = mallocarray(no_ld, sizeof(struct ksensor), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc->sc_sensors == NULL) + return (-1); + + for (i = 0; i < no_ld; i++) { + if ((link = scsi_get_link(sc->sc_scsibus, i, 0)) == NULL || + link->device_softc == NULL) + goto err; + + dev = link->device_softc; + sc->sc_sensors[i].type = SENSOR_DRIVE; + sc->sc_sensors[i].status = SENSOR_S_UNKNOWN; + strlcpy(sc->sc_sensors[i].desc, dev->dv_xname, + sizeof(sc->sc_sensors[i].desc)); + sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]); + } + + if (sensor_task_register(sc, mfii_refresh_sensors, 10) == NULL) + goto err; + + sensordev_install(&sc->sc_sensordev); + + return (0); + err: + free(sc->sc_sensors, M_DEVBUF, no_ld); + + return (-1); +} + +void +mfii_refresh_sensors(void *arg) +{ + int i, rv; + struct mfi_ld_list *list = NULL; + struct mfii_softc *sc = arg; + struct mfii_ccb *ccb; + + list = malloc(sizeof(*list), M_TEMP, M_WAITOK | M_ZERO); + ccb = scsi_io_get(&sc->sc_iopool, 0); + rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_GET_LIST, NULL, + list, sizeof(*list), SCSI_DATA_IN|SCSI_NOSLEEP); + scsi_io_put(&sc->sc_iopool, ccb); + + if (rv == 0) { + for (i = 0; i < letoh16(sc->sc_info.mci_lds_present); i++) { + switch (list->mll_list[i].mll_state) { + case MFI_LD_OFFLINE: + sc->sc_sensors[i].value = SENSOR_DRIVE_FAIL; + sc->sc_sensors[i].status = SENSOR_S_CRIT; + break; + case MFI_LD_PART_DEGRADED: + case MFI_LD_DEGRADED: + sc->sc_sensors[i].value = SENSOR_DRIVE_PFAIL; + sc->sc_sensors[i].status = SENSOR_S_WARN; + break; + case MFI_LD_ONLINE: + sc->sc_sensors[i].value = SENSOR_DRIVE_ONLINE; + sc->sc_sensors[i].status = SENSOR_S_OK; + break; + default: + sc->sc_sensors[i].value = 0; /* unknown */ + sc->sc_sensors[i].status = SENSOR_S_UNKNOWN; + break; + } + } + } + + free(list, M_TEMP, sizeof(*list)); +} +#endif +#endif