summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorad <ad@NetBSD.org>2001-07-30 19:59:04 +0000
committerad <ad@NetBSD.org>2001-07-30 19:59:04 +0000
commit3b1828355b3935a08fbdbf5c44ca48fbc92f4fa6 (patch)
tree34dfa5a256ed746e20d1de1d28cf3d7774b5e5c1 /sys/dev
parentb0178f25e3e1cc9cdcb1820e07e7f63f4d366656 (diff)
Add a driver for Mylex AcceleRAID and eXtremeRAID controllers with v6
firmware. Based off the FreeBSD driver, and re-worked by tls, erh and I.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/Makefile4
-rw-r--r--sys/dev/pci/files.pci9
-rw-r--r--sys/dev/pci/mly.c2454
-rw-r--r--sys/dev/pci/mly_tables.h375
-rw-r--r--sys/dev/pci/mlyio.h111
-rw-r--r--sys/dev/pci/mlyreg.h1313
-rw-r--r--sys/dev/pci/mlyvar.h345
7 files changed, 4607 insertions, 4 deletions
diff --git a/sys/dev/pci/Makefile b/sys/dev/pci/Makefile
index d9bf996b393..37a8b90a728 100644
--- a/sys/dev/pci/Makefile
+++ b/sys/dev/pci/Makefile
@@ -1,10 +1,10 @@
-# $NetBSD: Makefile,v 1.7 2001/04/11 07:42:37 jdolecek Exp $
+# $NetBSD: Makefile,v 1.8 2001/07/30 19:59:06 ad Exp $
# use 'make -f Makefile.pcidevs' to make pcidevs.h and pcidevs_data.h
INCSDIR= /usr/include/dev/pci
# Only install includes which are used by userland
-INCS= if_lmc_types.h if_lmcioctl.h tgareg.h
+INCS= if_lmc_types.h if_lmcioctl.h mlyio.h mlyreg.h tgareg.h
.include <bsd.kinc.mk>
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 5fa8a6ee600..4c27986ff52 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $NetBSD: files.pci,v 1.133 2001/07/25 00:11:51 thorpej Exp $
+# $NetBSD: files.pci,v 1.134 2001/07/30 19:59:06 ad Exp $
#
# Config file and device description for machine-independent PCI code.
# Included by ports that need it. Requires that the SCSI files be
@@ -54,10 +54,15 @@ file dev/pci/ld_twe.c ld_twe
attach cac at pci with cac_pci
file dev/pci/cac_pci.c cac_pci
-# Mylex RAID controllers
+# Mylex DAC960 RAID controllers
attach mlx at pci with mlx_pci
file dev/pci/mlx_pci.c mlx_pci
+# Newer Mylex AcceleRAID and eXtremeRAID controllers
+device mly: scsi
+attach mly at pci
+file dev/pci/mly.c mly
+
# DPT EATA SCSI controllers
attach dpt at pci with dpt_pci
file dev/pci/dpt_pci.c dpt_pci
diff --git a/sys/dev/pci/mly.c b/sys/dev/pci/mly.c
new file mode 100644
index 00000000000..c7cd22055cb
--- /dev/null
+++ b/sys/dev/pci/mly.c
@@ -0,0 +1,2454 @@
+/* $NetBSD: mly.c,v 1.1 2001/07/30 19:59:06 ad Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2000, 2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from FreeBSD: mly.c,v 1.8 2001/07/14 00:12:22 msmith Exp
+ */
+
+/*
+ * Driver for the Mylex AcceleRAID and eXtremeRAID family with v6 firmware.
+ *
+ * TODO:
+ *
+ * o Make mly->mly_btl a hash, then MLY_BTL_RESCAN becomes a SIMPLEQ.
+ * o Handle FC and multiple LUNs.
+ * o Fix mmbox usage.
+ * o Fix transfer speed fudge.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/buf.h>
+#include <sys/endian.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/ioctl.h>
+#include <sys/scsiio.h>
+#include <sys/kthread.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/bus.h>
+
+#include <dev/scsipi/scsi_all.h>
+#include <dev/scsipi/scsipi_all.h>
+#include <dev/scsipi/scsiconf.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/pci/mlyreg.h>
+#include <dev/pci/mlyio.h>
+#include <dev/pci/mlyvar.h>
+#include <dev/pci/mly_tables.h>
+
+static void mly_attach(struct device *, struct device *, void *);
+static int mly_match(struct device *, struct cfdata *, void *);
+static const struct mly_ident *mly_find_ident(struct pci_attach_args *);
+static int mly_fwhandshake(struct mly_softc *);
+static int mly_flush(struct mly_softc *);
+static int mly_intr(void *);
+static void mly_shutdown(void *);
+
+static int mly_alloc_ccbs(struct mly_softc *);
+static void mly_check_event(struct mly_softc *);
+static void mly_complete_event(struct mly_softc *, struct mly_ccb *);
+static void mly_complete_rescan(struct mly_softc *, struct mly_ccb *);
+static int mly_dmamem_alloc(struct mly_softc *, int, bus_dmamap_t *,
+ caddr_t *, bus_addr_t *, bus_dma_segment_t *);
+static void mly_dmamem_free(struct mly_softc *, int, bus_dmamap_t,
+ caddr_t, bus_dma_segment_t *);
+static int mly_enable_mmbox(struct mly_softc *);
+static void mly_fetch_event(struct mly_softc *);
+static int mly_get_controllerinfo(struct mly_softc *);
+static int mly_get_eventstatus(struct mly_softc *);
+static int mly_ioctl(struct mly_softc *, struct mly_cmd_ioctl *,
+ void **, size_t, void *, size_t *);
+static void mly_padstr(char *, const char *, int);
+static void mly_process_event(struct mly_softc *, struct mly_event *);
+static void mly_release_ccbs(struct mly_softc *);
+static int mly_scan_btl(struct mly_softc *, int, int);
+static void mly_scan_channel(struct mly_softc *, int);
+static void mly_thread(void *);
+static void mly_thread_create(void *);
+
+static int mly_ccb_alloc(struct mly_softc *, struct mly_ccb **);
+static void mly_ccb_complete(struct mly_softc *, struct mly_ccb *);
+static void mly_ccb_enqueue(struct mly_softc *, struct mly_ccb *);
+static void mly_ccb_free(struct mly_softc *, struct mly_ccb *);
+static int mly_ccb_map(struct mly_softc *, struct mly_ccb *);
+static int mly_ccb_poll(struct mly_softc *, struct mly_ccb *, int);
+static int mly_ccb_submit(struct mly_softc *, struct mly_ccb *);
+static void mly_ccb_unmap(struct mly_softc *, struct mly_ccb *);
+static int mly_ccb_wait(struct mly_softc *, struct mly_ccb *, int);
+
+static void mly_get_xfer_mode(struct mly_softc *, int,
+ struct scsipi_xfer_mode *);
+static void mly_scsipi_complete(struct mly_softc *, struct mly_ccb *);
+static int mly_scsipi_ioctl(struct scsipi_channel *, u_long, caddr_t,
+ int, struct proc *);
+static void mly_scsipi_minphys(struct buf *);
+static void mly_scsipi_request(struct scsipi_channel *,
+ scsipi_adapter_req_t, void *);
+
+static int mly_user_command(struct mly_softc *, struct mly_user_command *);
+static int mly_user_health(struct mly_softc *, struct mly_user_health *);
+
+cdev_decl(mly);
+
+extern struct cfdriver mly_cd;
+
+struct cfattach mly_ca = {
+ sizeof(struct mly_softc), mly_match, mly_attach
+};
+
+struct mly_ident {
+ u_short vendor;
+ u_short product;
+ u_short subvendor;
+ u_short subproduct;
+ int hwif;
+ const char *desc;
+} static const mly_ident[] = {
+ {
+ PCI_VENDOR_MYLEX,
+ PCI_PRODUCT_MYLEX_EXTREMERAID,
+ PCI_VENDOR_MYLEX,
+ 0x0040,
+ MLY_HWIF_STRONGARM,
+ "eXtremeRAID 2000"
+ },
+ {
+ PCI_VENDOR_MYLEX,
+ PCI_PRODUCT_MYLEX_EXTREMERAID,
+ PCI_VENDOR_MYLEX,
+ 0x0030,
+ MLY_HWIF_STRONGARM,
+ "eXtremeRAID 3000"
+ },
+ {
+ PCI_VENDOR_MYLEX,
+ PCI_PRODUCT_MYLEX_ACCELERAID,
+ PCI_VENDOR_MYLEX,
+ 0x0050,
+ MLY_HWIF_I960RX,
+ "AcceleRAID 352"
+ },
+ {
+ PCI_VENDOR_MYLEX,
+ PCI_PRODUCT_MYLEX_ACCELERAID,
+ PCI_VENDOR_MYLEX,
+ 0x0052,
+ MLY_HWIF_I960RX,
+ "AcceleRAID 170"
+ },
+ {
+ PCI_VENDOR_MYLEX,
+ PCI_PRODUCT_MYLEX_ACCELERAID,
+ PCI_VENDOR_MYLEX,
+ 0x0054,
+ MLY_HWIF_I960RX,
+ "AcceleRAID 160"
+ },
+};
+
+static void *mly_sdh;
+
+/*
+ * Try to find a `mly_ident' entry corresponding to this board.
+ */
+static const struct mly_ident *
+mly_find_ident(struct pci_attach_args *pa)
+{
+ const struct mly_ident *mpi, *maxmpi;
+ pcireg_t reg;
+
+ mpi = mly_ident;
+ maxmpi = mpi + sizeof(mly_ident) / sizeof(mly_ident[0]);
+
+ for (; mpi < maxmpi; mpi++) {
+ if (PCI_VENDOR(pa->pa_id) != mpi->vendor ||
+ PCI_PRODUCT(pa->pa_id) != mpi->product)
+ continue;
+
+ if (mpi->subvendor == 0x0000)
+ return (mpi);
+
+ reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
+
+ if (PCI_VENDOR(reg) == mpi->subvendor &&
+ PCI_PRODUCT(reg) == mpi->subproduct)
+ return (mpi);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Match a supported board.
+ */
+static int
+mly_match(struct device *parent, struct cfdata *cfdata, void *aux)
+{
+
+ return (mly_find_ident(aux) != NULL);
+}
+
+/*
+ * Attach a supported board.
+ */
+static void
+mly_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct pci_attach_args *pa;
+ struct mly_softc *mly;
+ struct mly_ioctl_getcontrollerinfo *mi;
+ const struct mly_ident *ident;
+ pci_chipset_tag_t pc;
+ pci_intr_handle_t ih;
+ bus_space_handle_t memh, ioh;
+ bus_space_tag_t memt, iot;
+ pcireg_t reg;
+ const char *intrstr;
+ int ior, memr, i, rv, state;
+ struct scsipi_adapter *adapt;
+ struct scsipi_channel *chan;
+
+ mly = (struct mly_softc *)self;
+ pa = aux;
+ pc = pa->pa_pc;
+ ident = mly_find_ident(pa);
+ state = 0;
+
+ mly->mly_dmat = pa->pa_dmat;
+ mly->mly_hwif = ident->hwif;
+
+ printf(": Mylex %s\n", ident->desc);
+
+ /*
+ * Map the PCI register window.
+ */
+ memr = -1;
+ ior = -1;
+
+ for (i = 0x10; i <= 0x14; i += 4) {
+ reg = pci_conf_read(pa->pa_pc, pa->pa_tag, i);
+
+ if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) {
+ if (ior == -1 && PCI_MAPREG_IO_SIZE(reg) != 0)
+ ior = i;
+ } else {
+ if (memr == -1 && PCI_MAPREG_MEM_SIZE(reg) != 0)
+ memr = i;
+ }
+ }
+
+ if (memr != -1)
+ if (pci_mapreg_map(pa, memr, PCI_MAPREG_TYPE_MEM, 0,
+ &memt, &memh, NULL, NULL))
+ memr = -1;
+ if (ior != -1)
+ if (pci_mapreg_map(pa, ior, PCI_MAPREG_TYPE_IO, 0,
+ &iot, &ioh, NULL, NULL))
+ ior = -1;
+
+ if (memr != -1) {
+ mly->mly_iot = memt;
+ mly->mly_ioh = memh;
+ } else if (ior != -1) {
+ mly->mly_iot = iot;
+ mly->mly_ioh = ioh;
+ } else {
+ printf("%s: can't map i/o or memory space\n", self->dv_xname);
+ return;
+ }
+
+ /*
+ * Enable the device.
+ */
+ reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
+ reg | PCI_COMMAND_MASTER_ENABLE);
+
+ /*
+ * Map and establish the interrupt.
+ */
+ if (pci_intr_map(pa, &ih)) {
+ printf("%s: can't map interrupt\n", self->dv_xname);
+ return;
+ }
+ intrstr = pci_intr_string(pc, ih);
+ mly->mly_ih = pci_intr_establish(pc, ih, IPL_BIO, mly_intr, mly);
+ if (mly->mly_ih == NULL) {
+ printf("%s: can't establish interrupt", self->dv_xname);
+ if (intrstr != NULL)
+ printf(" at %s", intrstr);
+ printf("\n");
+ return;
+ }
+
+ if (intrstr != NULL)
+ printf("%s: interrupting at %s\n", mly->mly_dv.dv_xname,
+ intrstr);
+
+ /*
+ * Take care of interface-specific tasks.
+ */
+ switch (mly->mly_hwif) {
+ case MLY_HWIF_I960RX:
+ mly->mly_doorbell_true = 0x00;
+ mly->mly_cmd_mailbox = MLY_I960RX_COMMAND_MAILBOX;
+ mly->mly_status_mailbox = MLY_I960RX_STATUS_MAILBOX;
+ mly->mly_idbr = MLY_I960RX_IDBR;
+ mly->mly_odbr = MLY_I960RX_ODBR;
+ mly->mly_error_status = MLY_I960RX_ERROR_STATUS;
+ mly->mly_interrupt_status = MLY_I960RX_INTERRUPT_STATUS;
+ mly->mly_interrupt_mask = MLY_I960RX_INTERRUPT_MASK;
+ break;
+
+ case MLY_HWIF_STRONGARM:
+ mly->mly_doorbell_true = 0xff;
+ mly->mly_cmd_mailbox = MLY_STRONGARM_COMMAND_MAILBOX;
+ mly->mly_status_mailbox = MLY_STRONGARM_STATUS_MAILBOX;
+ mly->mly_idbr = MLY_STRONGARM_IDBR;
+ mly->mly_odbr = MLY_STRONGARM_ODBR;
+ mly->mly_error_status = MLY_STRONGARM_ERROR_STATUS;
+ mly->mly_interrupt_status = MLY_STRONGARM_INTERRUPT_STATUS;
+ mly->mly_interrupt_mask = MLY_STRONGARM_INTERRUPT_MASK;
+ break;
+ }
+
+ /*
+ * Allocate and map the scatter/gather lists.
+ */
+ rv = mly_dmamem_alloc(mly, MLY_SGL_SIZE * MLY_MAX_CCBS,
+ &mly->mly_sg_dmamap, (caddr_t *)&mly->mly_sg,
+ &mly->mly_sg_busaddr, &mly->mly_sg_seg);
+ if (rv) {
+ printf("%s: unable to allocate S/G maps\n",
+ mly->mly_dv.dv_xname);
+ goto bad;
+ }
+ state++;
+
+ /*
+ * Allocate and map the memory mailbox.
+ */
+ rv = mly_dmamem_alloc(mly, sizeof(struct mly_mmbox),
+ &mly->mly_mmbox_dmamap, (caddr_t *)&mly->mly_mmbox,
+ &mly->mly_mmbox_busaddr, &mly->mly_mmbox_seg);
+ if (rv) {
+ printf("%s: unable to allocate mailboxes\n",
+ mly->mly_dv.dv_xname);
+ goto bad;
+ }
+ state++;
+
+ /*
+ * Initialise per-controller queues.
+ */
+ SLIST_INIT(&mly->mly_ccb_free);
+ SIMPLEQ_INIT(&mly->mly_ccb_queue);
+
+ /*
+ * Disable interrupts before we start talking to the controller.
+ */
+ mly_outb(mly, mly->mly_interrupt_mask, MLY_INTERRUPT_MASK_DISABLE);
+
+ /*
+ * Wait for the controller to come ready, handshaking with the
+ * firmware if required. This is typically only necessary on
+ * platforms where the controller BIOS does not run.
+ */
+ if (mly_fwhandshake(mly)) {
+ printf("%s: unable to bring controller online\n",
+ mly->mly_dv.dv_xname);
+ goto bad;
+ }
+
+ /*
+ * Allocate initial command buffers, obtain controller feature
+ * information, and then reallocate command buffers, since we'll
+ * know how many we want.
+ */
+ if (mly_alloc_ccbs(mly)) {
+ printf("%s: unable to allocate CCBs\n",
+ mly->mly_dv.dv_xname);
+ goto bad;
+ }
+ state++;
+ if (mly_get_controllerinfo(mly)) {
+ printf("%s: unable to retrieve controller info\n",
+ mly->mly_dv.dv_xname);
+ goto bad;
+ }
+ mly_release_ccbs(mly);
+ if (mly_alloc_ccbs(mly)) {
+ printf("%s: unable to allocate CCBs\n",
+ mly->mly_dv.dv_xname);
+ state--;
+ goto bad;
+ }
+
+ /*
+ * Get the current event counter for health purposes, populate the
+ * initial health status buffer.
+ */
+ if (mly_get_eventstatus(mly)) {
+ printf("%s: unable to retrieve event status\n",
+ mly->mly_dv.dv_xname);
+ goto bad;
+ }
+
+ /*
+ * Enable memory-mailbox mode.
+ */
+ if (mly_enable_mmbox(mly)) {
+ printf("%s: unable to enable memory mailbox\n",
+ mly->mly_dv.dv_xname);
+ goto bad;
+ }
+
+ /*
+ * Print a little information about the controller.
+ */
+ mi = mly->mly_controllerinfo;
+
+ printf("%s: %d physical channel%s, firmware %d.%02d-%d-%02d "
+ "(%02d%02d%02d%02d), %dMB RAM\n", mly->mly_dv.dv_xname,
+ mi->physical_channels_present,
+ (mi->physical_channels_present) > 1 ? "s" : "",
+ mi->fw_major, mi->fw_minor, mi->fw_turn, mi->fw_build,
+ mi->fw_century, mi->fw_year, mi->fw_month, mi->fw_day,
+ le16toh(mi->memory_size));
+
+ /*
+ * Register our `shutdownhook'.
+ */
+ if (mly_sdh == NULL)
+ shutdownhook_establish(mly_shutdown, NULL);
+
+ /*
+ * Clear any previous BTL information. For each bus that scsipi
+ * wants to scan, we'll receive the SCBUSIOLLSCAN ioctl and retrieve
+ * all BTL info at that point.
+ */
+ memset(&mly->mly_btl, 0, sizeof(mly->mly_btl));
+
+ mly->mly_nchans = mly->mly_controllerinfo->physical_channels_present +
+ mly->mly_controllerinfo->virtual_channels_present;
+
+ /*
+ * Attach to scsipi.
+ */
+ adapt = &mly->mly_adapt;
+ memset(adapt, 0, sizeof(*adapt));
+ adapt->adapt_dev = &mly->mly_dv;
+ adapt->adapt_nchannels = mly->mly_nchans;
+ adapt->adapt_openings = mly->mly_ncmds - MLY_CCBS_RESV;
+ adapt->adapt_max_periph = mly->mly_ncmds - MLY_CCBS_RESV;
+ adapt->adapt_request = mly_scsipi_request;
+ adapt->adapt_minphys = mly_scsipi_minphys;
+ adapt->adapt_ioctl = mly_scsipi_ioctl;
+
+ for (i = 0; i < mly->mly_nchans; i++) {
+ chan = &mly->mly_chans[i];
+ memset(chan, 0, sizeof(*chan));
+ chan->chan_adapter = adapt;
+ chan->chan_bustype = &scsi_bustype;
+ chan->chan_channel = i;
+ chan->chan_ntargets = MLY_MAX_TARGETS;
+ chan->chan_nluns = MLY_MAX_LUNS;
+ chan->chan_id = mly->mly_controllerparam->initiator_id;
+ chan->chan_flags = SCSIPI_CHAN_NOSETTLE;
+ config_found(&mly->mly_dv, chan, scsiprint);
+ }
+
+ /*
+ * Now enable interrupts...
+ */
+ mly_outb(mly, mly->mly_interrupt_mask, MLY_INTERRUPT_MASK_ENABLE);
+
+ /*
+ * Finally, create our monitoring thread.
+ */
+ kthread_create(mly_thread_create, mly);
+
+ mly->mly_state |= MLY_STATE_INITOK;
+ return;
+
+ bad:
+ if (state > 2)
+ mly_release_ccbs(mly);
+ if (state > 1)
+ mly_dmamem_free(mly, sizeof(struct mly_mmbox),
+ mly->mly_mmbox_dmamap, (caddr_t)mly->mly_mmbox,
+ &mly->mly_mmbox_seg);
+ if (state > 0)
+ mly_dmamem_free(mly, MLY_SGL_SIZE * MLY_MAX_CCBS,
+ mly->mly_sg_dmamap, (caddr_t)mly->mly_sg,
+ &mly->mly_sg_seg);
+}
+
+/*
+ * Scan all possible devices on the specified channel.
+ */
+static void
+mly_scan_channel(struct mly_softc *mly, int bus)
+{
+ int startbus, endbus, s, target;
+
+ if (bus == -1) {
+ startbus = 0;
+ endbus = mly->mly_nchans - 1;
+ } else {
+ startbus = bus;
+ endbus = bus;
+ }
+
+ for (bus = startbus; bus <= endbus; bus++)
+ for (target = 0; target < MLY_MAX_TARGETS; target++) {
+ s = splbio();
+ if (!mly_scan_btl(mly, bus, target)) {
+ tsleep(&mly->mly_btl[bus][target], PRIBIO,
+ "mlyscan", 0);
+ }
+ splx(s);
+ }
+}
+
+/*
+ * Shut down all configured `mly' devices.
+ */
+static void
+mly_shutdown(void *cookie)
+{
+ struct mly_softc *mly;
+ int i;
+
+ for (i = 0; i < mly_cd.cd_ndevs; i++) {
+ if ((mly = device_lookup(&mly_cd, i)) == NULL)
+ continue;
+
+ if (mly_flush(mly))
+ printf("%s: unable to flush cache\n",
+ mly->mly_dv.dv_xname);
+ }
+}
+
+/*
+ * Fill in the mly_controllerinfo and mly_controllerparam fields in the
+ * softc.
+ */
+static int
+mly_get_controllerinfo(struct mly_softc *mly)
+{
+ struct mly_cmd_ioctl mci;
+ int rv;
+
+ /*
+ * Build the getcontrollerinfo ioctl and send it.
+ */
+ memset(&mci, 0, sizeof(mci));
+ mci.sub_ioctl = MDACIOCTL_GETCONTROLLERINFO;
+ rv = mly_ioctl(mly, &mci, (void **)&mly->mly_controllerinfo,
+ sizeof(*mly->mly_controllerinfo), NULL, NULL);
+ if (rv != 0)
+ return (rv);
+
+ /*
+ * Build the getcontrollerparameter ioctl and send it.
+ */
+ memset(&mci, 0, sizeof(mci));
+ mci.sub_ioctl = MDACIOCTL_GETCONTROLLERPARAMETER;
+ rv = mly_ioctl(mly, &mci, (void **)&mly->mly_controllerparam,
+ sizeof(*mly->mly_controllerparam), NULL, NULL);
+
+ return (rv);
+}
+
+/*
+ * Rescan a device, possibly as a consequence of getting an event which
+ * suggests that it may have changed. Must be called with interrupts
+ * blocked.
+ */
+static int
+mly_scan_btl(struct mly_softc *mly, int bus, int target)
+{
+ struct mly_ccb *mc;
+ struct mly_cmd_ioctl *mci;
+ int rv;
+
+ if (target == mly->mly_controllerparam->initiator_id) {
+ mly->mly_btl[bus][target].mb_flags = MLY_BTL_PROTECTED;
+ return (EIO);
+ }
+
+ /* Don't re-scan if a scan is already in progress. */
+ if ((mly->mly_btl[bus][target].mb_flags & MLY_BTL_SCANNING) != 0)
+ return (EBUSY);
+
+ /* Get a command. */
+ if ((rv = mly_ccb_alloc(mly, &mc)) != 0)
+ return (rv);
+
+ /* Set up the data buffer. */
+ mc->mc_data = malloc(sizeof(union mly_devinfo),
+ M_DEVBUF, M_NOWAIT);
+ memset(mc->mc_data, 0, sizeof(union mly_devinfo));
+
+ mc->mc_flags |= MLY_CCB_DATAIN;
+ mc->mc_complete = mly_complete_rescan;
+
+ /*
+ * Build the ioctl.
+ */
+ mci = (struct mly_cmd_ioctl *)&mc->mc_packet->ioctl;
+ mci->opcode = MDACMD_IOCTL;
+ mci->timeout = 30 | MLY_TIMEOUT_SECONDS;
+ memset(&mci->param, 0, sizeof(mci->param));
+
+ if (MLY_BUS_IS_VIRTUAL(mly, bus)) {
+ mc->mc_length = sizeof(struct mly_ioctl_getlogdevinfovalid);
+ mci->data_size = htole32(mc->mc_length);
+ mci->sub_ioctl = MDACIOCTL_GETLOGDEVINFOVALID;
+ _lto3l(MLY_LOGADDR(0, MLY_LOGDEV_ID(mly, bus, target)),
+ mci->addr);
+ } else {
+ mc->mc_length = sizeof(struct mly_ioctl_getphysdevinfovalid);
+ mci->data_size = htole32(mc->mc_length);
+ mci->sub_ioctl = MDACIOCTL_GETPHYSDEVINFOVALID;
+ _lto3l(MLY_PHYADDR(0, bus, target, 0), mci->addr);
+ }
+
+ /*
+ * Dispatch the command.
+ */
+ if ((rv = mly_ccb_map(mly, mc)) != 0)
+ goto bad;
+ mly->mly_btl[bus][target].mb_flags |= MLY_BTL_SCANNING;
+ mly_ccb_enqueue(mly, mc);
+ return (0);
+
+ bad:
+ free(mc->mc_data, M_DEVBUF);
+ mly_ccb_free(mly, mc);
+ return(rv);
+}
+
+/*
+ * Handle the completion of a rescan operation.
+ */
+static void
+mly_complete_rescan(struct mly_softc *mly, struct mly_ccb *mc)
+{
+ struct mly_ioctl_getlogdevinfovalid *ldi;
+ struct mly_ioctl_getphysdevinfovalid *pdi;
+ struct mly_cmd_ioctl *mci;
+ struct mly_btl btl, *btlp;
+ struct scsipi_xfer_mode xm;
+ int bus, target, rescan;
+ u_int tmp;
+
+ mly_ccb_unmap(mly, mc);
+
+ /*
+ * Recover the bus and target from the command. We need these even
+ * in the case where we don't have a useful response.
+ */
+ mci = (struct mly_cmd_ioctl *)&mc->mc_packet->ioctl;
+ tmp = _3ltol(mci->addr);
+ rescan = 0;
+
+ if (mci->sub_ioctl == MDACIOCTL_GETLOGDEVINFOVALID) {
+ bus = MLY_LOGDEV_BUS(mly, MLY_LOGADDR_DEV(tmp));
+ target = MLY_LOGDEV_TARGET(mly, MLY_LOGADDR_DEV(tmp));
+ } else {
+ bus = MLY_PHYADDR_CHANNEL(tmp);
+ target = MLY_PHYADDR_TARGET(tmp);
+ }
+
+ /* XXX Validate bus/target? */
+ btlp = &mly->mly_btl[bus][target];
+
+ /* The default result is 'no device'. */
+ memset(&btl, 0, sizeof(btl));
+ btl.mb_flags = MLY_BTL_PROTECTED;
+
+ /* If the rescan completed OK, we have possibly-new BTL data. */
+ if (mc->mc_status != 0)
+ goto out;
+
+ if (mc->mc_length == sizeof(*ldi)) {
+ ldi = (struct mly_ioctl_getlogdevinfovalid *)mc->mc_data;
+ tmp = le32toh(ldi->logical_device_number);
+
+ if (MLY_LOGDEV_BUS(mly, tmp) != bus ||
+ MLY_LOGDEV_TARGET(mly, tmp) != target) {
+#ifdef MLYDEBUG_OBNOXIOUS
+ printf("%s: WARNING: BTL rescan (logical) for %d:%d "
+ "returned data for %d:%d instead\n",
+ mly->mly_dv.dv_xname, bus, target,
+ MLY_LOGDEV_BUS(mly, tmp),
+ MLY_LOGDEV_TARGET(mly, tmp));
+#endif
+ goto out;
+ }
+
+ btl.mb_flags = MLY_BTL_LOGICAL | MLY_BTL_TQING;
+ btl.mb_type = ldi->raid_level;
+ btl.mb_state = ldi->state;
+ } else if (mc->mc_length == sizeof(*pdi)) {
+ pdi = (struct mly_ioctl_getphysdevinfovalid *)mc->mc_data;
+
+ if (pdi->channel != bus || pdi->target != target) {
+#ifdef MLYDEBUG_OBNOXIOUS
+ printf("%s: WARNING: BTL rescan (physical) for %d:%d "
+ " returned data for %d:%d instead\n",
+ mly->mly_dv.dv_xname,
+ bus, target, pdi->channel, pdi->target);
+#endif
+ goto out;
+ }
+
+ btl.mb_flags = MLY_BTL_PHYSICAL;
+ btl.mb_type = MLY_DEVICE_TYPE_PHYSICAL;
+ btl.mb_state = pdi->state;
+ btl.mb_speed = pdi->speed;
+ btl.mb_width = pdi->width;
+
+ if (pdi->state != MLY_DEVICE_STATE_UNCONFIGURED)
+ btl.mb_flags |= MLY_BTL_PROTECTED;
+ if (pdi->command_tags != 0)
+ btl.mb_flags |= MLY_BTL_TQING;
+ } else {
+ printf("%s: BTL rescan result invalid\n", mly->mly_dv.dv_xname);
+ goto out;
+ }
+
+ /* Decide whether we need to rescan the device. */
+ if (btl.mb_flags != btlp->mb_flags ||
+ btl.mb_speed != btlp->mb_speed ||
+ btl.mb_width != btlp->mb_width)
+ rescan = 1;
+
+ out:
+ *btlp = btl;
+
+ if (rescan && (btl.mb_flags & MLY_BTL_PROTECTED) == 0) {
+ xm.xm_target = target;
+ mly_get_xfer_mode(mly, bus, &xm);
+ /* XXX SCSI mid-layer rescan goes here. */
+ }
+
+ /* Wake anybody waiting on the device to be rescanned. */
+ wakeup(btlp);
+
+ free(mc->mc_data, M_DEVBUF);
+ mly_ccb_free(mly, mc);
+}
+
+/*
+ * Get the current health status and set the 'next event' counter to suit.
+ */
+static int
+mly_get_eventstatus(struct mly_softc *mly)
+{
+ struct mly_cmd_ioctl mci;
+ struct mly_health_status *mh;
+ int rv;
+
+ /* Build the gethealthstatus ioctl and send it. */
+ memset(&mci, 0, sizeof(mci));
+ mh = NULL;
+ mci.sub_ioctl = MDACIOCTL_GETHEALTHSTATUS;
+
+ rv = mly_ioctl(mly, &mci, (void **)&mh, sizeof(*mh), NULL, NULL);
+ if (rv)
+ return (rv);
+
+ /* Get the event counter. */
+ mly->mly_event_change = le32toh(mh->change_counter);
+ mly->mly_event_waiting = le32toh(mh->next_event);
+ mly->mly_event_counter = le32toh(mh->next_event);
+
+ /* Save the health status into the memory mailbox */
+ memcpy(&mly->mly_mmbox->mmm_health.status, mh, sizeof(*mh));
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap,
+ offsetof(struct mly_mmbox, mmm_health),
+ sizeof(mly->mly_mmbox->mmm_health),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ free(mh, M_DEVBUF);
+ return (0);
+}
+
+/*
+ * Enable the memory mailbox mode.
+ */
+static int
+mly_enable_mmbox(struct mly_softc *mly)
+{
+ struct mly_cmd_ioctl mci;
+ u_int8_t *sp;
+ u_int64_t tmp;
+ int rv;
+
+ /* Build the ioctl and send it. */
+ memset(&mci, 0, sizeof(mci));
+ mci.sub_ioctl = MDACIOCTL_SETMEMORYMAILBOX;
+
+ /* Set buffer addresses. */
+ tmp = mly->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_command);
+ mci.param.setmemorymailbox.command_mailbox_physaddr = htole64(tmp);
+
+ tmp = mly->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_status);
+ mci.param.setmemorymailbox.status_mailbox_physaddr = htole64(tmp);
+
+ tmp = mly->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_health);
+ mci.param.setmemorymailbox.health_buffer_physaddr = htole64(tmp);
+
+ /* Set buffer sizes - abuse of data_size field is revolting. */
+ sp = (u_int8_t *)&mci.data_size;
+ sp[0] = (sizeof(union mly_cmd_packet) * MLY_MMBOX_COMMANDS) >> 10;
+ sp[1] = (sizeof(union mly_status_packet) * MLY_MMBOX_STATUS) >> 10;
+ mci.param.setmemorymailbox.health_buffer_size =
+ sizeof(union mly_health_region) >> 10;
+
+ rv = mly_ioctl(mly, &mci, NULL, 0, NULL, NULL);
+ if (rv)
+ return (rv);
+
+ mly->mly_state |= MLY_STATE_MMBOX_ACTIVE;
+ return (0);
+}
+
+/*
+ * Flush all pending I/O from the controller.
+ */
+static int
+mly_flush(struct mly_softc *mly)
+{
+ struct mly_cmd_ioctl mci;
+
+ /* Build the ioctl */
+ memset(&mci, 0, sizeof(mci));
+ mci.sub_ioctl = MDACIOCTL_FLUSHDEVICEDATA;
+ mci.param.deviceoperation.operation_device =
+ MLY_OPDEVICE_PHYSICAL_CONTROLLER;
+
+ /* Pass it off to the controller */
+ return (mly_ioctl(mly, &mci, NULL, 0, NULL, NULL));
+}
+
+/*
+ * Perform an ioctl command.
+ *
+ * If (data) is not NULL, the command requires data transfer. If (*data) is
+ * NULL the command requires data transfer from the controller, and we will
+ * allocate a buffer for it. If (*data) is not NULL, the command requires
+ * data transfer to the controller.
+ */
+static int
+mly_ioctl(struct mly_softc *mly, struct mly_cmd_ioctl *ioctl, void **data,
+ size_t datasize, void *sense_buffer,
+ size_t *sense_length)
+{
+ struct mly_ccb *mc;
+ struct mly_cmd_ioctl *mci;
+ u_int8_t status;
+ int rv;
+
+ mc = NULL;
+ if ((rv = mly_ccb_alloc(mly, &mc)) != 0)
+ goto bad;
+
+ /*
+ * Copy the ioctl structure, but save some important fields and then
+ * fixup.
+ */
+ mci = &mc->mc_packet->ioctl;
+ ioctl->sense_buffer_address = htole64(mci->sense_buffer_address);
+ ioctl->maximum_sense_size = mci->maximum_sense_size;
+ *mci = *ioctl;
+ mci->opcode = MDACMD_IOCTL;
+ mci->timeout = 30 | MLY_TIMEOUT_SECONDS;
+
+ /* Handle the data buffer. */
+ if (data != NULL) {
+ if (*data == NULL) {
+ /* Allocate data buffer */
+ mc->mc_data = malloc(datasize, M_DEVBUF, M_NOWAIT);
+ mc->mc_flags |= MLY_CCB_DATAIN;
+ } else {
+ mc->mc_data = *data;
+ mc->mc_flags |= MLY_CCB_DATAOUT;
+ }
+ mc->mc_length = datasize;
+ mc->mc_packet->generic.data_size = htole32(datasize);
+ }
+
+ /* Run the command. */
+ if (datasize > 0)
+ if ((rv = mly_ccb_map(mly, mc)) != 0)
+ goto bad;
+ rv = mly_ccb_poll(mly, mc, 30000);
+ if (datasize > 0)
+ mly_ccb_unmap(mly, mc);
+ if (rv != 0)
+ goto bad;
+
+ /* Clean up and return any data. */
+ status = mc->mc_status;
+
+ if (status != 0)
+ printf("mly_ioctl: command status %d\n", status);
+
+ if (mc->mc_sense > 0 && sense_buffer != NULL) {
+ memcpy(sense_buffer, mc->mc_packet, mc->mc_sense);
+ *sense_length = mc->mc_sense;
+ goto bad;
+ }
+
+ /* Should we return a data pointer? */
+ if (data != NULL && *data == NULL)
+ *data = mc->mc_data;
+
+ /* Command completed OK. */
+ rv = (status != 0 ? EIO : 0);
+
+ bad:
+ if (mc != NULL) {
+ /* Do we need to free a data buffer we allocated? */
+ if (rv != 0 && mc->mc_data != NULL && *data == NULL)
+ free(mc->mc_data, M_DEVBUF);
+ mly_ccb_free(mly, mc);
+ }
+
+ return (rv);
+}
+
+/*
+ * Check for event(s) outstanding in the controller.
+ */
+static void
+mly_check_event(struct mly_softc *mly)
+{
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap,
+ offsetof(struct mly_mmbox, mmm_health),
+ sizeof(mly->mly_mmbox->mmm_health),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+
+ /*
+ * The controller may have updated the health status information, so
+ * check for it here. Note that the counters are all in host
+ * memory, so this check is very cheap. Also note that we depend on
+ * checking on completion
+ */
+ if (le32toh(mly->mly_mmbox->mmm_health.status.change_counter) !=
+ mly->mly_event_change) {
+ mly->mly_event_change =
+ le32toh(mly->mly_mmbox->mmm_health.status.change_counter);
+ mly->mly_event_waiting =
+ le32toh(mly->mly_mmbox->mmm_health.status.next_event);
+
+ /* Wake up anyone that might be interested in this. */
+ wakeup(&mly->mly_event_change);
+ }
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap,
+ offsetof(struct mly_mmbox, mmm_health),
+ sizeof(mly->mly_mmbox->mmm_health),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ if (mly->mly_event_counter != mly->mly_event_waiting)
+ mly_fetch_event(mly);
+}
+
+/*
+ * Fetch one event from the controller. If we fail due to resource
+ * starvation, we'll be retried the next time a command completes.
+ */
+static void
+mly_fetch_event(struct mly_softc *mly)
+{
+ struct mly_ccb *mc;
+ struct mly_cmd_ioctl *mci;
+ int s;
+ u_int32_t event;
+
+ /* Get a command. */
+ if (mly_ccb_alloc(mly, &mc))
+ return;
+
+ /* Set up the data buffer. */
+ mc->mc_data = malloc(sizeof(struct mly_event), M_DEVBUF, M_NOWAIT);
+ memset(mc->mc_data, 0, sizeof(struct mly_event));
+
+ mc->mc_length = sizeof(struct mly_event);
+ mc->mc_flags |= MLY_CCB_DATAIN;
+ mc->mc_complete = mly_complete_event;
+
+ /*
+ * Get an event number to fetch. It's possible that we've raced
+ * with another context for the last event, in which case there will
+ * be no more events.
+ */
+ s = splbio();
+ if (mly->mly_event_counter == mly->mly_event_waiting) {
+ splx(s);
+ free(mc->mc_data, M_DEVBUF);
+ mly_ccb_free(mly, mc);
+ return;
+ }
+ event = mly->mly_event_counter++;
+ splx(s);
+
+ /*
+ * Build the ioctl.
+ *
+ * At this point we are committed to sending this request, as it
+ * will be the only one constructed for this particular event
+ * number.
+ */
+ mci = (struct mly_cmd_ioctl *)&mc->mc_packet->ioctl;
+ mci->opcode = MDACMD_IOCTL;
+ mci->data_size = htole32(sizeof(struct mly_event));
+ _lto3l(MLY_PHYADDR(0, 0, (event >> 16) & 0xff, (event >> 24) & 0xff),
+ mci->addr);
+ mci->timeout = 30 | MLY_TIMEOUT_SECONDS;
+ mci->sub_ioctl = MDACIOCTL_GETEVENT;
+ mci->param.getevent.sequence_number_low = htole16(event & 0xffff);
+
+ /*
+ * Submit the command.
+ */
+ if (mly_ccb_map(mly, mc) != 0)
+ goto bad;
+ mly_ccb_enqueue(mly, mc);
+ return;
+
+ bad:
+ printf("%s: couldn't fetch event %u\n", mly->mly_dv.dv_xname, event);
+ free(mc->mc_data, M_DEVBUF);
+ mly_ccb_free(mly, mc);
+}
+
+/*
+ * Handle the completion of an event poll.
+ */
+static void
+mly_complete_event(struct mly_softc *mly, struct mly_ccb *mc)
+{
+ struct mly_event *me;
+
+ me = (struct mly_event *)mc->mc_data;
+ mly_ccb_unmap(mly, mc);
+ mly_ccb_free(mly, mc);
+
+ /* If the event was successfully fetched, process it. */
+ if (mc->mc_status == SCSI_OK)
+ mly_process_event(mly, me);
+ else
+ printf("%s: unable to fetch event; status = 0x%x\n",
+ mly->mly_dv.dv_xname, mc->mc_status);
+
+ free(me, M_DEVBUF);
+
+ /* Check for another event. */
+ mly_check_event(mly);
+}
+
+/*
+ * Process a controller event. Called with interupts blocked (i.e., at
+ * interrupt time).
+ */
+static void
+mly_process_event(struct mly_softc *mly, struct mly_event *me)
+{
+ struct scsipi_sense_data *ssd;
+ int bus, target, event, class, action;
+ const char *fp, *tp;
+
+ ssd = (struct scsipi_sense_data *)&me->sense[0];
+
+ /*
+ * Errors can be reported using vendor-unique sense data. In this
+ * case, the event code will be 0x1c (Request sense data present),
+ * the sense key will be 0x09 (vendor specific), the MSB of the ASC
+ * will be set, and the actual event code will be a 16-bit value
+ * comprised of the ASCQ (low byte) and low seven bits of the ASC
+ * (low seven bits of the high byte).
+ */
+ if (le32toh(me->code) == 0x1c &&
+ (ssd->flags & SSD_KEY) == SKEY_VENDOR_UNIQUE &&
+ (ssd->add_sense_code & 0x80) != 0) {
+ event = ((int)(ssd->add_sense_code & ~0x80) << 8) +
+ ssd->add_sense_code_qual;
+ } else
+ event = le32toh(me->code);
+
+ /* Look up event, get codes. */
+ fp = mly_describe_code(mly_table_event, event);
+
+ /* Quiet event? */
+ class = fp[0];
+#ifdef notyet
+ if (isupper(class) && bootverbose)
+ class = tolower(class);
+#endif
+
+ /* Get action code, text string. */
+ action = fp[1];
+ tp = fp + 3;
+
+ /*
+ * Print some information about the event.
+ *
+ * This code uses a table derived from the corresponding portion of
+ * the Linux driver, and thus the parser is very similar.
+ */
+ switch (class) {
+ case 'p':
+ /*
+ * Error on physical drive.
+ */
+ printf("%s: physical device %d:%d %s\n", mly->mly_dv.dv_xname,
+ me->channel, me->target, tp);
+ if (action == 'r')
+ mly->mly_btl[me->channel][me->target].mb_flags |=
+ MLY_BTL_RESCAN;
+ break;
+
+ case 'l':
+ case 'm':
+ /*
+ * Error on logical unit, or message about logical unit.
+ *
+ * XXX Splitting me->lun as we do in the following can't possibly
+ * be correct. Where should we get this value?
+ */
+ bus = MLY_LOGDEV_BUS(mly, me->lun);
+ target = MLY_LOGDEV_TARGET(mly, me->lun);
+ if (action == 'r')
+ mly->mly_btl[bus][target].mb_flags |= MLY_BTL_RESCAN;
+ break;
+
+ case 's':
+ /*
+ * Report of sense data.
+ */
+ if (((ssd->flags & SSD_KEY) == SKEY_NO_SENSE ||
+ (ssd->flags & SSD_KEY) == SKEY_NOT_READY) &&
+ ssd->add_sense_code == 0x04 &&
+ (ssd->add_sense_code_qual == 0x01 ||
+ ssd->add_sense_code_qual == 0x02)) {
+ /* Ignore NO_SENSE or NOT_READY in one case */
+ break;
+ }
+
+ /*
+ * XXX Should translate this if SCSIVERBOSE.
+ */
+ printf("%s: physical device %d:%d %s\n", mly->mly_dv.dv_xname,
+ me->channel, me->target, tp);
+ printf("%s: sense key %d asc %02x ascq %02x\n",
+ mly->mly_dv.dv_xname, ssd->flags & SSD_KEY,
+ ssd->add_sense_code, ssd->add_sense_code_qual);
+ printf("%s: info %x%x%x%x csi %x%x%x%x\n",
+ mly->mly_dv.dv_xname, ssd->info[0], ssd->info[1],
+ ssd->info[2], ssd->info[3], ssd->cmd_spec_info[0],
+ ssd->cmd_spec_info[1], ssd->cmd_spec_info[2],
+ ssd->cmd_spec_info[3]);
+ if (action == 'r')
+ mly->mly_btl[me->channel][me->target].mb_flags |=
+ MLY_BTL_RESCAN;
+ break;
+
+ case 'e':
+ printf("%s: ", mly->mly_dv.dv_xname);
+ printf(tp, me->target, me->lun);
+ break;
+
+ case 'c':
+ printf("%s: controller %s\n", mly->mly_dv.dv_xname, tp);
+ break;
+
+ case '?':
+ printf("%s: %s - %d\n", mly->mly_dv.dv_xname, tp, event);
+ break;
+
+ default:
+ /* Probably a 'noisy' event being ignored. */
+ break;
+ }
+}
+
+/*
+ * Create the monitoring thread. Called after the standard kernel threads
+ * have been created.
+ */
+static void
+mly_thread_create(void *cookie)
+{
+ struct mly_softc *mly;
+ int rv;
+
+ mly = cookie;
+
+ rv = kthread_create1(mly_thread, mly, &mly->mly_thread, "%s",
+ mly->mly_dv.dv_xname);
+ if (rv != 0)
+ printf("%s: unable to create thread (%d)\n",
+ mly->mly_dv.dv_xname, rv);
+}
+
+/*
+ * Perform periodic activities.
+ */
+static void
+mly_thread(void *cookie)
+{
+ struct mly_softc *mly;
+ struct mly_btl *btl;
+ int s, bus, target, done;
+
+ mly = (struct mly_softc *)cookie;
+
+ for (;;) {
+ /* Check for new events. */
+ mly_check_event(mly);
+
+ /* Re-scan up to 1 device. */
+ s = splbio();
+ for (bus = 0; bus < mly->mly_nchans && !done; bus++) {
+ for (target = 0; target < MLY_MAX_TARGETS; target++) {
+ /* Perform device rescan? */
+ btl = &mly->mly_btl[bus][target];
+ if ((btl->mb_flags & MLY_BTL_RESCAN) != 0) {
+ btl->mb_flags ^= MLY_BTL_RESCAN;
+ mly_scan_btl(mly, bus, target);
+ done = 1;
+ break;
+ }
+ }
+ }
+ splx(s);
+
+ /* Sleep for N seconds. */
+ tsleep(mly_thread, PWAIT, "mlyzzz",
+ hz * MLY_PERIODIC_INTERVAL);
+ }
+}
+
+/*
+ * Submit a command to the controller and poll on completion. Return
+ * non-zero on timeout.
+ */
+static int
+mly_ccb_poll(struct mly_softc *mly, struct mly_ccb *mc, int timo)
+{
+ int rv;
+
+ if ((rv = mly_ccb_submit(mly, mc)) != 0)
+ return (rv);
+
+ for (timo *= 10; timo != 0; timo--) {
+ if ((mc->mc_flags & MLY_CCB_COMPLETE) != 0)
+ break;
+ mly_intr(mly);
+ DELAY(100);
+ }
+
+ return (timo == 0);
+}
+
+/*
+ * Submit a command to the controller and sleep on completion. Return
+ * non-zero on timeout.
+ */
+static int
+mly_ccb_wait(struct mly_softc *mly, struct mly_ccb *mc, int timo)
+{
+ int rv, s;
+
+ mly_ccb_enqueue(mly, mc);
+
+ s = splbio();
+ if ((mc->mc_flags & MLY_CCB_COMPLETE) != 0) {
+ splx(s);
+ return (0);
+ }
+ rv = tsleep(mc, PRIBIO, "mlywccb", timo * hz / 1000);
+ splx(s);
+
+ return (rv);
+}
+
+/*
+ * If a CCB is specified, enqueue it. Pull CCBs off the software queue in
+ * the order that they were enqueued and try to submit their command blocks
+ * to the controller for execution.
+ */
+void
+mly_ccb_enqueue(struct mly_softc *mly, struct mly_ccb *mc)
+{
+ int s;
+
+ s = splbio();
+
+ if (mc != NULL)
+ SIMPLEQ_INSERT_TAIL(&mly->mly_ccb_queue, mc, mc_link.simpleq);
+
+ while ((mc = SIMPLEQ_FIRST(&mly->mly_ccb_queue)) != NULL) {
+ if (mly_ccb_submit(mly, mc))
+ break;
+ SIMPLEQ_REMOVE_HEAD(&mly->mly_ccb_queue, mc, mc_link.simpleq);
+ }
+
+ splx(s);
+}
+
+/*
+ * Deliver a command to the controller.
+ */
+static int
+mly_ccb_submit(struct mly_softc *mly, struct mly_ccb *mc)
+{
+ union mly_cmd_packet *pkt;
+ int s, off;
+
+ mc->mc_packet->generic.command_id = htole16(mc->mc_slot);
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_pkt_dmamap,
+ mc->mc_packetphys - mly->mly_pkt_busaddr,
+ sizeof(union mly_cmd_packet),
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ s = splbio();
+
+ /*
+ * Do we have to use the hardware mailbox?
+ */
+ if ((mly->mly_state & MLY_STATE_MMBOX_ACTIVE) == 0) {
+ /*
+ * Check to see if the controller is ready for us.
+ */
+ if (mly_idbr_true(mly, MLY_HM_CMDSENT)) {
+ splx(s);
+ return (EBUSY);
+ }
+
+ /*
+ * It's ready, send the command.
+ */
+ mly_outl(mly, mly->mly_cmd_mailbox,
+ (u_int64_t)mc->mc_packetphys & 0xffffffff);
+ mly_outl(mly, mly->mly_cmd_mailbox + 4,
+ (u_int64_t)mc->mc_packetphys >> 32);
+ mly_outb(mly, mly->mly_idbr, MLY_HM_CMDSENT);
+ } else {
+ pkt = &mly->mly_mmbox->mmm_command[mly->mly_mmbox_cmd_idx];
+ off = (caddr_t)pkt - (caddr_t)mly->mly_mmbox;
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap,
+ off, sizeof(mly->mly_mmbox->mmm_command[0]),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+
+ /* Check to see if the next index is free yet. */
+ if (pkt->mmbox.flag != 0) {
+ splx(s);
+ return (EBUSY);
+ }
+
+ /* Copy in new command */
+ memcpy(pkt->mmbox.data, mc->mc_packet->mmbox.data,
+ sizeof(pkt->mmbox.data));
+
+ /* Copy flag last. */
+ pkt->mmbox.flag = mc->mc_packet->mmbox.flag;
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap,
+ off, sizeof(mly->mly_mmbox->mmm_command[0]),
+ BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+ /* Signal controller and update index. */
+ mly_outb(mly, mly->mly_idbr, MLY_AM_CMDSENT);
+ mly->mly_mmbox_cmd_idx =
+ (mly->mly_mmbox_cmd_idx + 1) % MLY_MMBOX_COMMANDS;
+ }
+
+ splx(s);
+ return (0);
+}
+
+/*
+ * Pick up completed commands from the controller and handle accordingly.
+ */
+int
+mly_intr(void *cookie)
+{
+ struct mly_ccb *mc;
+ union mly_status_packet *sp;
+ u_int16_t slot;
+ int forus, off;
+ struct mly_softc *mly;
+
+ mly = cookie;
+ forus = 0;
+
+ /*
+ * Pick up hardware-mailbox commands.
+ */
+ if (mly_odbr_true(mly, MLY_HM_STSREADY)) {
+ slot = mly_inw(mly, mly->mly_status_mailbox);
+
+ if (slot < MLY_SLOT_MAX) {
+ mc = mly->mly_ccbs + (slot - MLY_SLOT_START);
+ mc->mc_status =
+ mly_inb(mly, mly->mly_status_mailbox + 2);
+ mc->mc_sense =
+ mly_inb(mly, mly->mly_status_mailbox + 3);
+ mc->mc_resid =
+ mly_inl(mly, mly->mly_status_mailbox + 4);
+
+ mly_ccb_complete(mly, mc);
+ } else {
+ /* Slot 0xffff may mean "extremely bogus command". */
+ printf("%s: got HM completion for illegal slot %u\n",
+ mly->mly_dv.dv_xname, slot);
+ }
+
+ /* Unconditionally acknowledge status. */
+ mly_outb(mly, mly->mly_odbr, MLY_HM_STSREADY);
+ mly_outb(mly, mly->mly_idbr, MLY_HM_STSACK);
+ forus = 1;
+ }
+
+ /*
+ * Pick up memory-mailbox commands.
+ */
+ if (mly_odbr_true(mly, MLY_AM_STSREADY)) {
+ for (;;) {
+ sp = &mly->mly_mmbox->mmm_status[mly->mly_mmbox_sts_idx];
+ off = (caddr_t)sp - (caddr_t)mly->mly_mmbox;
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_mmbox_dmamap,
+ off, sizeof(mly->mly_mmbox->mmm_command[0]),
+ BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
+
+ /* Check for more status. */
+ if (sp->mmbox.flag == 0)
+ break;
+
+ /* Get slot number. */
+ slot = le16toh(sp->status.command_id);
+ if (slot < MLY_SLOT_MAX) {
+ mc = mly->mly_ccbs + (slot - MLY_SLOT_START);
+ mc->mc_status = sp->status.status;
+ mc->mc_sense = sp->status.sense_length;
+ mc->mc_resid = le32toh(sp->status.residue);
+ mly_ccb_complete(mly, mc);
+ } else {
+ /*
+ * Slot 0xffff may mean "extremely bogus
+ * command".
+ */
+ printf("%s: got AM completion for illegal "
+ "slot %u at %d\n", mly->mly_dv.dv_xname,
+ slot, mly->mly_mmbox_sts_idx);
+ }
+
+ /* Clear and move to next index. */
+ sp->mmbox.flag = 0;
+ mly->mly_mmbox_sts_idx =
+ (mly->mly_mmbox_sts_idx + 1) % MLY_MMBOX_STATUS;
+ }
+
+ /* Acknowledge that we have collected status value(s). */
+ mly_outb(mly, mly->mly_odbr, MLY_AM_STSREADY);
+ forus = 1;
+ }
+
+ /*
+ * Run the queue.
+ */
+ if (forus && SIMPLEQ_FIRST(&mly->mly_ccb_queue) != NULL)
+ mly_ccb_enqueue(mly, NULL);
+
+ return (forus);
+}
+
+/*
+ * Process completed commands
+ */
+static void
+mly_ccb_complete(struct mly_softc *mly, struct mly_ccb *mc)
+{
+ void (*complete)(struct mly_softc *, struct mly_ccb *);
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_pkt_dmamap,
+ mc->mc_packetphys - mly->mly_pkt_busaddr,
+ sizeof(union mly_cmd_packet),
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ complete = mc->mc_complete;
+ mc->mc_flags |= MLY_CCB_COMPLETE;
+
+ /*
+ * Call completion handler or wake up sleeping consumer.
+ */
+ if (complete != NULL)
+ (*complete)(mly, mc);
+ else
+ wakeup(mc);
+}
+
+/*
+ * Allocate a command.
+ */
+int
+mly_ccb_alloc(struct mly_softc *mly, struct mly_ccb **mcp)
+{
+ struct mly_ccb *mc;
+ int s;
+
+ s = splbio();
+ mc = SLIST_FIRST(&mly->mly_ccb_free);
+ if (mc != NULL)
+ SLIST_REMOVE_HEAD(&mly->mly_ccb_free, mc_link.slist);
+ splx(s);
+
+ *mcp = mc;
+ return (mc == NULL ? EAGAIN : 0);
+}
+
+/*
+ * Release a command back to the freelist.
+ */
+void
+mly_ccb_free(struct mly_softc *mly, struct mly_ccb *mc)
+{
+ int s;
+
+ /*
+ * Fill in parts of the command that may cause confusion if a
+ * consumer doesn't when we are later allocated.
+ */
+ mc->mc_data = NULL;
+ mc->mc_flags = 0;
+ mc->mc_complete = NULL;
+ mc->mc_private = NULL;
+
+ /*
+ * By default, we set up to overwrite the command packet with sense
+ * information.
+ */
+ mc->mc_packet->generic.command_control = 0;
+ mc->mc_packet->generic.sense_buffer_address =
+ htole64(mc->mc_packetphys);
+ mc->mc_packet->generic.maximum_sense_size =
+ sizeof(union mly_cmd_packet);
+
+ s = splbio();
+ SLIST_INSERT_HEAD(&mly->mly_ccb_free, mc, mc_link.slist);
+ splx(s);
+}
+
+/*
+ * Allocate and initialise command and packet structures.
+ *
+ * If the controller supports fewer than MLY_MAX_CCBS commands, limit our
+ * allocation to that number. If we don't yet know how many commands the
+ * controller supports, allocate a very small set (suitable for initialisation
+ * purposes only).
+ */
+static int
+mly_alloc_ccbs(struct mly_softc *mly)
+{
+ struct mly_ccb *mc;
+ int i, rv;
+
+ if (mly->mly_controllerinfo == NULL)
+ mly->mly_ncmds = MLY_CCBS_RESV;
+ else {
+ i = le16toh(mly->mly_controllerinfo->maximum_parallel_commands);
+ mly->mly_ncmds = min(MLY_MAX_CCBS, i);
+ }
+
+ /*
+ * Allocate enough space for all the command packets in one chunk
+ * and map them permanently into controller-visible space.
+ */
+ rv = mly_dmamem_alloc(mly,
+ mly->mly_ncmds * sizeof(union mly_cmd_packet),
+ &mly->mly_pkt_dmamap, (caddr_t *)&mly->mly_pkt,
+ &mly->mly_pkt_busaddr, &mly->mly_pkt_seg);
+ if (rv)
+ return (rv);
+
+ mly->mly_ccbs = malloc(sizeof(struct mly_ccb) * mly->mly_ncmds,
+ M_DEVBUF, M_NOWAIT);
+ memset(mly->mly_ccbs, 0, sizeof(struct mly_ccb) * mly->mly_ncmds);
+
+ for (i = 0; i < mly->mly_ncmds; i++) {
+ mc = mly->mly_ccbs + i;
+ mc->mc_slot = MLY_SLOT_START + i;
+ mc->mc_packet = mly->mly_pkt + i;
+ mc->mc_packetphys = mly->mly_pkt_busaddr +
+ (i * sizeof(union mly_cmd_packet));
+
+ rv = bus_dmamap_create(mly->mly_dmat, MLY_MAX_XFER,
+ MLY_MAX_SEGS, MLY_MAX_XFER, 0,
+ BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
+ &mc->mc_datamap);
+ if (rv) {
+ mly_release_ccbs(mly);
+ return (rv);
+ }
+
+ mly_ccb_free(mly, mc);
+ }
+
+ return (0);
+}
+
+/*
+ * Free all the storage held by commands.
+ *
+ * Must be called with all commands on the free list.
+ */
+static void
+mly_release_ccbs(struct mly_softc *mly)
+{
+ struct mly_ccb *mc;
+
+ /* Throw away command buffer DMA maps. */
+ while (mly_ccb_alloc(mly, &mc) == 0)
+ bus_dmamap_destroy(mly->mly_dmat, mc->mc_datamap);
+
+ /* Release CCB storage. */
+ free(mly->mly_ccbs, M_DEVBUF);
+
+ /* Release the packet storage. */
+ mly_dmamem_free(mly, mly->mly_ncmds * sizeof(union mly_cmd_packet),
+ mly->mly_pkt_dmamap, (caddr_t)mly->mly_pkt, &mly->mly_pkt_seg);
+}
+
+/*
+ * Map a command into controller-visible space.
+ */
+static int
+mly_ccb_map(struct mly_softc *mly, struct mly_ccb *mc)
+{
+ struct mly_cmd_generic *gen;
+ struct mly_sg_entry *sg;
+ bus_dma_segment_t *ds;
+ int flg, nseg, rv;
+
+#ifdef DIAGNOSTIC
+ /* Don't map more than once. */
+ if ((mc->mc_flags & MLY_CCB_MAPPED) != 0)
+ panic("mly_ccb_map: already mapped");
+ mc->mc_flags |= MLY_CCB_MAPPED;
+
+ /* Does the command have a data buffer? */
+ if (mc->mc_data == NULL)
+ panic("mly_ccb_map: no data buffer");
+#endif
+
+ rv = bus_dmamap_load(mly->mly_dmat, mc->mc_datamap, mc->mc_data,
+ mc->mc_length, NULL, BUS_DMA_NOWAIT | BUS_DMA_STREAMING |
+ ((mc->mc_flags & MLY_CCB_DATAIN) != 0 ?
+ BUS_DMA_READ : BUS_DMA_WRITE));
+ if (rv != 0)
+ return (rv);
+
+ gen = &mc->mc_packet->generic;
+
+ /*
+ * Can we use the transfer structure directly?
+ */
+ if ((nseg = mc->mc_datamap->dm_nsegs) <= 2) {
+ mc->mc_sgoff = -1;
+ sg = &gen->transfer.direct.sg[0];
+ } else {
+ mc->mc_sgoff = (mc->mc_slot - MLY_SLOT_START) *
+ MLY_MAX_SEGS;
+ sg = mly->mly_sg + mc->mc_sgoff;
+ gen->command_control |= MLY_CMDCTL_EXTENDED_SG_TABLE;
+ gen->transfer.indirect.entries[0] = htole16(nseg);
+ gen->transfer.indirect.table_physaddr[0] =
+ htole64(mly->mly_sg_busaddr +
+ (mc->mc_sgoff * sizeof(struct mly_sg_entry)));
+ }
+
+ /*
+ * Fill the S/G table.
+ */
+ for (ds = mc->mc_datamap->dm_segs; nseg != 0; nseg--, sg++, ds++) {
+ sg->physaddr = htole64(ds->ds_addr);
+ sg->length = htole64(ds->ds_len);
+ }
+
+ /*
+ * Sync up the data map.
+ */
+ if ((mc->mc_flags & MLY_CCB_DATAIN) != 0)
+ flg = BUS_DMASYNC_PREREAD;
+ else /* if ((mc->mc_flags & MLY_CCB_DATAOUT) != 0) */ {
+ gen->command_control |= MLY_CMDCTL_DATA_DIRECTION;
+ flg = BUS_DMASYNC_PREWRITE;
+ }
+
+ bus_dmamap_sync(mly->mly_dmat, mc->mc_datamap, 0, mc->mc_length, flg);
+
+ /*
+ * Sync up the chained S/G table, if we're using one.
+ */
+ if (mc->mc_sgoff == -1)
+ return (0);
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_sg_dmamap, mc->mc_sgoff,
+ MLY_SGL_SIZE, BUS_DMASYNC_PREWRITE);
+
+ return (0);
+}
+
+/*
+ * Unmap a command from controller-visible space.
+ */
+static void
+mly_ccb_unmap(struct mly_softc *mly, struct mly_ccb *mc)
+{
+ int flg;
+
+#ifdef DIAGNOSTIC
+ if ((mc->mc_flags & MLY_CCB_MAPPED) == 0)
+ panic("mly_ccb_unmap: not mapped");
+ mc->mc_flags &= ~MLY_CCB_MAPPED;
+#endif
+
+ if ((mc->mc_flags & MLY_CCB_DATAIN) != 0)
+ flg = BUS_DMASYNC_POSTREAD;
+ else /* if ((mc->mc_flags & MLY_CCB_DATAOUT) != 0) */
+ flg = BUS_DMASYNC_POSTWRITE;
+
+ bus_dmamap_sync(mly->mly_dmat, mc->mc_datamap, 0, mc->mc_length, flg);
+ bus_dmamap_unload(mly->mly_dmat, mc->mc_datamap);
+
+ if (mc->mc_sgoff == -1)
+ return;
+
+ bus_dmamap_sync(mly->mly_dmat, mly->mly_sg_dmamap, mc->mc_sgoff,
+ MLY_SGL_SIZE, BUS_DMASYNC_POSTWRITE);
+}
+
+/*
+ * Adjust the size of each I/O before it passes to the SCSI layer.
+ */
+static void
+mly_scsipi_minphys(struct buf *bp)
+{
+
+ if (bp->b_bcount > MLY_MAX_XFER)
+ bp->b_bcount = MLY_MAX_XFER;
+ minphys(bp);
+}
+
+/*
+ * Start a SCSI command.
+ */
+static void
+mly_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
+ void *arg)
+{
+ struct mly_ccb *mc;
+ struct mly_cmd_scsi_small *ss;
+ struct scsipi_xfer *xs;
+ struct scsipi_periph *periph;
+ struct mly_softc *mly;
+ struct mly_btl *btl;
+ int s, tmp;
+
+ mly = (void *)chan->chan_adapter->adapt_dev;
+
+ switch (req) {
+ case ADAPTER_REQ_RUN_XFER:
+ xs = arg;
+ periph = xs->xs_periph;
+ btl = &mly->mly_btl[chan->chan_channel][periph->periph_target];
+ s = splbio();
+ tmp = btl->mb_flags;
+ splx(s);
+
+ /*
+ * Check for I/O attempt to a protected or non-existant
+ * device.
+ */
+ if ((tmp & MLY_BTL_PROTECTED) != 0) {
+ xs->error = XS_SELTIMEOUT;
+ scsipi_done(xs);
+ break;
+ }
+
+#ifdef DIAGNOSTIC
+ /* XXX Increase if/when we support large SCSI commands. */
+ if (xs->cmdlen > MLY_CMD_SCSI_SMALL_CDB) {
+ printf("%s: cmd too large\n", mly->mly_dv.dv_xname);
+ xs->error = XS_DRIVER_STUFFUP;
+ scsipi_done(xs);
+ break;
+ }
+#endif
+
+ if (mly_ccb_alloc(mly, &mc)) {
+ xs->error = XS_RESOURCE_SHORTAGE;
+ scsipi_done(xs);
+ break;
+ }
+
+ /* Build the command. */
+ mc->mc_data = xs->data;
+ mc->mc_length = xs->datalen;
+ mc->mc_complete = mly_scsipi_complete;
+ mc->mc_private = xs;
+
+ /* Build the packet for the controller. */
+ ss = &mc->mc_packet->scsi_small;
+ ss->opcode = MDACMD_SCSI;
+#ifdef notdef
+ /*
+ * XXX FreeBSD does this, but it doesn't fix anything,
+ * XXX and appears potentially harmful.
+ */
+ ss->command_control |= MLY_CMDCTL_DISABLE_DISCONNECT;
+#endif
+ if ((xs->xs_control & XS_CTL_DATA_OUT) != 0)
+ mc->mc_flags |= MLY_CCB_DATAOUT;
+ else if ((xs->xs_control & XS_CTL_DATA_IN) != 0)
+ mc->mc_flags |= MLY_CCB_DATAIN;
+
+ ss->data_size = htole32(xs->datalen);
+ _lto3l(MLY_PHYADDR(0, chan->chan_channel,
+ periph->periph_target, periph->periph_lun), ss->addr);
+
+ if (xs->timeout < 60 * 1000)
+ ss->timeout = xs->timeout / 1000 |
+ MLY_TIMEOUT_SECONDS;
+ else if (xs->timeout < 60 * 60 * 1000)
+ ss->timeout = xs->timeout / (60 * 1000) |
+ MLY_TIMEOUT_MINUTES;
+ else
+ ss->timeout = xs->timeout / (60 * 60 * 1000) |
+ MLY_TIMEOUT_HOURS;
+
+ ss->maximum_sense_size = sizeof(xs->sense);
+ ss->cdb_length = xs->cmdlen;
+ memcpy(ss->cdb, xs->cmd, xs->cmdlen);
+
+ if (mc->mc_length != 0)
+ if (mly_ccb_map(mly, mc) != 0) {
+ xs->error = XS_DRIVER_STUFFUP;
+ mly_ccb_free(mly, mc);
+ scsipi_done(xs);
+ break;
+ }
+
+ /*
+ * Give the command to the controller.
+ */
+ if ((xs->xs_control & XS_CTL_POLL) != 0) {
+ if (mly_ccb_poll(mly, mc, xs->timeout + 5000)) {
+ xs->error = XS_REQUEUE;
+ if (mc->mc_length != 0)
+ mly_ccb_unmap(mly, mc);
+ mly_ccb_free(mly, mc);
+ scsipi_done(xs);
+ }
+ } else
+ mly_ccb_enqueue(mly, mc);
+
+ break;
+
+ case ADAPTER_REQ_GROW_RESOURCES:
+ /*
+ * Not supported.
+ */
+ break;
+
+ case ADAPTER_REQ_SET_XFER_MODE:
+ /*
+ * We can't change the transfer mode, but at least let
+ * scsipi know what the adapter has negotiated.
+ */
+ mly_get_xfer_mode(mly, chan->chan_channel, arg);
+ break;
+ }
+}
+
+/*
+ * Handle completion of a SCSI command.
+ */
+static void
+mly_scsipi_complete(struct mly_softc *mly, struct mly_ccb *mc)
+{
+ struct scsipi_xfer *xs;
+ struct scsipi_channel *chan;
+ struct scsipi_inquiry_data *inq;
+ struct mly_btl *btl;
+ int target, sl, s;
+ const char *p;
+
+ xs = mc->mc_private;
+ xs->status = mc->mc_status;
+
+ /*
+ * XXX The `resid' value as returned by the controller appears to be
+ * bogus, so we always set it to zero. Is it perhaps the transfer
+ * count?
+ */
+ xs->resid = 0; /* mc->mc_resid; */
+
+ if (mc->mc_length != 0)
+ mly_ccb_unmap(mly, mc);
+
+ switch (mc->mc_status) {
+ case SCSI_OK:
+ /*
+ * In order to report logical device type and status, we
+ * overwrite the result of the INQUIRY command to logical
+ * devices.
+ */
+ if (xs->cmd->opcode == INQUIRY) {
+ chan = xs->xs_periph->periph_channel;
+ target = xs->xs_periph->periph_target;
+ btl = &mly->mly_btl[chan->chan_channel][target];
+
+ s = splbio();
+ if ((btl->mb_flags & MLY_BTL_LOGICAL) != 0) {
+ inq = (struct scsipi_inquiry_data *)xs->data;
+ mly_padstr(inq->vendor, "MYLEX", 8);
+ p = mly_describe_code(mly_table_device_type,
+ btl->mb_type);
+ mly_padstr(inq->product, p, 16);
+ p = mly_describe_code(mly_table_device_state,
+ btl->mb_state);
+ mly_padstr(inq->revision, p, 4);
+ }
+ splx(s);
+ }
+
+ xs->error = XS_NOERROR;
+ break;
+
+ case SCSI_CHECK:
+ sl = mc->mc_sense;
+ if (sl > sizeof(xs->sense.scsi_sense))
+ sl = sizeof(xs->sense.scsi_sense);
+ memcpy(&xs->sense.scsi_sense, mc->mc_packet, sl);
+ xs->error = XS_SENSE;
+ break;
+
+ case SCSI_BUSY:
+ case SCSI_QUEUE_FULL:
+ xs->error = XS_BUSY;
+ break;
+
+ default:
+ printf("%s: unknown SCSI status 0x%x\n",
+ mly->mly_dv.dv_xname, xs->status);
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ }
+
+ mly_ccb_free(mly, mc);
+ scsipi_done(xs);
+}
+
+/*
+ * Notify scsipi about a target's transfer mode.
+ */
+static void
+mly_get_xfer_mode(struct mly_softc *mly, int bus, struct scsipi_xfer_mode *xm)
+{
+ struct mly_btl *btl;
+ int s;
+
+ btl = &mly->mly_btl[bus][xm->xm_target];
+ xm->xm_mode = 0;
+
+ s = splbio();
+
+ if ((btl->mb_flags & MLY_BTL_PHYSICAL) != 0) {
+ if (btl->mb_speed == 0) {
+ xm->xm_period = 0;
+ xm->xm_offset = 0;
+ } else {
+ xm->xm_period = 12; /* XXX */
+ xm->xm_offset = 8; /* XXX */
+ xm->xm_mode |= PERIPH_CAP_SYNC; /* XXX */
+ }
+
+ switch (btl->mb_width) {
+ case 32:
+ xm->xm_mode = PERIPH_CAP_WIDE32;
+ break;
+ case 16:
+ xm->xm_mode = PERIPH_CAP_WIDE16;
+ break;
+ default:
+ xm->xm_mode = 0;
+ break;
+ }
+ } else /* ((btl->mb_flags & MLY_BTL_LOGICAL) != 0) */ {
+ xm->xm_mode = PERIPH_CAP_WIDE16 | PERIPH_CAP_SYNC;
+ xm->xm_period = 12;
+ xm->xm_offset = 8;
+ }
+
+ if ((btl->mb_flags & MLY_BTL_TQING) != 0)
+ xm->xm_mode |= PERIPH_CAP_TQING;
+
+ splx(s);
+
+ scsipi_async_event(&mly->mly_chans[bus], ASYNC_EVENT_XFER_MODE, xm);
+}
+
+/*
+ * ioctl hook; used here only to initiate low-level rescans.
+ */
+static int
+mly_scsipi_ioctl(struct scsipi_channel *chan, u_long cmd, caddr_t data,
+ int flag, struct proc *p)
+{
+ struct mly_softc *mly;
+ int rv;
+
+ mly = (struct mly_softc *)chan->chan_adapter->adapt_dev;
+
+ switch (cmd) {
+ case SCBUSIOLLSCAN:
+ mly_scan_channel(mly, chan->chan_channel);
+ rv = 0;
+ break;
+ default:
+ rv = ENOTTY;
+ break;
+ }
+
+ return (rv);
+}
+
+/*
+ * Handshake with the firmware while the card is being initialised.
+ */
+static int
+mly_fwhandshake(struct mly_softc *mly)
+{
+ u_int8_t error, param0, param1;
+ int spinup;
+
+ spinup = 0;
+
+ /* Set HM_STSACK and let the firmware initialise. */
+ mly_outb(mly, mly->mly_idbr, MLY_HM_STSACK);
+ DELAY(1000); /* too short? */
+
+ /* If HM_STSACK is still true, the controller is initialising. */
+ if (!mly_idbr_true(mly, MLY_HM_STSACK))
+ return (0);
+
+ printf("%s: controller initialisation started\n",
+ mly->mly_dv.dv_xname);
+
+ /*
+ * Spin waiting for initialisation to finish, or for a message to be
+ * delivered.
+ */
+ while (mly_idbr_true(mly, MLY_HM_STSACK)) {
+ /* Check for a message */
+ if (!mly_error_valid(mly))
+ continue;
+
+ error = mly_inb(mly, mly->mly_error_status) & ~MLY_MSG_EMPTY;
+ param0 = mly_inb(mly, mly->mly_cmd_mailbox);
+ param1 = mly_inb(mly, mly->mly_cmd_mailbox + 1);
+
+ switch (error) {
+ case MLY_MSG_SPINUP:
+ if (!spinup) {
+ printf("%s: drive spinup in progress\n",
+ mly->mly_dv.dv_xname);
+ spinup = 1;
+ }
+ break;
+
+ case MLY_MSG_RACE_RECOVERY_FAIL:
+ printf("%s: mirror race recovery failed - \n",
+ mly->mly_dv.dv_xname);
+ printf("%s: one or more drives offline\n",
+ mly->mly_dv.dv_xname);
+ break;
+
+ case MLY_MSG_RACE_IN_PROGRESS:
+ printf("%s: mirror race recovery in progress\n",
+ mly->mly_dv.dv_xname);
+ break;
+
+ case MLY_MSG_RACE_ON_CRITICAL:
+ printf("%s: mirror race recovery on critical drive\n",
+ mly->mly_dv.dv_xname);
+ break;
+
+ case MLY_MSG_PARITY_ERROR:
+ printf("%s: FATAL MEMORY PARITY ERROR\n",
+ mly->mly_dv.dv_xname);
+ return (ENXIO);
+
+ default:
+ printf("%s: unknown initialisation code 0x%x\n",
+ mly->mly_dv.dv_xname, error);
+ break;
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Space-fill a character string
+ */
+static void
+mly_padstr(char *dst, const char *src, int len)
+{
+
+ while (len-- > 0) {
+ if (*src != '\0')
+ *dst++ = *src++;
+ else
+ *dst++ = ' ';
+ }
+}
+
+/*
+ * Allocate DMA safe memory.
+ */
+static int
+mly_dmamem_alloc(struct mly_softc *mly, int size, bus_dmamap_t *dmamap,
+ caddr_t *kva, bus_addr_t *paddr, bus_dma_segment_t *seg)
+{
+ int rseg, rv, state;
+
+ state = 0;
+
+ if ((rv = bus_dmamem_alloc(mly->mly_dmat, size, NBPG, 0,
+ seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) {
+ printf("%s: dmamem_alloc = %d\n", mly->mly_dv.dv_xname, rv);
+ goto bad;
+ }
+
+ state++;
+
+ if ((rv = bus_dmamem_map(mly->mly_dmat, seg, 1, size, kva,
+ BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
+ printf("%s: dmamem_map = %d\n", mly->mly_dv.dv_xname, rv);
+ goto bad;
+ }
+
+ state++;
+
+ if ((rv = bus_dmamap_create(mly->mly_dmat, size, size, 1, 0,
+ BUS_DMA_NOWAIT, dmamap)) != 0) {
+ printf("%s: dmamap_create = %d\n", mly->mly_dv.dv_xname, rv);
+ goto bad;
+ }
+
+ state++;
+
+ if ((rv = bus_dmamap_load(mly->mly_dmat, *dmamap, *kva, size,
+ NULL, BUS_DMA_NOWAIT)) != 0) {
+ printf("%s: dmamap_load = %d\n", mly->mly_dv.dv_xname, rv);
+ goto bad;
+ }
+
+ *paddr = (*dmamap)->dm_segs[0].ds_addr;
+ memset(*kva, 0, size);
+ return (0);
+
+ bad:
+ if (state > 2)
+ bus_dmamap_destroy(mly->mly_dmat, *dmamap);
+ if (state > 1)
+ bus_dmamem_unmap(mly->mly_dmat, *kva, size);
+ if (state > 0)
+ bus_dmamem_free(mly->mly_dmat, seg, 1);
+
+ return (rv);
+}
+
+/*
+ * Free DMA safe memory.
+ */
+static void
+mly_dmamem_free(struct mly_softc *mly, int size, bus_dmamap_t dmamap,
+ caddr_t kva, bus_dma_segment_t *seg)
+{
+
+ bus_dmamap_unload(mly->mly_dmat, dmamap);
+ bus_dmamap_destroy(mly->mly_dmat, dmamap);
+ bus_dmamem_unmap(mly->mly_dmat, kva, size);
+ bus_dmamem_free(mly->mly_dmat, seg, 1);
+}
+
+
+/*
+ * Accept an open operation on the control device.
+ */
+int
+mlyopen(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct mly_softc *mly;
+
+ if ((mly = device_lookup(&mly_cd, minor(dev))) == NULL)
+ return (ENXIO);
+ if ((mly->mly_state & MLY_STATE_INITOK) == 0)
+ return (ENXIO);
+ if ((mly->mly_state & MLY_STATE_OPEN) != 0)
+ return (EBUSY);
+
+ mly->mly_state |= MLY_STATE_OPEN;
+ return (0);
+}
+
+/*
+ * Accept the last close on the control device.
+ */
+int
+mlyclose(dev_t dev, int flag, int mode, struct proc *p)
+{
+ struct mly_softc *mly;
+
+ mly = device_lookup(&mly_cd, minor(dev));
+ mly->mly_state &= ~MLY_STATE_OPEN;
+ return (0);
+}
+
+/*
+ * Handle control operations.
+ */
+int
+mlyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
+{
+ struct mly_softc *mly;
+ int rv;
+
+ if (securelevel >= 2)
+ return (EPERM);
+
+ mly = device_lookup(&mly_cd, minor(dev));
+
+ switch (cmd) {
+ case MLYIO_COMMAND:
+ rv = mly_user_command(mly, (void *)data);
+ break;
+ case MLYIO_HEALTH:
+ rv = mly_user_health(mly, (void *)data);
+ break;
+ default:
+ rv = ENOTTY;
+ break;
+ }
+
+ return (rv);
+}
+
+/*
+ * Execute a command passed in from userspace.
+ *
+ * The control structure contains the actual command for the controller, as
+ * well as the user-space data pointer and data size, and an optional sense
+ * buffer size/pointer. On completion, the data size is adjusted to the
+ * command residual, and the sense buffer size to the size of the returned
+ * sense data.
+ */
+static int
+mly_user_command(struct mly_softc *mly, struct mly_user_command *uc)
+{
+ struct mly_ccb *mc;
+ int rv, mapped;
+
+ if ((rv = mly_ccb_alloc(mly, &mc)) != 0)
+ return (rv);
+
+ mapped = 0;
+ mc->mc_data = NULL;
+
+ /*
+ * Handle data size/direction.
+ */
+ if ((mc->mc_length = abs(uc->DataTransferLength)) != 0) {
+ mc->mc_data = malloc(mc->mc_length, M_DEVBUF, M_WAITOK);
+ if (mc->mc_data == NULL) {
+ rv = ENOMEM;
+ goto out;
+ }
+
+ if (uc->DataTransferLength > 0) {
+ mc->mc_flags |= MLY_CCB_DATAIN;
+ memset(mc->mc_data, 0, mc->mc_length);
+ }
+
+ if (uc->DataTransferLength < 0) {
+ mc->mc_flags |= MLY_CCB_DATAOUT;
+ rv = copyin(uc->DataTransferBuffer, mc->mc_data,
+ mc->mc_length);
+ if (rv != 0)
+ goto out;
+ }
+
+ if ((rv = mly_ccb_map(mly, mc)) != 0)
+ goto out;
+ mapped = 1;
+ }
+
+ /* Copy in the command and execute it. */
+ memcpy(mc->mc_packet, &uc->CommandMailbox, sizeof(uc->CommandMailbox));
+
+ if ((rv = mly_ccb_wait(mly, mc, 60000)) != 0)
+ goto out;
+
+ /* Return the data to userspace. */
+ if (uc->DataTransferLength > 0) {
+ rv = copyout(mc->mc_data, uc->DataTransferBuffer,
+ mc->mc_length);
+ if (rv != 0)
+ goto out;
+ }
+
+ /* Return the sense buffer to userspace. */
+ if (uc->RequestSenseLength > 0 && mc->mc_sense > 0) {
+ rv = copyout(mc->mc_packet, uc->RequestSenseBuffer,
+ min(uc->RequestSenseLength, mc->mc_sense));
+ if (rv != 0)
+ goto out;
+ }
+
+ /* Return command results to userspace (caller will copy out). */
+ uc->DataTransferLength = mc->mc_resid;
+ uc->RequestSenseLength = min(uc->RequestSenseLength, mc->mc_sense);
+ uc->CommandStatus = mc->mc_status;
+ rv = 0;
+
+ out:
+ if (mapped)
+ mly_ccb_unmap(mly, mc);
+ if (mc->mc_data != NULL)
+ free(mc->mc_data, M_DEVBUF);
+ if (mc != NULL)
+ mly_ccb_free(mly, mc);
+
+ return (rv);
+}
+
+/*
+ * Return health status to userspace. If the health change index in the
+ * user structure does not match that currently exported by the controller,
+ * we return the current status immediately. Otherwise, we block until
+ * either interrupted or new status is delivered.
+ */
+static int
+mly_user_health(struct mly_softc *mly, struct mly_user_health *uh)
+{
+ struct mly_health_status mh;
+ int rv, s;
+
+ /* Fetch the current health status from userspace. */
+ rv = copyin(uh->HealthStatusBuffer, &mh, sizeof(mh));
+ if (rv != 0)
+ return (rv);
+
+ /* spin waiting for a status update */
+ s = splbio();
+ if (mly->mly_event_change == mh.change_counter)
+ rv = tsleep(&mly->mly_event_change, PRIBIO | PCATCH,
+ "mlyhealth", 0);
+ splx(s);
+
+ if (rv == 0) {
+ /*
+ * Copy the controller's health status buffer out (there is
+ * a race here if it changes again).
+ */
+ rv = copyout(&mly->mly_mmbox->mmm_health.status,
+ uh->HealthStatusBuffer, sizeof(uh->HealthStatusBuffer));
+ }
+
+ return (rv);
+}
diff --git a/sys/dev/pci/mly_tables.h b/sys/dev/pci/mly_tables.h
new file mode 100644
index 00000000000..a8520996adf
--- /dev/null
+++ b/sys/dev/pci/mly_tables.h
@@ -0,0 +1,375 @@
+/* $NetBSD: mly_tables.h,v 1.1 2001/07/30 19:59:07 ad Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from FreeBSD: mly_tables.h,v 1.2 2001/07/14 00:12:22 msmith Exp
+ */
+
+#ifndef _PCI_MLY_TABLES_H_
+#define _PCI_MLY_TABLES_H_
+
+struct mly_code_lookup {
+ const char *string;
+ u_int code;
+};
+
+static const char *mly_describe_code(const struct mly_code_lookup *,
+ u_int32_t);
+
+/*
+ * Look up a text description of a numeric code and return a pointer to same.
+ */
+static const char *
+mly_describe_code(const struct mly_code_lookup *table, u_int32_t code)
+{
+ int i;
+
+ for (i = 0; table[i].string != NULL; i++)
+ if (table[i].code == code)
+ return (table[i].string);
+
+ return (table[i + 1].string);
+}
+
+/*
+ * This table is directly derived from the corresponding table in the Linux
+ * driver, and uses a derivative encoding for simplicity's sake.
+ *
+ * The first character of the string determines the format of the message.
+ *
+ * p "physical device <channel>:<target> <text>" (physical device status)
+ * s "physical device <channel>:<target> <text>" (scsi message or error)
+ * " sense key <key> asc <asc> ascq <ascq>"
+ * " info <info> csi <csi>"
+ * l "logical drive <unit>: <text>" (logical device status)
+ * m "logical drive <unit>: <text>" (logical device message)
+ *
+ * Messages which are typically suppressed have the first character
+ * capitalised. These messages will only be printed if bootverbose is set.
+ *
+ * The second character in the string indicates an action to be taken as a
+ * result of the event.
+ *
+ * r rescan the device for possible state change
+ *
+ */
+static const struct mly_code_lookup mly_table_event[] = {
+ /*
+ * Physical device events (0x0000 - 0x007f).
+ */
+ { "pr online", 0x0001 },
+ { "pr standby", 0x0002 },
+ { "p automatic rebuild started", 0x0005 },
+ { "p manual rebuild started", 0x0006 },
+ { "pr rebuild completed", 0x0007 },
+ { "pr rebuild cancelled", 0x0008 },
+ { "pr rebuild failed for unknown reasons", 0x0009 },
+ { "pr rebuild failed due to new physical device", 0x000a },
+ { "pr rebuild failed due to logical drive failure", 0x000b },
+ { "sr offline", 0x000c },
+ { "pr found", 0x000d },
+ { "pr gone", 0x000e },
+ { "p unconfigured", 0x000f },
+ { "p expand capacity started", 0x0010 },
+ { "pr expand capacity completed", 0x0011 },
+ { "pr expand capacity failed", 0x0012 },
+ { "p parity error", 0x0016 },
+ { "p soft error", 0x0017 },
+ { "p miscellaneous error", 0x0018 },
+ { "p reset", 0x0019 },
+ { "p active spare found", 0x001a },
+ { "p warm spare found", 0x001b },
+ { "s sense data received", 0x001c },
+ { "p initialization started", 0x001d },
+ { "pr initialization completed", 0x001e },
+ { "pr initialization failed", 0x001f },
+ { "pr initialization cancelled", 0x0020 },
+ { "P write recovery failed", 0x0021 },
+ { "p scsi bus reset failed", 0x0022 },
+ { "p double check condition", 0x0023 },
+ { "p device cannot be accessed", 0x0024 },
+ { "p gross error on scsi processor", 0x0025 },
+ { "p bad tag from device", 0x0026 },
+ { "p command timeout", 0x0027 },
+ { "pr system reset", 0x0028 },
+ { "p busy status or parity error", 0x0029 },
+ { "pr host set device to failed state", 0x002a },
+ { "pr selection timeout", 0x002b },
+ { "p scsi bus phase error", 0x002c },
+ { "pr device returned unknown status", 0x002d },
+ { "pr device not ready", 0x002e },
+ { "p device not found at startup", 0x002f },
+ { "p COD write operation failed", 0x0030 },
+ { "p BDT write operation failed", 0x0031 },
+ { "p missing at startup", 0x0039 },
+ { "p start rebuild failed (physical drive too small)", 0x003a },
+
+ /*
+ * Logical device events (0x0080 - 0x00ff).
+ */
+ { "m consistency check started", 0x0080 },
+ { "mr consistency check completed", 0x0081 },
+ { "mr consistency check cancelled", 0x0082 },
+ { "mr consistency check completed with errors", 0x0083 },
+ { "mr consistency check failed (logical drive failure)",0x0084 },
+ { "mr consistency check failed (physical device failure)",0x0085 },
+ { "lr offline", 0x0086 },
+ { "lr critical", 0x0087 },
+ { "lr online", 0x0088 },
+ { "m automatic rebuild started", 0x0089 },
+ { "m manual rebuild started", 0x008a },
+ { "mr rebuild completed", 0x008b },
+ { "mr rebuild cancelled", 0x008c },
+ { "mr rebuild failed for unknown reasons", 0x008d },
+ { "mr rebuild failed due to new physical device", 0x008e },
+ { "mr rebuild failed due to logical drive failure", 0x008f },
+ { "l initialization started", 0x0090 },
+ { "lr initialization completed", 0x0091 },
+ { "lr initialization cancelled", 0x0092 },
+ { "lr initialization failed", 0x0093 },
+ { "lr found", 0x0094 },
+ { "lr gone", 0x0095 },
+ { "l expand capacity started", 0x0096 },
+ { "lr expand capacity completed", 0x0097 },
+ { "lr expand capacity failed", 0x0098 },
+ { "l bad block found", 0x0099 },
+ { "lr size changed", 0x009a },
+ { "lr type changed", 0x009b },
+ { "l bad data block found", 0x009c },
+ { "l read of data block in bdt", 0x009e },
+ { "l write back data for disk block lost", 0x009f },
+
+ /*
+ * Enclosure management events (0x0100 - 0x017f).
+ */
+ { "e enclosure %d fan %d failed", 0x0140 },
+ { "e enclosure %d fan %d ok", 0x0141 },
+ { "e enclosure %d fan %d not present", 0x0142 },
+ { "e enclosure %d power supply %d failed", 0x0143 },
+ { "e enclosure %d power supply %d ok", 0x0144 },
+ { "e enclosure %d power supply %d not present", 0x0145 },
+ { "e enclosure %d temperature sensor %d failed", 0x0146 },
+ { "e enclosure %d temperature sensor %d critical", 0x0147 },
+ { "e enclosure %d temperature sensor %d ok", 0x0148 },
+ { "e enclosure %d temperature sensor %d not present", 0x0149 },
+ { "e enclosure %d unit %d access critical", 0x014a },
+ { "e enclosure %d unit %d access ok", 0x014b },
+ { "e enclosure %d unit %d access offline", 0x014c },
+
+ /*
+ * Controller events (0x0180 - 0x01ff).
+ */
+ { "c cache write back error", 0x0181 },
+ { "c battery backup unit found", 0x0188 },
+ { "c battery backup unit charge level low", 0x0189 },
+ { "c battery backup unit charge level ok", 0x018a },
+ { "c installation aborted", 0x0193 },
+ { "c mirror race recovery in progress", 0x0195 },
+ { "c mirror race on critical drive", 0x0196 },
+ { "c memory soft ecc error", 0x019e },
+ { "c memory hard ecc error", 0x019f },
+ { "c battery backup unit failed", 0x01a2 },
+
+ { NULL, 0 },
+ { "? unknown event code", 0 }
+};
+
+/*
+ * Values here must be 16 characters or less, as they are packed into
+ * the 'product' field in the SCSI inquiry data.
+ */
+static const struct mly_code_lookup mly_table_device_state[] = {
+ { "OFLN", MLY_DEVICE_STATE_OFFLINE },
+ { "UNCF", MLY_DEVICE_STATE_UNCONFIGURED },
+ { "ONLN", MLY_DEVICE_STATE_ONLINE },
+ { "CRIT", MLY_DEVICE_STATE_CRITICAL },
+ { "NORD", MLY_DEVICE_STATE_WRITEONLY },
+ { "STBY", MLY_DEVICE_STATE_STANDBY },
+ { "MISS", MLY_DEVICE_STATE_MISSING },
+ { NULL, 0 },
+ { "????", 0 }
+};
+
+/*
+ * Values here must be 8 characters or less, as they are packed into the
+ * 'vendor' field in the SCSI inquiry data.
+ */
+static const struct mly_code_lookup mly_table_device_type[] = {
+ { "RAID 0", MLY_DEVICE_TYPE_RAID0 },
+ { "RAID 1", MLY_DEVICE_TYPE_RAID1 },
+ { "RAID 3", MLY_DEVICE_TYPE_RAID3 },
+ { "RAID 5", MLY_DEVICE_TYPE_RAID5 },
+ { "RAID 6", MLY_DEVICE_TYPE_RAID6 },
+ { "RAID 7", MLY_DEVICE_TYPE_RAID7 },
+ { "SPAN", MLY_DEVICE_TYPE_NEWSPAN },
+ { "RAID 3", MLY_DEVICE_TYPE_RAID3F },
+ { "RAID 3", MLY_DEVICE_TYPE_RAID3L },
+ { "SPAN", MLY_DEVICE_TYPE_SPAN },
+ { "RAID 5", MLY_DEVICE_TYPE_RAID5L },
+ { "RAID E", MLY_DEVICE_TYPE_RAIDE },
+ { "PHYSICAL", MLY_DEVICE_TYPE_PHYSICAL },
+ { NULL, 0 },
+ { "UNKNOWN", 0 }
+};
+
+#ifdef notused
+static struct mly_code_lookup mly_table_bustype[] = {
+ { "SCSI", 0x00 },
+ { "FC-AL", 0x01 },
+ { "PCI", 0x03 },
+ { NULL, 0 },
+ { "unknown bus", 0}
+};
+
+static const struct mly_code_lookup mly_table_controllertype[] = {
+#if 0 /* not supported by this driver */
+ { "DAC960E", 0x01 }, /* EISA */
+ { "DAC960M", 0x08 }, /* MCA */
+ { "DAC960PD", 0x10 }, /* PCI Dual */
+ { "DAC960PL", 0x11 }, /* PCU low-cost */
+ { "DAC960PDU", 0x12 }, /* PD Ultra */
+ { "DAC960PE", 0x13 }, /* Peregrine low-cost */
+ { "DAC960PG", 0x14 }, /* Peregrine high-performance */
+ { "DAC960PJ", 0x15 }, /* Road Runner */
+ { "DAC960PTL0", 0x16 }, /* Jaguar */
+ { "DAC960PR", 0x17 }, /* Road Runner (again?) */
+ { "DAC960PRL", 0x18 }, /* Tomcat */
+ { "DAC960PT", 0x19 }, /* Road Runner (yet again?) */
+ { "DAC1164P", 0x1a }, /* Little Apple */
+ { "DAC960PTL1", 0x1b }, /* Jaguar+ */
+#endif
+ { "EXR2000P", 0x1c }, /* Big Apple */
+ { "EXR3000P", 0x1d }, /* Fibre Apple */
+ { "AcceleRAID 352", 0x1e }, /* Leopard */
+ { "AcceleRAID 170", 0x1f }, /* Lynx */
+ { "AcceleRAID 160", 0x20 }, /* Bobcat */
+ { NULL, 0 },
+ { "unknown adapter", 0 }
+};
+
+static const struct mly_code_lookup mly_table_oemname[] = {
+ { "Mylex", MLY_OEM_MYLEX },
+ { "IBM", MLY_OEM_IBM },
+ { "Hewlett-Packard", MLY_OEM_HP },
+ { "DEC/Compaq", MLY_OEM_DEC },
+ { "Siemens", MLY_OEM_SIEMENS },
+ { "Intel", MLY_OEM_INTEL },
+ { NULL, 0 },
+ { "unknown OEM", 0 }
+};
+
+static const struct mly_code_lookup mly_table_memorytype[] = {
+ { "DRAM", 0x01 },
+ { "EDRAM", 0x02 },
+ { "EDO RAM", 0x03 },
+ { "SDRAM", 0x04 },
+ { NULL, 0 },
+ { "unknown memory", 0 }
+};
+
+static const struct mly_code_lookup mly_table_cputype[] = {
+ { "i960CA", 0x01 },
+ { "i960RD", 0x02 },
+ { "i960RN", 0x03 },
+ { "i960RP", 0x04 },
+ { "NorthBay(?)", 0x05 },
+ { "StrongArm", 0x06 },
+ { "i960RM", 0x07 },
+ { NULL, 0 },
+ { "unknown CPU", 0 }
+};
+
+static const struct mly_code_lookup mly_table_stripe_size[] = {
+ { "NONE", MLY_STRIPE_ZERO },
+ { "512B", MLY_STRIPE_512b },
+ { "1k", MLY_STRIPE_1k },
+ { "2k", MLY_STRIPE_2k },
+ { "4k", MLY_STRIPE_4k },
+ { "8k", MLY_STRIPE_8k },
+ { "16k", MLY_STRIPE_16k },
+ { "32k", MLY_STRIPE_32k },
+ { "64k", MLY_STRIPE_64k },
+ { "128k", MLY_STRIPE_128k },
+ { "256k", MLY_STRIPE_256k },
+ { "512k", MLY_STRIPE_512k },
+ { "1M", MLY_STRIPE_1m },
+ { NULL, 0 },
+ { "unknown", 0 }
+};
+
+static const struct mly_code_lookup mly_table_cacheline_size[] = {
+ { "NONE", MLY_CACHELINE_ZERO },
+ { "512B", MLY_CACHELINE_512b },
+ { "1k", MLY_CACHELINE_1k },
+ { "2k", MLY_CACHELINE_2k },
+ { "4k", MLY_CACHELINE_4k },
+ { "8k", MLY_CACHELINE_8k },
+ { "16k", MLY_CACHELINE_16k },
+ { "32k", MLY_CACHELINE_32k },
+ { "64k", MLY_CACHELINE_64k },
+ { NULL, 0 },
+ { "unknown", 0 }
+};
+#endif /* notused */
+
+#endif /* !defined _PCI_MLY_TABLES_H_ */
diff --git a/sys/dev/pci/mlyio.h b/sys/dev/pci/mlyio.h
new file mode 100644
index 00000000000..b1ba5826b9c
--- /dev/null
+++ b/sys/dev/pci/mlyio.h
@@ -0,0 +1,111 @@
+/* $NetBSD: mlyio.h,v 1.1 2001/07/30 19:59:06 ad Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2001 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from FreeBSD: mlyio.h,v 1.2 2001/07/14 00:12:22 msmith Exp
+ */
+
+/*
+ * Control structures exchanged through the GAM interface with userland
+ * management tools.
+ *
+ * The member naming here is consistent with the Linux driver, with which this
+ * interface is basically compatible.
+ */
+
+#ifndef _PCI_MLYIO_H_
+#define _PCI_MLYIO_H_
+
+struct mly_user_command {
+ unsigned char ControllerNumber;
+ union mly_cmd_packet CommandMailbox;
+ int DataTransferLength;
+ int RequestSenseLength;
+ void *DataTransferBuffer;
+ void *RequestSenseBuffer;
+ int CommandStatus; /* not in the Linux structure */
+};
+
+#define MLYIO_COMMAND _IOWR('M', 200, struct mly_user_command)
+
+struct mly_user_health {
+ unsigned char ControllerNumber;
+ void *HealthStatusBuffer;
+};
+
+#define MLYIO_HEALTH _IOW('M', 201, struct mly_user_health)
+
+/*
+ * Command queue statistics
+ */
+
+#define MLYQ_FREE 0
+#define MLYQ_BUSY 1
+#define MLYQ_COMPLETE 2
+#define MLYQ_COUNT 3
+
+struct mly_qstat {
+ u_int32_t q_length;
+ u_int32_t q_max;
+};
+
+#endif /* !defined _PCI_MLYIO_ */
diff --git a/sys/dev/pci/mlyreg.h b/sys/dev/pci/mlyreg.h
new file mode 100644
index 00000000000..57036ba65b2
--- /dev/null
+++ b/sys/dev/pci/mlyreg.h
@@ -0,0 +1,1313 @@
+/* $NetBSD: mlyreg.h,v 1.1 2001/07/30 19:59:07 ad Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from FreeBSD: mlyreg.h,v 1.1 2000/08/23 03:22:39 msmith Exp
+ */
+
+/*
+ * Section numbers in this document refer to the Mylex "Firmware Software Interface"
+ * document ('FSI'), revision 0.11 04/11/00 unless otherwise qualified.
+ *
+ * Reference is made to the Mylex "Programming Guide for 6.x Controllers" document
+ * ('PG6'), document #771242 revision 0.02, 04/11/00
+ *
+ * Note that fields marked N/A are not supported by the PCI controllers, but are
+ * defined here to hold place in datastructures that are shared with the SCSI
+ * controllers. Items not relevant to PCI controllers are not described here.
+ *
+ * Ordering of items in this file is a little odd due to the constraints of
+ * nested declarations.
+ */
+
+#ifndef _PCI_MLYREG_H_
+#define _PCI_MLYREG_H_
+
+/*
+ * 2.1 (Scatter Gather List Format)
+ */
+struct mly_sg_entry {
+ u_int64_t physaddr;
+ u_int64_t length;
+} __attribute__ ((__packed__));
+
+/*
+ * 5.2 System Device Access
+ *
+ * This is corroborated by the layout of the MDACIOCTL_GETCONTROLLERINFO data
+ * in 21.8
+ */
+#define MLY_MAX_CHANNELS 6
+#define MLY_MAX_TARGETS 16
+#define MLY_MAX_LUNS 1
+
+/*
+ * 8.1 Different Device States
+ */
+#define MLY_DEVICE_STATE_OFFLINE 0x08 /* DEAD/OFFLINE */
+#define MLY_DEVICE_STATE_UNCONFIGURED 0x00
+#define MLY_DEVICE_STATE_ONLINE 0x01
+#define MLY_DEVICE_STATE_CRITICAL 0x09
+#define MLY_DEVICE_STATE_WRITEONLY 0x03
+#define MLY_DEVICE_STATE_STANDBY 0x21
+#define MLY_DEVICE_STATE_MISSING 0x04 /* or-ed with (ONLINE or WRITEONLY or STANDBY) */
+
+/*
+ * 8.2 Device Type Field definitions
+ */
+#define MLY_DEVICE_TYPE_RAID0 0x0 /* RAID 0 */
+#define MLY_DEVICE_TYPE_RAID1 0x1 /* RAID 1 */
+#define MLY_DEVICE_TYPE_RAID3 0x3 /* RAID 3 right asymmetric parity */
+#define MLY_DEVICE_TYPE_RAID5 0x5 /* RAID 5 right asymmetric parity */
+#define MLY_DEVICE_TYPE_RAID6 0x6 /* RAID 6 (Mylex RAID 6) */
+#define MLY_DEVICE_TYPE_RAID7 0x7 /* RAID 7 (JBOD) */
+#define MLY_DEVICE_TYPE_NEWSPAN 0x8 /* New Mylex SPAN */
+#define MLY_DEVICE_TYPE_RAID3F 0x9 /* RAID 3 fixed parity */
+#define MLY_DEVICE_TYPE_RAID3L 0xb /* RAID 3 left symmetric parity */
+#define MLY_DEVICE_TYPE_SPAN 0xc /* current spanning implementation */
+#define MLY_DEVICE_TYPE_RAID5L 0xd /* RAID 5 left symmetric parity */
+#define MLY_DEVICE_TYPE_RAIDE 0xe /* RAID E (concatenation) */
+#define MLY_DEVICE_TYPE_PHYSICAL 0xf /* physical device */
+
+/*
+ * 8.3 Stripe Size
+ */
+#define MLY_STRIPE_ZERO 0x0 /* no stripe (RAID 1, RAID 7, etc) */
+#define MLY_STRIPE_512b 0x1
+#define MLY_STRIPE_1k 0x2
+#define MLY_STRIPE_2k 0x3
+#define MLY_STRIPE_4k 0x4
+#define MLY_STRIPE_8k 0x5
+#define MLY_STRIPE_16k 0x6
+#define MLY_STRIPE_32k 0x7
+#define MLY_STRIPE_64k 0x8
+#define MLY_STRIPE_128k 0x9
+#define MLY_STRIPE_256k 0xa
+#define MLY_STRIPE_512k 0xb
+#define MLY_STRIPE_1m 0xc
+
+/*
+ * 8.4 Cacheline Size
+ */
+#define MLY_CACHELINE_ZERO 0x0 /* caching cannot be enabled */
+#define MLY_CACHELINE_512b 0x1
+#define MLY_CACHELINE_1k 0x2
+#define MLY_CACHELINE_2k 0x3
+#define MLY_CACHELINE_4k 0x4
+#define MLY_CACHELINE_8k 0x5
+#define MLY_CACHELINE_16k 0x6
+#define MLY_CACHELINE_32k 0x7
+#define MLY_CACHELINE_64k 0x8
+
+/*
+ * 8.5 Read/Write control
+ */
+#define MLY_RWCtl_INITTED (1<<7) /* if set, the logical device is initialised */
+ /* write control */
+#define MLY_RWCtl_WCD (0) /* write cache disabled */
+#define MLY_RWCtl_WDISABLE (1<<3) /* writing disabled */
+#define MLY_RWCtl_WCE (2<<3) /* write cache enabled */
+#define MLY_RWCtl_IWCE (3<<3) /* intelligent write cache enabled */
+ /* read control */
+#define MLY_RWCtl_RCD (0) /* read cache is disabled */
+#define MLY_RWCtl_RCE (1) /* read cache enabled */
+#define MLY_RWCtl_RAHEAD (2) /* readahead enabled */
+#define MLY_RWCtl_IRAHEAD (3) /* intelligent readahead enabled */
+
+/*
+ * 9.0 LUN Map Format
+ */
+struct mly_lun_map {
+ u_int8_t res1:4;
+ u_int8_t host_port_mapped:1; /* this system drive visibile to host on this controller/port combination */
+ u_int8_t tid_valid:1; /* target ID valid */
+ u_int8_t hid_valid:1; /* host ID valid */
+ u_int8_t lun_valid:1; /* LUN valid */
+ u_int8_t res2;
+ u_int8_t lun; /* LUN */
+ u_int8_t tid; /* TID */
+ u_int8_t hid[32]; /* HID (one bit for each host) */
+} __attribute__ ((__packed__));
+
+/*
+ * 10.1 Controller Parameters
+ */
+struct mly_param_controller {
+ u_int8_t rdahen:1; /* N/A */
+ u_int8_t bilodly:1; /* N/A */
+ u_int8_t fua_disable:1;
+ u_int8_t reass1s:1; /* N/A */
+ u_int8_t truvrfy:1; /* N/A */
+ u_int8_t dwtvrfy:1; /* N/A */
+ u_int8_t background_initialisation:1;
+ u_int8_t clustering:1; /* N/A */
+
+ u_int8_t bios_disable:1;
+ u_int8_t boot_from_cdrom:1;
+ u_int8_t drive_coercion:1;
+ u_int8_t write_same_disable:1;
+ u_int8_t hba_mode:1; /* N/A */
+ u_int8_t bios_geometry:2;
+#define MLY_BIOSGEOM_2G 0x0
+#define MLY_BIOSGEOM_8G 0x1
+ u_int8_t res1:1; /* N/A */
+
+ u_int8_t res2[2]; /* N/A */
+
+ u_int8_t v_dec:1;
+ u_int8_t safte:1; /* N/A */
+ u_int8_t ses:1; /* N/A */
+ u_int8_t res3:2; /* N/A */
+ u_int8_t v_arm:1;
+ u_int8_t v_ofm:1;
+ u_int8_t res4:1; /* N/A */
+
+ u_int8_t rebuild_check_rate;
+ u_int8_t cache_line_size; /* see 8.4 */
+ u_int8_t oem_code;
+#define MLY_OEM_MYLEX 0x00
+#define MLY_OEM_IBM 0x08
+#define MLY_OEM_HP 0x0a
+#define MLY_OEM_DEC 0x0c
+#define MLY_OEM_SIEMENS 0x10
+#define MLY_OEM_INTEL 0x12
+ u_int8_t spinup_mode;
+#define MLY_SPIN_AUTO 0
+#define MLY_SPIN_PWRSPIN 1
+#define MLY_SPIN_WSSUSPIN 2
+ u_int8_t spinup_devices;
+ u_int8_t spinup_interval;
+ u_int8_t spinup_wait_time;
+
+ u_int8_t res5:3; /* N/A */
+ u_int8_t vutursns:1; /* N/A */
+ u_int8_t dccfil:1; /* N/A */
+ u_int8_t nopause:1; /* N/A */
+ u_int8_t disqfull:1; /* N/A */
+ u_int8_t disbusy:1; /* N/A */
+
+ u_int8_t res6:2; /* N/A */
+ u_int8_t failover_node_name; /* N/A */
+ u_int8_t res7:1; /* N/A */
+ u_int8_t ftopo:3; /* N/A */
+ u_int8_t disable_ups:1; /* N/A */
+
+ u_int8_t res8:1; /* N/A */
+ u_int8_t propagate_reset:1; /* N/A */
+ u_int8_t nonstd_mp_reset:1; /* N/A */
+ u_int8_t res9:5; /* N/A */
+
+ u_int8_t res10; /* N/A */
+ u_int8_t serial_port_baud_rate; /* N/A */
+ u_int8_t serial_port_control; /* N/A */
+ u_int8_t change_stripe_ok_developer_flag_only; /* N/A */
+
+ u_int8_t small_large_host_transfers:2; /* N/A */
+ u_int8_t frame_control:2; /* N/A */
+ u_int8_t pci_latency_control:2; /* N/A */
+ u_int8_t treat_lip_as_reset:1; /* N/A */
+ u_int8_t res11:1; /* N/A */
+
+ u_int8_t ms_autorest:1; /* N/A */
+ u_int8_t res12:7; /* N/A */
+
+ u_int8_t ms_aa_fsim:1; /* N/A */
+ u_int8_t ms_aa_ccach:1; /* N/A */
+ u_int8_t ms_aa_fault_signals:1; /* N/A */
+ u_int8_t ms_aa_c4_faults:1; /* N/A */
+ u_int8_t ms_aa_host_reset_delay_mask:4; /* N/A */
+
+ u_int8_t ms_flg_simplex_no_rstcom:1; /* N/A */
+ u_int8_t res13:7; /* N/A */
+
+ u_int8_t res14; /* N/A */
+ u_int8_t hardloopid[2][2]; /* N/A */
+ u_int8_t ctrlname[2][16+1]; /* N/A */
+ u_int8_t initiator_id;
+ u_int8_t startup_option;
+#define MLY_STARTUP_IF_NO_CHANGE 0x0
+#define MLY_STARTUP_IF_NO_LUN_CHANGE 0x1
+#define MLY_STARTUP_IF_NO_LUN_OFFLINE 0x2
+#define MLY_STARTUP_IF_LUN0_NO_CHANGE 0x3
+#define MLY_STARTUP_IF_LUN0_NOT_OFFLINE 0x4
+#define MLY_STARTUP_ALWAYS 0x5
+
+ u_int8_t res15[62];
+} __attribute__ ((__packed__));
+
+/*
+ * 10.2 Physical Device Parameters
+ */
+struct mly_param_physical_device {
+ u_int16_t tags;
+ u_int16_t speed;
+ u_int8_t width;
+ u_int8_t combing:1;
+ u_int8_t res1:7;
+ u_int8_t res2[3];
+} __attribute__ ((__packed__));
+
+/*
+ * 10.3 Logical Device Parameters
+ */
+struct mly_param_logical_device {
+ u_int8_t type; /* see 8.2 */
+ u_int8_t state; /* see 8.1 */
+ u_int16_t raid_device;
+ u_int8_t res1;
+ u_int8_t bios_geometry; /* BIOS control word? */
+ u_int8_t stripe_size; /* see 8.3 */
+ u_int8_t read_write_control; /* see 8.5 */
+ u_int8_t res2[8];
+} __attribute__ ((__packed__));
+
+/*
+ * 12.3 Health Status Buffer
+ *
+ * Pad to 128 bytes.
+ */
+struct mly_health_status {
+ u_int32_t uptime_us; /* N/A */
+ u_int32_t uptime_ms; /* N/A */
+ u_int32_t realtime; /* N/A */
+ u_int32_t res1; /* N/A */
+ u_int32_t change_counter;
+ u_int32_t res2; /* N/A */
+ u_int32_t debug_message_index; /* N/A */
+ u_int32_t bios_message_index; /* N/A */
+ u_int32_t trace_page; /* N/A */
+ u_int32_t profiler_page; /* N/A */
+ u_int32_t next_event;
+ u_int8_t res3[4 + 16 + 64]; /* N/A */
+} __attribute__ ((__packed__));
+
+/*
+ * 14.2 Timeout Bit Format
+ */
+#define MLY_TIMEOUT_SECONDS 0x00
+#define MLY_TIMEOUT_MINUTES 0x40
+#define MLY_TIMEOUT_HOURS 0x80
+
+/*
+ * 14.3 Operation Device
+ */
+#define MLY_OPDEVICE_PHYSICAL_DEVICE 0x0
+#define MLY_OPDEVICE_RAID_DEVICE 0x1
+#define MLY_OPDEVICE_PHYSICAL_CHANNEL 0x2
+#define MLY_OPDEVICE_RAID_CHANNEL 0x3
+#define MLY_OPDEVICE_PHYSICAL_CONTROLLER 0x4
+#define MLY_OPDEVICE_RAID_CONTROLLER 0x5
+#define MLY_OPDEVICE_CONFIGURATION_GROUP 0x10
+
+/*
+ * 14.4 Status Bit Format
+ *
+ * AKA Status Mailbox Format
+ *
+ * XXX format conflict between FSI and PG6 over the ordering of the
+ * status and sense length fields.
+ */
+struct mly_status {
+ u_int16_t command_id;
+ u_int8_t status;
+ u_int8_t sense_length;
+ int32_t residue;
+} __attribute__ ((__packed__));
+
+/*
+ * 14.5 Command Control Bit (CCB) format
+ *
+ * This byte is unfortunately named.
+ */
+#define MLY_CMDCTL_FORCE_UNIT_ACCESS 0x01
+#define MLY_CMDCTL_DISABLE_PAGE_OUT 0x02
+#define MLY_CMDCTL_EXTENDED_SG_TABLE 0x08
+#define MLY_CMDCTL_DATA_DIRECTION 0x10
+#define MLY_CMDCTL_NO_AUTO_SENSE 0x40
+#define MLY_CMDCTL_DISABLE_DISCONNECT 0x80
+
+/*
+ * 15.0 Commands
+ *
+ * We use the command names as given by Mylex
+ */
+#define MDACMD_MEMCOPY 0x1 /* memory to memory copy */
+#define MDACMD_SCSIPT 0x2 /* SCSI passthrough (small command) */
+#define MDACMD_SCSILCPT 0x3 /* SCSI passthrough (large command) */
+#define MDACMD_SCSI 0x4 /* SCSI command for logical/phyiscal device (small command) */
+#define MDACMD_SCSILC 0x5 /* SCSI command for logical/phyiscal device (large command) */
+#define MDACMD_IOCTL 0x20 /* Management command */
+#define MDACMD_IOCTLCHECK 0x23 /* Validate management command (not implemented) */
+
+/*
+ * 16.0 IOCTL command
+ *
+ * We use the IOCTL names as given by Mylex
+ * Note that only ioctls supported by the PCI controller family are listed
+ */
+#define MDACIOCTL_GETCONTROLLERINFO 0x1
+#define MDACIOCTL_GETLOGDEVINFOVALID 0x3
+#define MDACIOCTL_GETPHYSDEVINFOVALID 0x5
+#define MDACIOCTL_GETCONTROLLERSTATISTICS 0xb
+#define MDACIOCTL_GETLOGDEVSTATISTICS 0xd
+#define MDACIOCTL_GETPHYSDEVSTATISTICS 0xf
+#define MDACIOCTL_GETHEALTHSTATUS 0x11
+#define MDACIOCTL_GETEVENT 0x15
+/* flash update */
+#define MDACIOCTL_STOREIMAGE 0x2c
+#define MDACIOCTL_READIMAGE 0x2d
+#define MDACIOCTL_FLASHIMAGES 0x2e
+/* battery backup unit */
+#define MDACIOCTL_GET_SUBSYSTEM_DATA 0x70
+#define MDACIOCTL_SET_SUBSYSTEM_DATA 0x71
+/* non-data commands */
+#define MDACIOCTL_STARTDISOCVERY 0x81
+#define MDACIOCTL_SETRAIDDEVSTATE 0x82
+#define MDACIOCTL_INITPHYSDEVSTART 0x84
+#define MDACIOCTL_INITPHYSDEVSTOP 0x85
+#define MDACIOCTL_INITRAIDDEVSTART 0x86
+#define MDACIOCTL_INITRAIDDEVSTOP 0x87
+#define MDACIOCTL_REBUILDRAIDDEVSTART 0x88
+#define MDACIOCTL_REBUILDRAIDDEVSTOP 0x89
+#define MDACIOCTL_MAKECONSISTENTDATASTART 0x8a
+#define MDACIOCTL_MAKECONSISTENTDATASTOP 0x8b
+#define MDACIOCTL_CONSISTENCYCHECKSTART 0x8c
+#define MDACIOCTL_CONSISTENCYCHECKSTOP 0x8d
+#define MDACIOCTL_SETMEMORYMAILBOX 0x8e
+#define MDACIOCTL_RESETDEVICE 0x90
+#define MDACIOCTL_FLUSHDEVICEDATA 0x91
+#define MDACIOCTL_PAUSEDEVICE 0x92
+#define MDACIOCTL_UNPAUSEDEVICE 0x93
+#define MDACIOCTL_LOCATEDEVICE 0x94
+#define MDACIOCTL_SETMASTERSLAVEMODE 0x95
+#define MDACIOCTL_SETREALTIMECLOCK 0xac
+/* RAID configuration */
+#define MDACIOCTL_CREATENEWCONF 0xc0
+#define MDACIOCTL_DELETERAIDDEV 0xc1
+#define MDACIOCTL_REPLACEINTERNALDEV 0xc2
+#define MDACIOCTL_RENAMERAIDDEV 0xc3
+#define MDACIOCTL_ADDNEWCONF 0xc4
+#define MDACIOCTL_XLATEPHYSDEVTORAIDDEV 0xc5
+#define MDACIOCTL_MORE 0xc6
+#define MDACIOCTL_SETPHYSDEVPARAMETER 0xc8
+#define MDACIOCTL_GETPHYSDEVPARAMETER 0xc9
+#define MDACIOCTL_CLEARCONF 0xca
+#define MDACIOCTL_GETDEVCONFINFO 0xcb
+#define MDACIOCTL_GETGROUPCONFINFO 0xcc
+#define MDACIOCTL_GETFREESPACELIST 0xcd
+#define MDACIOCTL_GETLOGDEVPARAMETER 0xce
+#define MDACIOCTL_SETLOGDEVPARAMETER 0xcf
+#define MDACIOCTL_GETCONTROLLERPARAMETER 0xd0
+#define MDACIOCTL_SETCONTRLLERPARAMETER 0xd1
+#define MDACIOCTL_CLEARCONFSUSPMODE 0xd2
+#define MDACIOCTL_GETBDT_FOR_SYSDRIVE 0xe0
+
+/*
+ * 17.1.4 Data Transfer Memory Address Without SG List
+ */
+struct mly_short_transfer {
+ struct mly_sg_entry sg[2];
+} __attribute__ ((__packed__));
+
+/*
+ * 17.1.5 Data Transfer Memory Address With SG List
+ *
+ * Note that only the first s/g table is currently used.
+ */
+struct mly_sg_transfer {
+ u_int16_t entries[3];
+ u_int16_t res1;
+ u_int64_t table_physaddr[3];
+} __attribute__ ((__packed__));
+
+/*
+ * 17.1.3 Data Transfer Memory Address Format
+ */
+union mly_cmd_transfer {
+ struct mly_short_transfer direct;
+ struct mly_sg_transfer indirect;
+};
+
+/*
+ * 21.1 MDACIOCTL_SETREALTIMECLOCK
+ * 21.7 MDACIOCTL_GETHEALTHSTATUS
+ * 21.8 MDACIOCTL_GETCONTROLLERINFO
+ * 21.9 MDACIOCTL_GETLOGDEVINFOVALID
+ * 21.10 MDACIOCTL_GETPHYSDEVINFOVALID
+ * 21.11 MDACIOCTL_GETPHYSDEVSTATISTICS
+ * 21.12 MDACIOCTL_GETLOGDEVSTATISTICS
+ * 21.13 MDACIOCTL_GETCONTROLLERSTATISTICS
+ * 21.27 MDACIOCTL_GETBDT_FOR_SYSDRIVE
+ * 23.4 MDACIOCTL_CREATENEWCONF
+ * 23.5 MDACIOCTL_ADDNEWCONF
+ * 23.8 MDACIOCTL_GETDEVCONFINFO
+ * 23.9 MDACIOCTL_GETFREESPACELIST
+ * 24.1 MDACIOCTL_MORE
+ * 25.1 MDACIOCTL_GETPHYSDEVPARAMETER
+ * 25.2 MDACIOCTL_SETPHYSDEVPARAMETER
+ * 25.3 MDACIOCTL_GETLOGDEVPARAMETER
+ * 25.4 MDACIOCTL_SETLOGDEVPARAMETER
+ * 25.5 MDACIOCTL_GETCONTROLLERPARAMETER
+ * 25.6 MDACIOCTL_SETCONTROLLERPARAMETER
+ *
+ * These commands just transfer data
+ */
+struct mly_ioctl_param_data {
+ u_int8_t param[10];
+ union mly_cmd_transfer transfer;
+} __attribute__ ((__packed__));
+
+/*
+ * 21.2 MDACIOCTL_SETMEMORYMAILBOX
+ */
+struct mly_ioctl_param_setmemorymailbox {
+ u_int8_t health_buffer_size;
+ u_int8_t res1;
+ u_int64_t health_buffer_physaddr;
+ u_int64_t command_mailbox_physaddr;
+ u_int64_t status_mailbox_physaddr;
+ u_int64_t res2[2];
+} __attribute__ ((__packed__));
+
+/*
+ * 21.8.2 MDACIOCTL_GETCONTROLLERINFO: Data Format
+ */
+struct mly_ioctl_getcontrollerinfo {
+ u_int8_t res1; /* N/A */
+ u_int8_t interface_type;
+ u_int8_t controller_type;
+ u_int8_t res2; /* N/A */
+ u_int16_t interface_speed;
+ u_int8_t interface_width;
+ u_int8_t res3[9]; /* N/A */
+ char interface_name[16];
+ char controller_name[16];
+ u_int8_t res4[16]; /* N/A */
+ /* firmware release information */
+ u_int8_t fw_major;
+ u_int8_t fw_minor;
+ u_int8_t fw_turn;
+ u_int8_t fw_build;
+ u_int8_t fw_day;
+ u_int8_t fw_month;
+ u_int8_t fw_century;
+ u_int8_t fw_year;
+ /* hardware release information */
+ u_int8_t hw_revision; /* N/A */
+ u_int8_t res5[3]; /* N/A */
+ u_int8_t hw_release_day; /* N/A */
+ u_int8_t hw_release_month; /* N/A */
+ u_int8_t hw_release_century; /* N/A */
+ u_int8_t hw_release_year; /* N/A */
+ /* hardware manufacturing information */
+ u_int8_t batch_number; /* N/A */
+ u_int8_t res6; /* N/A */
+ u_int8_t plant_number;
+ u_int8_t res7;
+ u_int8_t hw_manuf_day;
+ u_int8_t hw_manuf_month;
+ u_int8_t hw_manuf_century;
+ u_int8_t hw_manuf_year;
+ u_int8_t max_pdd_per_xldd;
+ u_int8_t max_ildd_per_xldd;
+ u_int16_t nvram_size;
+ u_int8_t max_number_of_xld; /* N/A */
+ u_int8_t res8[3]; /* N/A */
+ /* unique information per controller */
+ char serial_number[16];
+ u_int8_t res9[16]; /* N/A */
+ /* vendor information */
+ u_int8_t res10[3]; /* N/A */
+ u_int8_t oem_information;
+ char vendor_name[16]; /* N/A */
+ /* other physical/controller/operation information */
+ u_int8_t bbu_present:1;
+ u_int8_t active_clustering:1;
+ u_int8_t res11:6; /* N/A */
+ u_int8_t res12[3]; /* N/A */
+ /* physical device scan information */
+ u_int8_t physical_scan_active:1;
+ u_int8_t res13:7; /* N/A */
+ u_int8_t physical_scan_channel;
+ u_int8_t physical_scan_target;
+ u_int8_t physical_scan_lun;
+ /* maximum command data transfer size */
+ u_int16_t maximum_block_count;
+ u_int16_t maximum_sg_entries;
+ /* logical/physical device counts */
+ u_int16_t logical_devices_present;
+ u_int16_t logical_devices_critical;
+ u_int16_t logical_devices_offline;
+ u_int16_t physical_devices_present;
+ u_int16_t physical_disks_present;
+ u_int16_t physical_disks_critical; /* N/A */
+ u_int16_t physical_disks_offline;
+ u_int16_t maximum_parallel_commands;
+ /* channel and target ID information */
+ u_int8_t physical_channels_present;
+ u_int8_t virtual_channels_present;
+ u_int8_t physical_channels_possible;
+ u_int8_t virtual_channels_possible;
+ u_int8_t maximum_targets_possible[16]; /* N/A (6 and up) */
+ u_int8_t res14[12]; /* N/A */
+ /* memory/cache information */
+ u_int16_t memory_size;
+ u_int16_t cache_size;
+ u_int32_t valid_cache_size; /* N/A */
+ u_int32_t dirty_cache_size; /* N/A */
+ u_int16_t memory_speed;
+ u_int8_t memory_width;
+ u_int8_t memory_type:5;
+ u_int8_t res15:1; /* N/A */
+ u_int8_t memory_parity:1;
+ u_int8_t memory_ecc:1;
+ char memory_information[16]; /* N/A */
+ /* execution memory information */
+ u_int16_t exmemory_size;
+ u_int16_t l2cache_size; /* N/A */
+ u_int8_t res16[8]; /* N/A */
+ u_int16_t exmemory_speed;
+ u_int8_t exmemory_width;
+ u_int8_t exmemory_type:5;
+ u_int8_t res17:1; /* N/A */
+ u_int8_t exmemory_parity:1;
+ u_int8_t exmemory_ecc:1;
+ char exmemory_name[16]; /* N/A */
+ /* CPU information */
+ struct {
+ u_int16_t speed;
+ u_int8_t type;
+ u_int8_t number;
+ u_int8_t res1[12]; /* N/A */
+ char name[16]; /* N/A */
+ } cpu[2] __attribute__ ((__packed__));
+ /* debugging/profiling/command time tracing information */
+ u_int16_t profiling_page; /* N/A */
+ u_int16_t profiling_programs; /* N/A */
+ u_int16_t time_trace_page; /* N/A */
+ u_int16_t time_trace_programs; /* N/A */
+ u_int8_t res18[8]; /* N/A */
+ /* error counters on physical devices */
+ u_int16_t physical_device_bus_resets; /* N/A */
+ u_int16_t physical_device_parity_errors; /* N/A */
+ u_int16_t physical_device_soft_errors; /* N/A */
+ u_int16_t physical_device_commands_failed; /* N/A */
+ u_int16_t physical_device_miscellaneous_errors; /* N/A */
+ u_int16_t physical_device_command_timeouts; /* N/A */
+ u_int16_t physical_device_selection_timeouts; /* N/A */
+ u_int16_t physical_device_retries; /* N/A */
+ u_int16_t physical_device_aborts; /* N/A */
+ u_int16_t physical_device_host_command_aborts; /* N/A */
+ u_int16_t physical_device_PFAs_detected; /* N/A */
+ u_int16_t physical_device_host_commands_failed; /* N/A */
+ u_int8_t res19[8]; /* N/A */
+ /* error counters on logical devices */
+ u_int16_t logical_device_soft_errors; /* N/A */
+ u_int16_t logical_device_commands_failed; /* N/A */
+ u_int16_t logical_device_host_command_aborts; /* N/A */
+ u_int16_t res20; /* N/A */
+ /* error counters on controller */
+ u_int16_t controller_parity_ecc_errors;
+ u_int16_t controller_host_command_aborts; /* N/A */
+ u_int8_t res21[4]; /* N/A */
+ /* long duration activity information */
+ u_int16_t background_inits_active;
+ u_int16_t logical_inits_active;
+ u_int16_t physical_inits_active;
+ u_int16_t consistency_checks_active;
+ u_int16_t rebuilds_active;
+ u_int16_t MORE_active;
+ u_int16_t patrol_active; /* N/A */
+ u_int8_t long_operation_status; /* N/A */
+ u_int8_t res22; /* N/A */
+ /* flash ROM information */
+ u_int8_t flash_type; /* N/A */
+ u_int8_t res23; /* N/A */
+ u_int16_t flash_size;
+ u_int32_t flash_maximum_age;
+ u_int32_t flash_age;
+ u_int8_t res24[4]; /* N/A */
+ char flash_name[16]; /* N/A */
+ /* firmware runtime information */
+ u_int8_t rebuild_rate;
+ u_int8_t background_init_rate;
+ u_int8_t init_rate;
+ u_int8_t consistency_check_rate;
+ u_int8_t res25[4]; /* N/A */
+ u_int32_t maximum_dp;
+ u_int32_t free_dp;
+ u_int32_t maximum_iop;
+ u_int32_t free_iop;
+ u_int16_t maximum_comb_length;
+ u_int16_t maximum_configuration_groups;
+ u_int8_t installation_abort:1;
+ u_int8_t maintenance:1;
+ u_int8_t res26:6; /* N/A */
+ u_int8_t res27[3]; /* N/A */
+ u_int8_t res28[32 + 512]; /* N/A */
+} __attribute__ ((__packed__));
+
+/*
+ * 21.9.2 MDACIOCTL_GETLOGDEVINFOVALID
+ */
+struct mly_ioctl_getlogdevinfovalid {
+ u_int8_t res1; /* N/A */
+ u_int8_t channel;
+ u_int8_t target;
+ u_int8_t lun;
+ u_int8_t state; /* see 8.1 */
+ u_int8_t raid_level; /* see 8.2 */
+ u_int8_t stripe_size; /* see 8.3 */
+ u_int8_t cache_line_size; /* see 8.4 */
+ u_int8_t read_write_control; /* see 8.5 */
+ u_int8_t consistency_check:1;
+ u_int8_t rebuild:1;
+ u_int8_t make_consistent:1;
+ u_int8_t initialisation:1;
+ u_int8_t migration:1;
+ u_int8_t patrol:1;
+ u_int8_t res2:2; /* N/A */
+ u_int8_t ar5_limit;
+ u_int8_t ar5_algo;
+ u_int16_t logical_device_number;
+ u_int16_t bios_control;
+ /* erorr counters */
+ u_int16_t soft_errors; /* N/A */
+ u_int16_t commands_failed; /* N/A */
+ u_int16_t host_command_aborts; /* N/A */
+ u_int16_t deferred_write_errors; /* N/A */
+ u_int8_t res3[8]; /* N/A */
+ /* device size information */
+ u_int8_t res4[2]; /* N/A */
+ u_int16_t device_block_size;
+ u_int32_t original_device_size; /* N/A */
+ u_int32_t device_size; /* XXX "blocks or MB" Huh? */
+ u_int8_t res5[4]; /* N/A */
+ char device_name[32]; /* N/A */
+ u_int8_t inquiry[36];
+ u_int8_t res6[12]; /* N/A */
+ u_int64_t last_read_block; /* N/A */
+ u_int64_t last_written_block; /* N/A */
+ u_int64_t consistency_check_block;
+ u_int64_t rebuild_block;
+ u_int64_t make_consistent_block;
+ u_int64_t initialisation_block;
+ u_int64_t migration_block;
+ u_int64_t patrol_block; /* N/A */
+ u_int8_t res7[64]; /* N/A */
+} __attribute__ ((__packed__));
+
+/*
+ * 21.10.2 MDACIOCTL_GETPHYSDEVINFOVALID: Data Format
+ */
+struct mly_ioctl_getphysdevinfovalid {
+ u_int8_t res1;
+ u_int8_t channel;
+ u_int8_t target;
+ u_int8_t lun;
+ u_int8_t raid_ft:1; /* configuration status */
+ u_int8_t res2:1; /* N/A */
+ u_int8_t local:1;
+ u_int8_t res3:5;
+ u_int8_t host_dead:1; /* multiple host/controller status *//* N/A */
+ u_int8_t host_connection_dead:1; /* N/A */
+ u_int8_t res4:6; /* N/A */
+ u_int8_t state; /* see 8.1 */
+ u_int8_t width;
+ u_int16_t speed;
+ /* multiported physical device information */
+ u_int8_t ports_available; /* N/A */
+ u_int8_t ports_inuse; /* N/A */
+ u_int8_t res5[4];
+ u_int8_t ether_address[16]; /* N/A */
+ u_int16_t command_tags;
+ u_int8_t consistency_check:1; /* N/A */
+ u_int8_t rebuild:1; /* N/A */
+ u_int8_t make_consistent:1; /* N/A */
+ u_int8_t initialisation:1;
+ u_int8_t migration:1; /* N/A */
+ u_int8_t patrol:1; /* N/A */
+ u_int8_t res6:2;
+ u_int8_t long_operation_status; /* N/A */
+ u_int8_t parity_errors;
+ u_int8_t soft_errors;
+ u_int8_t hard_errors;
+ u_int8_t miscellaneous_errors;
+ u_int8_t command_timeouts; /* N/A */
+ u_int8_t retries; /* N/A */
+ u_int8_t aborts; /* N/A */
+ u_int8_t PFAs_detected; /* N/A */
+ u_int8_t res7[6];
+ u_int16_t block_size;
+ u_int32_t original_device_size; /* XXX "blocks or MB" Huh? */
+ u_int32_t device_size; /* XXX "blocks or MB" Huh? */
+ u_int8_t res8[4];
+ char name[16]; /* N/A */
+ u_int8_t res9[16 + 32];
+ u_int8_t inquiry[36];
+ u_int8_t res10[12 + 16];
+ u_int64_t last_read_block; /* N/A */
+ u_int64_t last_written_block; /* N/A */
+ u_int64_t consistency_check_block; /* N/A */
+ u_int64_t rebuild_block; /* N/A */
+ u_int64_t make_consistent_block; /* N/A */
+ u_int64_t initialisation_block; /* N/A */
+ u_int64_t migration_block; /* N/A */
+ u_int64_t patrol_block; /* N/A */
+ u_int8_t res11[256];
+} __attribute__ ((__packed__));
+
+union mly_devinfo {
+ struct mly_ioctl_getlogdevinfovalid logdev;
+ struct mly_ioctl_getphysdevinfovalid physdev;
+};
+
+/*
+ * 21.11.2 MDACIOCTL_GETPHYSDEVSTATISTICS: Data Format
+ * 21.12.2 MDACIOCTL_GETLOGDEVSTATISTICS: Data Format
+ */
+struct mly_ioctl_getdevstatistics {
+ u_int32_t uptime_ms; /* getphysedevstatistics only */
+ u_int8_t res1[5]; /* N/A */
+ u_int8_t channel;
+ u_int8_t target;
+ u_int8_t lun;
+ u_int16_t raid_device; /* getlogdevstatistics only */
+ u_int8_t res2[2]; /* N/A */
+ /* total read/write performance including cache data */
+ u_int32_t total_reads;
+ u_int32_t total_writes;
+ u_int32_t total_read_size;
+ u_int32_t total_write_size;
+ /* cache read/write performance */
+ u_int32_t cache_reads; /* N/A */
+ u_int32_t cache_writes; /* N/A */
+ u_int32_t cache_read_size; /* N/A */
+ u_int32_t cache_write_size; /* N/A */
+ /* commands active/wait information */
+ u_int32_t command_waits_done; /* N/A */
+ u_int16_t active_commands; /* N/A */
+ u_int16_t waiting_commands; /* N/A */
+ u_int8_t res3[8]; /* N/A */
+} __attribute__ ((__packed__));
+
+/*
+ * 21.13.2 MDACIOCTL_GETCONTROLLERSTATISTICS: Data Format
+ */
+struct mly_ioctl_getcontrollerstatistics {
+ u_int32_t uptime_ms; /* N/A */
+ u_int8_t res1[12]; /* N/A */
+ /* target physical device performance data information */
+ u_int32_t target_physical_device_interrupts; /* N/A */
+ u_int32_t target_physical_device_stray_interrupts;/* N/A */
+ u_int8_t res2[8]; /* N/A */
+ u_int32_t target_physical_device_reads; /* N/A */
+ u_int32_t target_physical_device_writes; /* N/A */
+ u_int32_t target_physical_device_read_size; /* N/A */
+ u_int32_t target_physical_device_write_size; /* N/A */
+ /* host system performance data information */
+ u_int32_t host_system_interrupts; /* N/A */
+ u_int32_t host_system_stray_interrupts; /* N/A */
+ u_int32_t host_system_sent_interrupts; /* N/A */
+ u_int8_t res3[4]; /* N/A */
+ u_int32_t physical_device_reads; /* N/A */
+ u_int32_t physical_device_writes; /* N/A */
+ u_int32_t physical_device_read_size; /* N/A */
+ u_int32_t physical_device_write_size; /* N/A */
+ u_int32_t physical_device_cache_reads; /* N/A */
+ u_int32_t physical_device_cache_writes; /* N/A */
+ u_int32_t physical_device_cache_read_size; /* N/A */
+ u_int32_t physical_device_cache_write_size; /* N/A */
+ u_int32_t logical_device_reads; /* N/A */
+ u_int32_t logical_device_writes; /* N/A */
+ u_int32_t logical_device_read_size; /* N/A */
+ u_int32_t logical_device_write_size; /* N/A */
+ u_int32_t logical_device_cache_reads; /* N/A */
+ u_int32_t logical_device_cache_writes; /* N/A */
+ u_int32_t logical_device_cache_read_size; /* N/A */
+ u_int32_t logical_device_cache_write_size; /* N/A */
+ u_int16_t target_physical_device_commands_active; /* N/A */
+ u_int16_t target_physical_device_commands_waiting;/* N/A */
+ u_int16_t host_system_commands_active; /* N/A */
+ u_int16_t host_system_commands_waiting; /* N/A */
+ u_int8_t res4[48 + 64]; /* N/A */
+} __attribute__ ((__packed__));
+
+/*
+ * 21.2 MDACIOCTL_SETRAIDDEVSTATE
+ */
+struct mly_ioctl_param_setraiddevstate {
+ u_int8_t state;
+} __attribute__ ((__packed__));
+
+/*
+ * 21.27.2 MDACIOCTL_GETBDT_FOR_SYSDRIVE: Data Format
+ */
+#define MLY_MAX_BDT_ENTRIES 1022
+struct mly_ioctl_getbdt_for_sysdrive {
+ u_int32_t num_of_bdt_entries;
+ u_int32_t bad_data_block_address[MLY_MAX_BDT_ENTRIES];
+} __attribute__ ((__packed__));
+
+/*
+ * 22.1 Physical Device Definition (PDD)
+ */
+struct mly_pdd {
+ u_int8_t type; /* see 8.2 */
+ u_int8_t state; /* see 8.1 */
+ u_int16_t raid_device;
+ u_int32_t device_size; /* XXX "block or MB" Huh? */
+ u_int8_t controller;
+ u_int8_t channel;
+ u_int8_t target;
+ u_int8_t lun;
+ u_int32_t start_address;
+} __attribute__ ((__packed__));
+
+/*
+ * 22.2 RAID Device Use Definition (UDD)
+ */
+struct mly_udd {
+ u_int8_t res1;
+ u_int8_t state; /* see 8.1 */
+ u_int16_t raid_device;
+ u_int32_t start_address;
+} __attribute__ ((__packed__));
+
+/*
+ * RAID Device Definition (LDD)
+ */
+struct mly_ldd {
+ u_int8_t type; /* see 8.2 */
+ u_int8_t state; /* see 8.1 */
+ u_int16_t raid_device;
+ u_int32_t device_size; /* XXX "block or MB" Huh? */
+ u_int8_t devices_used_count;
+ u_int8_t stripe_size; /* see 8.3 */
+ u_int8_t cache_line_size; /* see 8.4 */
+ u_int8_t read_write_control; /* see 8.5 */
+ u_int32_t devices_used_size; /* XXX "block or MB" Huh? */
+ u_int16_t devices_used[32]; /* XXX actual size of this field unknown! */
+} __attribute__ ((__packed__));
+
+/*
+ * Define a datastructure giving the smallest allocation that will hold
+ * a PDD, UDD or LDD for MDACIOCTL_GETDEVCONFINFO.
+ */
+struct mly_devconf_hdr {
+ u_int8_t type; /* see 8.2 */
+ u_int8_t state; /* see 8.1 */
+ u_int16_t raid_device;
+};
+
+union mly_ioctl_devconfinfo {
+ struct mly_pdd pdd;
+ struct mly_udd udd;
+ struct mly_ldd ldd;
+ struct mly_devconf_hdr hdr;
+};
+
+/*
+ * 22.3 MDACIOCTL_RENAMERAIDDEV
+ *
+ * XXX this command is listed as transferring data, but does not define the data.
+ */
+struct mly_ioctl_param_renameraiddev {
+ u_int8_t new_raid_device;
+} __attribute__ ((__packed__));
+
+/*
+ * 23.6.2 MDACIOCTL_XLATEPHYSDEVTORAIDDEV
+ *
+ * XXX documentation suggests this format will change
+ */
+struct mly_ioctl_param_xlatephysdevtoraiddev {
+ u_int16_t raid_device;
+ u_int8_t res1[2];
+ u_int8_t controller;
+ u_int8_t channel;
+ u_int8_t target;
+ u_int8_t lun;
+} __attribute__ ((__packed__));
+
+/*
+ * 23.7 MDACIOCTL_GETGROUPCONFINFO
+ */
+struct mly_ioctl_param_getgroupconfinfo {
+ u_int16_t group;
+ u_int8_t res1[8];
+ union mly_cmd_transfer transfer;
+} __attribute__ ((__packed__));
+
+/*
+ * 23.9.2 MDACIOCTL_GETFREESPACELIST: Data Format
+ *
+ * The controller will populate as much of this structure as is provided,
+ * or as is required to fully list the free space available.
+ */
+struct mly_ioctl_getfreespacelist_entry {
+ u_int16_t raid_device;
+ u_int8_t res1[6];
+ u_int32_t address; /* XXX "blocks or MB" Huh? */
+ u_int32_t size; /* XXX "blocks or MB" Huh? */
+} __attribute__ ((__packed__));
+
+struct mly_ioctl_getfrespacelist {
+ u_int16_t returned_entries;
+ u_int16_t total_entries;
+ u_int8_t res1[12];
+ struct mly_ioctl_getfreespacelist_entry space[0]; /* expand to suit */
+} __attribute__ ((__packed__));
+
+/*
+ * 27.1 MDACIOCTL_GETSUBSYSTEMDATA
+ * 27.2 MDACIOCTL_SETSUBSYSTEMDATA
+ *
+ * PCI controller only supports a limited subset of the possible operations.
+ *
+ * XXX where does the status end up? (the command transfers no data)
+ */
+struct mly_ioctl_param_subsystemdata {
+ u_int8_t operation:4;
+#define MLY_BBU_GETSTATUS 0x00
+#define MLY_BBU_SET_THRESHOLD 0x00 /* minutes in param[0,1] */
+ u_int8_t subsystem:4;
+#define MLY_SUBSYSTEM_BBU 0x01
+ u_int parameter[3]; /* only for SETSUBSYSTEMDATA */
+} __attribute__ ((__packed__));
+
+struct mly_ioctl_getsubsystemdata_bbustatus {
+ u_int16_t current_power;
+ u_int16_t maximum_power;
+ u_int16_t power_threshold;
+ u_int8_t charge_level;
+ u_int8_t hardware_version;
+ u_int8_t battery_type;
+#define MLY_BBU_TYPE_UNKNOWN 0x00
+#define MLY_BBU_TYPE_NICAD 0x01
+#define MLY_BBU_TYPE_MISSING 0xfe
+ u_int8_t res1;
+ u_int8_t operation_status;
+#define MLY_BBU_STATUS_NO_SYNC 0x01
+#define MLY_BBU_STATUS_OUT_OF_SYNC 0x02
+#define MLY_BBU_STATUS_FIRST_WARNING 0x04
+#define MLY_BBU_STATUS_SECOND_WARNING 0x08
+#define MLY_BBU_STATUS_RECONDITIONING 0x10
+#define MLY_BBU_STATUS_DISCHARGING 0x20
+#define MLY_BBU_STATUS_FASTCHARGING 0x40
+ u_int8_t res2;
+} __attribute__ ((__packed__));
+
+/*
+ * 28.9 MDACIOCTL_RESETDEVICE
+ * 28.10 MDACIOCTL_FLUSHDEVICEDATA
+ * 28.11 MDACIOCTL_PAUSEDEVICE
+ * 28.12 MDACIOCTL_UNPAUSEDEVICE
+ */
+struct mly_ioctl_param_deviceoperation {
+ u_int8_t operation_device; /* see 14.3 */
+} __attribute__ ((__packed__));
+
+/*
+ * 31.1 Event Data Format
+ */
+struct mly_event {
+ u_int32_t sequence_number;
+ u_int32_t timestamp;
+ u_int32_t code;
+ u_int8_t controller;
+ u_int8_t channel;
+ u_int8_t target; /* also enclosure */
+ u_int8_t lun; /* also enclosure unit */
+ u_int8_t res1[4];
+ u_int32_t param;
+ u_int8_t sense[40];
+} __attribute__ ((__packed__));
+
+/*
+ * 31.2 MDACIOCTL_GETEVENT
+ */
+struct mly_ioctl_param_getevent {
+ u_int16_t sequence_number_low;
+ u_int8_t res1[8];
+ union mly_cmd_transfer transfer;
+} __attribute__ ((__packed__));
+
+union mly_ioctl_param {
+ struct mly_ioctl_param_data data;
+ struct mly_ioctl_param_setmemorymailbox setmemorymailbox;
+ struct mly_ioctl_param_setraiddevstate setraiddevstate;
+ struct mly_ioctl_param_renameraiddev renameraiddev;
+ struct mly_ioctl_param_xlatephysdevtoraiddev xlatephysdevtoraiddev;
+ struct mly_ioctl_param_getgroupconfinfo getgroupconfinfo;
+ struct mly_ioctl_param_subsystemdata subsystemdata;
+ struct mly_ioctl_param_deviceoperation deviceoperation;
+ struct mly_ioctl_param_getevent getevent;
+};
+
+/*
+ * 19 SCSI Command Format
+ */
+#define MLY_PHYADDR(c, b, t, l) \
+ ((l) | ((t) << 8) | ((b) << 16) | ((c) << 19))
+#define MLY_LOGADDR(c, u) \
+ ((u) | ((c) << 19))
+
+#define MLY_LOGADDR_DEV(a) (a & 0xffff)
+#define MLY_LOGADDR_CTLR(a) (a >> 19)
+
+#define MLY_PHYADDR_LUN(a) (a & 0xff)
+#define MLY_PHYADDR_TARGET(a) ((a >> 8) & 0xff)
+#define MLY_PHYADDR_CHANNEL(a) ((a >> 16) & 0x07)
+#define MLY_PHYADDR_CTLR(a) ((a >> 19) & 0x1f)
+
+/*
+ * struct mly_cmd_address_physical {
+ * u_int8_t lun;
+ * u_int8_t target;
+ * u_int8_t channel:3;
+ * u_int8_t controller:5;
+ * } __attribute__ ((__packed__));
+ *
+ * struct mly_cmd_address_logical {
+ * u_int16_t logdev;
+ * u_int8_t res1:3;
+ * u_int8_t controller:5;
+ * } __attribute__ ((__packed__));
+ */
+
+struct mly_cmd_generic {
+ u_int16_t command_id;
+ u_int8_t opcode;
+ u_int8_t command_control;
+ u_int32_t data_size;
+ u_int64_t sense_buffer_address;
+ u_int8_t addr[3];
+ u_int8_t timeout;
+ u_int8_t maximum_sense_size;
+ u_int8_t res1[11];
+ union mly_cmd_transfer transfer;
+} __attribute__ ((__packed__));
+
+
+/*
+ * 19.1 MDACMD_SCSI & MDACMD_SCSIPT
+ */
+#define MLY_CMD_SCSI_SMALL_CDB 10
+struct mly_cmd_scsi_small {
+ u_int16_t command_id;
+ u_int8_t opcode;
+ u_int8_t command_control;
+ u_int32_t data_size;
+ u_int64_t sense_buffer_address;
+ u_int8_t addr[3];
+ u_int8_t timeout;
+ u_int8_t maximum_sense_size;
+ u_int8_t cdb_length;
+ u_int8_t cdb[MLY_CMD_SCSI_SMALL_CDB];
+ union mly_cmd_transfer transfer;
+} __attribute__ ((__packed__));
+
+/*
+ * 19.2 MDACMD_SCSILC & MDACMD_SCSILCPT
+ */
+struct mly_cmd_scsi_large {
+ u_int16_t command_id;
+ u_int8_t opcode;
+ u_int8_t command_control;
+ u_int32_t data_size;
+ u_int64_t sense_buffer_address;
+ u_int8_t addr[3];
+ u_int8_t timeout;
+ u_int8_t maximum_sense_size;
+ u_int8_t cdb_length;
+ u_int16_t res1;
+ u_int64_t cdb_physaddr;
+ union mly_cmd_transfer transfer;
+} __attribute__ ((__packed__));
+
+/*
+ * 20.1 IOCTL Command Format: Internal Bus
+ */
+struct mly_cmd_ioctl {
+ u_int16_t command_id;
+ u_int8_t opcode;
+ u_int8_t command_control;
+ u_int32_t data_size;
+ u_int64_t sense_buffer_address;
+ u_int8_t addr[3];
+ u_int8_t timeout;
+ u_int8_t maximum_sense_size;
+ u_int8_t sub_ioctl;
+ union mly_ioctl_param param;
+} __attribute__ ((__packed__));
+
+/*
+ * PG6: 8.2.2
+ */
+struct mly_cmd_mmbox {
+ u_int32_t flag;
+ u_int8_t data[60];
+} __attribute__ ((__packed__));
+
+union mly_cmd_packet {
+ struct mly_cmd_generic generic;
+ struct mly_cmd_scsi_small scsi_small;
+ struct mly_cmd_scsi_large scsi_large;
+ struct mly_cmd_ioctl ioctl;
+ struct mly_cmd_mmbox mmbox;
+};
+
+/*
+ * PG6: 5.3
+ */
+#define MLY_I960RX_COMMAND_MAILBOX 0x10
+#define MLY_I960RX_STATUS_MAILBOX 0x18
+#define MLY_I960RX_IDBR 0x20
+#define MLY_I960RX_ODBR 0x2c
+#define MLY_I960RX_ERROR_STATUS 0x2e
+#define MLY_I960RX_INTERRUPT_STATUS 0x30
+#define MLY_I960RX_INTERRUPT_MASK 0x34
+
+#define MLY_STRONGARM_COMMAND_MAILBOX 0x50
+#define MLY_STRONGARM_STATUS_MAILBOX 0x58
+#define MLY_STRONGARM_IDBR 0x60
+#define MLY_STRONGARM_ODBR 0x61
+#define MLY_STRONGARM_ERROR_STATUS 0x63
+#define MLY_STRONGARM_INTERRUPT_STATUS 0x30
+#define MLY_STRONGARM_INTERRUPT_MASK 0x34
+
+/*
+ * PG6: 5.4.3 Doorbell 0
+ */
+#define MLY_HM_CMDSENT (1<<0)
+#define MLY_HM_STSACK (1<<1)
+#define MLY_SOFT_RST (1<<3)
+#define MLY_AM_CMDSENT (1<<4)
+
+/*
+ * PG6: 5.4.4 Doorbell 1
+ *
+ * Note that the documentation claims that these bits are set when the
+ * status queue(s) are empty, wheras the Linux driver and experience
+ * suggest they are set when there is status available.
+ */
+#define MLY_HM_STSREADY (1<<0)
+#define MLY_AM_STSREADY (1<<1)
+
+/*
+ * PG6: 5.4.6 Doorbell 3
+ */
+#define MLY_MSG_EMPTY (1<<3)
+#define MLY_MSG_SPINUP 0x08
+#define MLY_MSG_RACE_RECOVERY_FAIL 0x60
+#define MLY_MSG_RACE_IN_PROGRESS 0x70
+#define MLY_MSG_RACE_ON_CRITICAL 0xb0
+#define MLY_MSG_PARITY_ERROR 0xf0
+
+/*
+ * PG6: 5.4.8 Outbound Interrupt Mask
+ */
+#define MLY_INTERRUPT_MASK_DISABLE 0xff
+#define MLY_INTERRUPT_MASK_ENABLE (0xff & ~(1<<2))
+
+/*
+ * PG6: 8.2 Advanced Mailbox Scheme
+ *
+ * Note that this must be allocated on a 4k boundary, and all internal
+ * fields must also reside on a 4k boundary.
+ * We could dynamically size this structure, but the extra effort
+ * is probably unjustified. Note that these buffers do not need to be
+ * adjacent - we just group them to simplify allocation of the bus-visible
+ * buffer.
+ *
+ * XXX Note that for some reason, if MLY_MMBOX_COMMANDS is > 64, the controller
+ * fails to respond to the command at (MLY_MMBOX_COMMANDS - 64). It's not
+ * wrapping to 0 at this point (determined by experimentation). This is not
+ * consistent with the Linux driver's implementation.
+ * Whilst it's handy to have lots of room for status returns in case we end up
+ * being slow getting back to completed commands, it seems unlikely that we
+ * would get 64 commands ahead of the controller on the submissions side, so
+ * the current workaround is to simply limit the command ring to 64 entries.
+ */
+union mly_status_packet {
+ struct mly_status status;
+ struct {
+ u_int32_t flag;
+ u_int8_t data[4];
+ } __attribute__ ((__packed__)) mmbox;
+};
+union mly_health_region {
+ struct mly_health_status status;
+ u_int8_t pad[1024];
+};
+
+#define MLY_MMBOX_COMMANDS 64
+#define MLY_MMBOX_STATUS 512
+struct mly_mmbox {
+ union mly_cmd_packet mmm_command[MLY_MMBOX_COMMANDS];
+ union mly_status_packet mmm_status[MLY_MMBOX_STATUS];
+ union mly_health_region mmm_health;
+} __attribute__ ((__packed__));
+
+#endif /* !defined _PCI_MLYREG_H_ */
diff --git a/sys/dev/pci/mlyvar.h b/sys/dev/pci/mlyvar.h
new file mode 100644
index 00000000000..4be8394604f
--- /dev/null
+++ b/sys/dev/pci/mlyvar.h
@@ -0,0 +1,345 @@
+/* $NetBSD: mlyvar.h,v 1.1 2001/07/30 19:59:07 ad Exp $ */
+
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Andrew Doran, Thor Lancelot Simon, and Eric Haszlakiewicz.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2000, 2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from FreeBSD: mlyvar.h,v 1.3 2001/07/14 00:12:22 msmith Exp
+ */
+
+#ifndef _PCI_MLYVAR_H_
+#define _PCI_MLYVAR_H_
+
+/*
+ * The firmware interface allows for a 16-bit command identifier. We cap
+ * ourselves at a reasonable limit. Note that we reserve a small number of
+ * CCBs for control operations.
+ */
+#define MLY_MAX_CCBS 256
+#define MLY_CCBS_RESV 4
+
+/*
+ * The firmware interface allows for a 16-bit s/g list length. We limit
+ * ourselves to a reasonable maximum.
+ */
+#define MLY_MAX_SEGS 17
+#define MLY_SGL_SIZE (MLY_MAX_SEGS * sizeof(struct mly_sg_entry))
+
+#define MLY_MAX_XFER ((MLY_MAX_SEGS - 1) * PAGE_SIZE)
+
+/*
+ * The interval at which we poke the controller for status updates (in
+ * seconds).
+ */
+#define MLY_PERIODIC_INTERVAL 5
+
+/*
+ * Command slot regulation. We can't use slot 0 due to the memory mailbox
+ * implementation.
+ */
+#define MLY_SLOT_START 1
+#define MLY_SLOT_MAX (MLY_SLOT_START + MLY_MAX_CCBS)
+
+/*
+ * Per-device structure, used to save persistent state on devices.
+ *
+ * Note that this isn't really Bus/Target/Lun since we don't support lun !=
+ * 0 at this time.
+ */
+struct mly_btl {
+ int mb_flags;
+ int mb_state; /* See 8.1 */
+ int mb_type; /* See 8.2 */
+
+ /* Physical devices only. */
+ int mb_speed; /* Interface transfer rate */
+ int mb_width; /* Interface width */
+};
+#define MLY_BTL_PHYSICAL 0x01 /* physical device */
+#define MLY_BTL_LOGICAL 0x02 /* logical device */
+#define MLY_BTL_PROTECTED 0x04 /* I/O not allowed */
+#define MLY_BTL_TQING 0x08 /* tagged queueing */
+#define MLY_BTL_SCANNING 0x10 /* scan in progress */
+#define MLY_BTL_RESCAN 0x20 /* need to re-scan */
+
+/*
+ * Per-command context.
+ */
+struct mly_softc;
+
+struct mly_ccb {
+ union {
+ SLIST_ENTRY(mly_ccb) slist;
+ SIMPLEQ_ENTRY(mly_ccb) simpleq;
+ } mc_link; /* list linkage */
+
+ u_int mc_slot; /* command slot we occupy */
+ u_int mc_flags; /* status flags */
+ u_int mc_status; /* command completion status */
+ u_int mc_sense; /* sense data length */
+ int32_t mc_resid; /* I/O residual count */
+
+ union mly_cmd_packet *mc_packet;/* our controller command */
+ bus_addr_t mc_packetphys; /* physical address of the mapped packet */
+
+ void *mc_data; /* data buffer */
+ size_t mc_length; /* data length */
+ bus_dmamap_t mc_datamap; /* DMA map for data */
+ u_int mc_sgoff; /* S/G list offset */
+
+ void (*mc_complete)(struct mly_softc *, struct mly_ccb *);
+ void *mc_private;
+};
+#define MLY_CCB_DATAIN 0x01
+#define MLY_CCB_DATAOUT 0x02
+#define MLY_CCB_MAPPED 0x04
+#define MLY_CCB_COMPLETE 0x08
+
+/*
+ * Per-controller context.
+ */
+struct mly_softc {
+ /* Generic device info. */
+ struct device mly_dv;
+ bus_space_handle_t mly_ioh;
+ bus_space_tag_t mly_iot;
+ bus_dma_tag_t mly_dmat;
+ void *mly_ih;
+
+ /* Scatter-gather lists. */
+ struct mly_sg_entry *mly_sg;
+ bus_addr_t mly_sg_busaddr;
+ bus_dma_tag_t mly_sg_dmat;
+ bus_dmamap_t mly_sg_dmamap;
+ bus_dma_segment_t mly_sg_seg;
+
+ /* Memory mailbox. */
+ struct mly_mmbox *mly_mmbox;
+ bus_addr_t mly_mmbox_busaddr;
+ bus_dma_tag_t mly_mmbox_dmat;
+ bus_dmamap_t mly_mmbox_dmamap;
+ bus_dma_segment_t mly_mmbox_seg;
+ u_int mly_mmbox_cmd_idx;
+ u_int mly_mmbox_sts_idx;
+
+ /* Command packets. */
+ union mly_cmd_packet *mly_pkt;
+ bus_addr_t mly_pkt_busaddr;
+ bus_dma_tag_t mly_pkt_dmat;
+ bus_dmamap_t mly_pkt_dmamap;
+ bus_dma_segment_t mly_pkt_seg;
+
+ /* Command management. */
+ struct mly_ccb *mly_ccbs;
+ SLIST_HEAD(,mly_ccb) mly_ccb_free;
+ SIMPLEQ_HEAD(,mly_ccb) mly_ccb_queue;
+ u_int mly_ncmds;
+
+ /* Controller hardware interface. */
+ u_int mly_hwif;
+ u_int mly_doorbell_true;
+ u_int mly_cmd_mailbox;
+ u_int mly_status_mailbox;
+ u_int mly_idbr;
+ u_int mly_odbr;
+ u_int mly_error_status;
+ u_int mly_interrupt_status;
+ u_int mly_interrupt_mask;
+
+ /* Controller features, limits and status. */
+ u_int mly_state;
+ struct mly_ioctl_getcontrollerinfo *mly_controllerinfo;
+ struct mly_param_controller *mly_controllerparam;
+ struct mly_btl mly_btl[MLY_MAX_CHANNELS][MLY_MAX_TARGETS];
+
+ /* Health monitoring. */
+ u_int mly_event_change;
+ u_int mly_event_counter;
+ u_int mly_event_waiting;
+ struct proc *mly_thread;
+
+ /* SCSI mid-layer connection. */
+ struct scsipi_adapter mly_adapt;
+ struct scsipi_channel mly_chans[MLY_MAX_CHANNELS];
+ u_int mly_nchans;
+};
+#define MLY_HWIF_I960RX 0
+#define MLY_HWIF_STRONGARM 1
+
+#define MLY_STATE_OPEN 0x01
+#define MLY_STATE_MMBOX_ACTIVE 0x02
+#define MLY_STATE_INITOK 0x04
+
+/*
+ * Register access helpers.
+ */
+
+static __inline__ u_int8_t mly_inb(struct mly_softc *, int);
+static __inline__ u_int16_t mly_inw(struct mly_softc *, int);
+static __inline__ u_int32_t mly_inl(struct mly_softc *, int);
+static __inline__ void mly_outb(struct mly_softc *, int, u_int8_t);
+static __inline__ void mly_outw(struct mly_softc *, int, u_int16_t);
+static __inline__ void mly_outl(struct mly_softc *, int, u_int32_t);
+static __inline__ int mly_idbr_true(struct mly_softc *, u_int8_t);
+static __inline__ int mly_odbr_true(struct mly_softc *, u_int8_t);
+static __inline__ int mly_error_valid(struct mly_softc *);
+
+static __inline__ u_int8_t
+mly_inb(struct mly_softc *mly, int off)
+{
+
+ bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 1,
+ BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ);
+ return (bus_space_read_1(mly->mly_iot, mly->mly_ioh, off));
+}
+
+static __inline__ u_int16_t
+mly_inw(struct mly_softc *mly, int off)
+{
+
+ bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 2,
+ BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ);
+ return (bus_space_read_2(mly->mly_iot, mly->mly_ioh, off));
+}
+
+static __inline__ u_int32_t
+mly_inl(struct mly_softc *mly, int off)
+{
+
+ bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 4,
+ BUS_SPACE_BARRIER_WRITE | BUS_SPACE_BARRIER_READ);
+ return (bus_space_read_4(mly->mly_iot, mly->mly_ioh, off));
+}
+
+static __inline__ void
+mly_outb(struct mly_softc *mly, int off, u_int8_t val)
+{
+
+ bus_space_write_1(mly->mly_iot, mly->mly_ioh, off, val);
+ bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 1,
+ BUS_SPACE_BARRIER_WRITE);
+}
+
+static __inline__ void
+mly_outw(struct mly_softc *mly, int off, u_int16_t val)
+{
+
+ bus_space_write_2(mly->mly_iot, mly->mly_ioh, off, val);
+ bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 2,
+ BUS_SPACE_BARRIER_WRITE);
+}
+
+static __inline__ void
+mly_outl(struct mly_softc *mly, int off, u_int32_t val)
+{
+
+ bus_space_write_4(mly->mly_iot, mly->mly_ioh, off, val);
+ bus_space_barrier(mly->mly_iot, mly->mly_ioh, off, 4,
+ BUS_SPACE_BARRIER_WRITE);
+}
+
+static __inline__ int
+mly_idbr_true(struct mly_softc *mly, u_int8_t mask)
+{
+ u_int8_t val;
+
+ val = mly_inb(mly, mly->mly_idbr) ^ mly->mly_doorbell_true;
+ return ((val & mask) == mask);
+}
+
+static __inline__ int
+mly_odbr_true(struct mly_softc *mly, u_int8_t mask)
+{
+
+ return ((mly_inb(mly, mly->mly_odbr) & mask) == mask);
+}
+
+static __inline__ int
+mly_error_valid(struct mly_softc *mly)
+{
+ u_int8_t val;
+
+ val = mly_inb(mly, mly->mly_error_status) ^ mly->mly_doorbell_true;
+ return ((val & MLY_MSG_EMPTY) == 0);
+}
+
+/*
+ * Bus/target/logical ID-related macros.
+ */
+
+#define MLY_LOGDEV_ID(mly, bus, target) \
+ (((bus) - (mly)->mly_controllerinfo->physical_channels_present) * \
+ MLY_MAX_TARGETS + (target))
+
+#define MLY_LOGDEV_BUS(mly, logdev) \
+ (((logdev) / MLY_MAX_TARGETS) + \
+ (mly)->mly_controllerinfo->physical_channels_present)
+
+#define MLY_LOGDEV_TARGET(mly, logdev) \
+ ((logdev) % MLY_MAX_TARGETS)
+
+#define MLY_BUS_IS_VIRTUAL(mly, bus) \
+ ((bus) >= (mly)->mly_controllerinfo->physical_channels_present)
+
+#define MLY_BUS_IS_VALID(mly, bus) \
+ (((bus) < (mly)->mly_nchans))
+
+#endif /* !defined _PCI_MLYVAR_H_ */