summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/Makefile4
-rw-r--r--share/man/man4/aac.456
-rw-r--r--share/man/man4/pci.414
-rw-r--r--sys/arch/i386/conf/GENERIC4
-rw-r--r--sys/dev/ic/aac.c1937
-rw-r--r--sys/dev/ic/aac_tables.h121
-rw-r--r--sys/dev/ic/aacreg.h717
-rw-r--r--sys/dev/ic/aacvar.h341
-rw-r--r--sys/dev/pci/aac_pci.c203
-rw-r--r--sys/dev/pci/files.pci8
10 files changed, 3397 insertions, 8 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 1399052d485..07746dba414 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1,6 +1,6 @@
-# $OpenBSD: Makefile,v 1.133 2000/11/06 01:41:23 deraadt Exp $
+# $OpenBSD: Makefile,v 1.134 2000/11/10 09:39:37 niklas Exp $
-MAN= ac97.4 adv.4 aha.4 ahb.4 ahc.4 aic.4 amphy.4 an.4 aria.4 ast.4 \
+MAN= aac.4 ac97.4 adv.4 aha.4 ahb.4 ahc.4 aic.4 amphy.4 an.4 aria.4 ast.4 \
atalk.4 atapiscsi.4 audio.4 aue.4 auvia.4 awi.4 bpf.4 bridge.4 \
brgphy.4 cardbus.4 ccd.4 cd.4 ch.4 clnp.4 clcs.4 cltp.4 cmpci.4 cnw.4 \
com.4 cue.4 cy.4 dc.4 ddb.4 de.4 dpt.4 drum.4 eap.4 ec.4 ef.4 \
diff --git a/share/man/man4/aac.4 b/share/man/man4/aac.4
new file mode 100644
index 00000000000..08bc26d8211
--- /dev/null
+++ b/share/man/man4/aac.4
@@ -0,0 +1,56 @@
+.\" $OpenBSD: aac.4,v 1.1 2000/11/10 09:39:37 niklas Exp $
+.\"
+.\" Copyright (c) 2000 Michael Shalayeff. All rights reserved.
+.\"
+.\"
+.Dd November 7, 2000
+.Dt AAC 4
+.Os
+.Sh NAME
+.Nm aac
+.Nd Adaptec RAID driver
+.Sh SYNOPSIS
+.Cd "aac* at pci? dev ? function ?"
+.Cd "scsibus* at aac?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for Adaptec's "FSA" family of raid controllers,
+including the
+.Tn AAC2622 ,
+.Tn AAC364 ,
+.Tn AAC3642 ,
+.Tn Dell PERC 2/Si ,
+.Tn Dell PERC 3/Di ,
+.Tn Dell PERC 3/Si ,
+.Tn Dell PERC 2/QC ,
+.Tn Dell PERC 3/QC and
+.Tn HP NetRaid-4M
+models.
+.Pp
+All the RAID set volume management is done via the card BIOS.
+.Pp
+This driver makes drives and/or RAID sets appear as
+.Xr sd 4 .
+.Sh SEE ALSO
+.Xr intro 4 ,
+.Xr scsi 4 ,
+.Xr sd 4
+.Sh AUTHOR
+The
+.Nm
+driver was written by
+.An Niklas Hallqvist Aq niklas@opensbd.org ,
+inspired by the FreeBSD driver by
+.An Mike Smith Aq msmith@freebsd.org .
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 2.9 .
+.Sh BUGS
+.Pp
+The card provides a mechanism to do online RAID set management,
+unfortunately the protocol is not public.
+.Pp
+Only Dell PERC 3/Si was tested, but others should work.
diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4
index 9aab163de4f..21ed078f72a 100644
--- a/share/man/man4/pci.4
+++ b/share/man/man4/pci.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pci.4,v 1.19 2000/11/08 20:27:08 deraadt Exp $
+.\" $OpenBSD: pci.4,v 1.20 2000/11/10 09:39:37 niklas Exp $
.\" $NetBSD: pci.4,v 1.29 2000/04/01 00:32:23 tsarna Exp $
.\"
.\" Copyright (c) 2000 Theo de Raadt. All rights reserved.
@@ -105,16 +105,22 @@ interfaces.
.El
.Ss RAID and cache controllers
.Bl -tag -width speaker -offset ind
+.It Xr aac 4
+Adaptec "FSA" family (Adaptec AAC, Dell PERC, HP NetRaid)
+.Tn RAID
+controllers.
.It Xr dpt 4
-DPT SmartCache/SmartRAID III and IV SCSI interfaces.
+DPT SmartCache/SmartRAID III and IV
+.Tn SCSI
+controllers.
.It Xr gdt 4
ICP-Vortex GDT
.Tn RAID
-interfaces.
+conttrollers.
.It Xr twe 4
3ware Escalade
.Tn RAID
-interfaces.
+controllers.
.El
.Ss Network interfaces
.Bl -tag -width speaker -offset ind
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC
index fbc126ce726..d85e11d84f1 100644
--- a/sys/arch/i386/conf/GENERIC
+++ b/sys/arch/i386/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.212 2000/10/26 20:50:44 aaron Exp $
+# $OpenBSD: GENERIC,v 1.213 2000/11/10 09:39:37 niklas Exp $
# $NetBSD: GENERIC,v 1.48 1996/05/20 18:17:23 mrg Exp $
#
# GENERIC -- everything that's currently supported
@@ -188,6 +188,8 @@ gdt* at pci? dev ? function ? # ICP Vortex GDT RAID controllers
scsibus* at gdt?
twe* at pci? dev ? function ? # 3ware Escalade RAID controllers
scsibus* at twe?
+aac* at pci? dev ? function ? # Adaptec FSA RAID controllers
+scsibus* at aac?
isp* at pci? dev ? function ? # Qlogic ISP [12]0x0 SCSI/FibreChannel
scsibus* at isp?
aic0 at isa? port 0x340 irq 11 # Adaptec 152[02] SCSI controllers
diff --git a/sys/dev/ic/aac.c b/sys/dev/ic/aac.c
new file mode 100644
index 00000000000..87b31467d8d
--- /dev/null
+++ b/sys/dev/ic/aac.c
@@ -0,0 +1,1937 @@
+/* $OpenBSD: aac.c,v 1.1 2000/11/10 09:39:35 niklas Exp $ */
+
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * Copyright (c) 2000 Niklas Hallqvist
+ * 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.
+ *
+ * $FreeBSD: /c/ncvs/src/sys/dev/aac/aac.c,v 1.1 2000/09/13 03:20:34 msmith Exp $
+ */
+
+/*
+ * Driver for the Adaptec 'FSA' family of PCI/SCSI RAID adapters.
+ */
+
+/*
+ * This driver would not have rewritten for OpenBSD if it was not for the
+ * hardware donation from Nocom. I want to thank them for their support.
+ * Of course, credit should go to Mike Smith for the original work he did
+ * in the FreeBSD driver where I found lots of reusable code and inspiration.
+ * - Niklas Hallqvist
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+#include <dev/ic/aacreg.h>
+#include <dev/ic/aacvar.h>
+#include <dev/ic/aac_tables.h>
+
+/* XXX from stddef.h */
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+
+/* Geometry constants. */
+#define AAC_MAXCYLS 1024
+#define AAC_HEADS 64
+#define AAC_SECS 32 /* mapping 64*32 */
+#define AAC_MEDHEADS 127
+#define AAC_MEDSECS 63 /* mapping 127*63 */
+#define AAC_BIGHEADS 255
+#define AAC_BIGSECS 63 /* mapping 255*63 */
+#define AAC_SECS32 0x1f /* round capacity */
+
+void aac_bio_complete __P((struct aac_ccb *));
+void aac_complete __P((void *, int));
+void aac_copy_internal_data __P((struct scsi_xfer *, u_int8_t *, size_t));
+struct scsi_xfer *aac_dequeue __P((struct aac_softc *));
+int aac_dequeue_fib __P((struct aac_softc *, int, u_int32_t *,
+ struct aac_fib **));
+char *aac_describe_code __P((struct aac_code_lookup *, u_int32_t));
+void aac_describe_controller __P((struct aac_softc *));
+void aac_enqueue __P((struct aac_softc *, struct scsi_xfer *, int));
+void aac_enqueue_ccb __P((struct aac_softc *, struct aac_ccb *));
+int aac_enqueue_fib __P((struct aac_softc *, int, u_int32_t, u_int32_t));
+void aac_eval_mapping __P((u_int32_t, int *, int *, int *));
+int aac_exec_ccb __P((struct aac_ccb *));
+void aac_free_ccb __P((struct aac_softc *, struct aac_ccb *));
+struct aac_ccb *aac_get_ccb __P((struct aac_softc *, int));
+#if 0
+void aac_handle_aif __P((struct aac_softc *, struct aac_aif_command *));
+#endif
+void aac_host_command __P((struct aac_softc *));
+void aac_host_response __P((struct aac_softc *));
+int aac_init __P((struct aac_softc *));
+int aac_internal_cache_cmd __P((struct scsi_xfer *));
+int aac_map_command __P((struct aac_ccb *));
+#ifdef AAC_DEBUG
+void aac_print_fib __P((struct aac_softc *, struct aac_fib *, char *));
+#endif
+int aac_raw_scsi_cmd __P((struct scsi_xfer *));
+int aac_scsi_cmd __P((struct scsi_xfer *));
+int aac_start __P((struct aac_ccb *));
+void aac_start_ccbs __P((struct aac_softc *));
+void aac_startup __P((struct aac_softc *));
+int aac_sync_command __P((struct aac_softc *, u_int32_t, u_int32_t,
+ u_int32_t, u_int32_t, u_int32_t, u_int32_t *));
+int aac_sync_fib __P((struct aac_softc *, u_int32_t, u_int32_t, void *,
+ u_int16_t, void *, u_int16_t *));
+void aac_timeout __P((void *));
+void aac_unmap_command __P((struct aac_ccb *));
+void aac_watchdog __P((void *));
+
+struct cfdriver aac_cd = {
+ NULL, "aac", DV_DULL
+};
+
+struct scsi_adapter aac_switch = {
+ aac_scsi_cmd, aacminphys, 0, 0,
+};
+
+struct scsi_adapter aac_raw_switch = {
+ aac_raw_scsi_cmd, aacminphys, 0, 0,
+};
+
+struct scsi_device aac_dev = {
+ NULL, NULL, NULL, NULL
+};
+
+/* i960Rx interface */
+int aac_rx_get_fwstatus __P((struct aac_softc *));
+void aac_rx_qnotify __P((struct aac_softc *, int));
+int aac_rx_get_istatus __P((struct aac_softc *));
+void aac_rx_clear_istatus __P((struct aac_softc *, int));
+void aac_rx_set_mailbox __P((struct aac_softc *, u_int32_t, u_int32_t,
+ u_int32_t, u_int32_t, u_int32_t));
+int aac_rx_get_mailboxstatus __P((struct aac_softc *));
+void aac_rx_set_interrupts __P((struct aac_softc *, int));
+
+/* StrongARM interface */
+int aac_sa_get_fwstatus __P((struct aac_softc *));
+void aac_sa_qnotify __P((struct aac_softc *, int));
+int aac_sa_get_istatus __P((struct aac_softc *));
+void aac_sa_clear_istatus __P((struct aac_softc *, int));
+void aac_sa_set_mailbox __P((struct aac_softc *, u_int32_t, u_int32_t,
+ u_int32_t, u_int32_t, u_int32_t));
+int aac_sa_get_mailboxstatus __P((struct aac_softc *));
+void aac_sa_set_interrupts __P((struct aac_softc *, int));
+
+struct aac_interface aac_rx_interface = {
+ aac_rx_get_fwstatus,
+ aac_rx_qnotify,
+ aac_rx_get_istatus,
+ aac_rx_clear_istatus,
+ aac_rx_set_mailbox,
+ aac_rx_get_mailboxstatus,
+ aac_rx_set_interrupts
+};
+
+struct aac_interface aac_sa_interface = {
+ aac_sa_get_fwstatus,
+ aac_sa_qnotify,
+ aac_sa_get_istatus,
+ aac_sa_clear_istatus,
+ aac_sa_set_mailbox,
+ aac_sa_get_mailboxstatus,
+ aac_sa_set_interrupts
+};
+
+#ifdef AAC_DEBUG
+int aac_debug = AAC_DEBUG;
+#endif
+
+int
+aac_attach(sc)
+ struct aac_softc *sc;
+{
+ int i, error;
+ bus_dma_segment_t seg;
+ int nsegs;
+ struct aac_ccb *ccb;
+
+ TAILQ_INIT(&sc->sc_free_ccb);
+ TAILQ_INIT(&sc->sc_ccbq);
+ TAILQ_INIT(&sc->sc_completed);
+ LIST_INIT(&sc->sc_queue);
+
+ /* disable interrupts before we enable anything */
+ AAC_MASK_INTERRUPTS(sc);
+
+ /* mark controller as suspended until we get ourselves organised */
+ sc->sc_state |= AAC_STATE_SUSPEND;
+
+ /*
+ * Initialise the adapter.
+ */
+ error = aac_init(sc);
+ if (error)
+ return (error);
+
+ /*
+ * Print a little information about the controller.
+ */
+ aac_describe_controller(sc);
+
+ /* Initialize the ccbs */
+ for (i = 0; i < AAC_ADAP_NORM_CMD_ENTRIES; i++) {
+ ccb = &sc->sc_ccbs[i];
+ error = bus_dmamap_create(sc->sc_dmat,
+ (AAC_MAXSGENTRIES - 1) << PGSHIFT, AAC_MAXSGENTRIES,
+ (AAC_MAXSGENTRIES - 1) << PGSHIFT, 0,
+ BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ccb->ac_dmamap_xfer);
+ if (error) {
+ printf("%s: cannot create ccb dmamap (%d)",
+ sc->sc_dev.dv_xname, error);
+ /* XXX cleanup */
+ return (1);
+ }
+
+ /* allocate the FIB cluster in DMAable memory and load it */
+ if (bus_dmamem_alloc(sc->sc_dmat, sizeof *ccb->ac_fib, 1, 0,
+ &seg, 1, &nsegs, BUS_DMA_NOWAIT)) {
+ printf("%s: can't allocate FIB structure\n",
+ sc->sc_dev.dv_xname);
+ /* XXX cleanup */
+ return (1);
+ }
+ ccb->ac_fibphys = seg.ds_addr;
+ if (bus_dmamem_map(sc->sc_dmat, &seg, nsegs,
+ sizeof *ccb->ac_fib, (caddr_t *)&ccb->ac_fib, 0)) {
+ printf("%s: can't map FIB structure\n",
+ sc->sc_dev.dv_xname);
+ /* XXX cleanup */
+ return (1);
+ }
+
+ TAILQ_INSERT_TAIL(&sc->sc_free_ccb, &sc->sc_ccbs[i],
+ ac_chain);
+ }
+
+ /* Fill in the prototype scsi_link. */
+ sc->sc_link.adapter_softc = sc;
+ sc->sc_link.adapter = &aac_switch;
+ sc->sc_link.device = &aac_dev;
+ sc->sc_link.openings = AAC_ADAP_NORM_CMD_ENTRIES; /* XXX optimal? */
+ sc->sc_link.adapter_buswidth = AAC_MAX_CONTAINERS;
+ sc->sc_link.adapter_target = AAC_MAX_CONTAINERS;
+
+ config_found(&sc->sc_dev, &sc->sc_link, scsiprint);
+
+ return (0);
+}
+
+/*
+ * Look up a text description of a numeric error code and return a pointer to
+ * same.
+ */
+char *
+aac_describe_code(table, code)
+ struct aac_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);
+}
+
+void
+aac_describe_controller(sc)
+ struct aac_softc *sc;
+{
+ u_int8_t buf[AAC_FIB_DATASIZE]; /* XXX a bit big for the stack */
+ u_int16_t bufsize;
+ struct aac_adapter_info *info;
+ u_int8_t arg;
+
+ arg = 0;
+ if (aac_sync_fib(sc, RequestAdapterInfo, 0, &arg, sizeof arg, &buf,
+ &bufsize)) {
+ printf("%s: RequestAdapterInfo failed\n", sc->sc_dev.dv_xname);
+ return;
+ }
+ if (bufsize != sizeof *info) {
+ printf("%s: "
+ "RequestAdapterInfo returned wrong data size (%d != %d)\n",
+ sc->sc_dev.dv_xname, bufsize, sizeof *info);
+ return;
+ }
+ info = (struct aac_adapter_info *)&buf[0];
+
+ printf("%s: %s %dMHz, %dMB, %s (%d) Kernel %d.%d-%d\n",
+ sc->sc_dev.dv_xname,
+ aac_describe_code(aac_cpu_variant, info->CpuVariant),
+ info->ClockSpeed, info->TotalMem / (1024 * 1024),
+ aac_describe_code(aac_battery_platform, info->batteryPlatform),
+ info->batteryPlatform, info->KernelRevision.external.comp.major,
+ info->KernelRevision.external.comp.minor,
+ info->KernelRevision.external.comp.dash);
+
+ /* save the kernel revision structure for later use */
+ sc->sc_revision = info->KernelRevision;
+}
+
+int
+aac_init(sc)
+ struct aac_softc *sc;
+{
+ bus_dma_segment_t seg;
+ int nsegs;
+ int i, error;
+ int state = 0;
+ struct aac_adapter_init *ip;
+ u_int32_t code;
+ u_int8_t *qaddr;
+
+ /*
+ * First wait for the adapter to come ready.
+ */
+ for (i = 0; i < AAC_BOOT_TIMEOUT * 1000; i++) {
+ code = AAC_GET_FWSTATUS(sc);
+ if (code & AAC_SELF_TEST_FAILED) {
+ printf("%s: FATAL: selftest failed\n",
+ sc->sc_dev.dv_xname);
+ return (ENXIO);
+ }
+ if (code & AAC_KERNEL_PANIC) {
+ printf("%s: FATAL: controller kernel panic\n",
+ sc->sc_dev.dv_xname);
+ return (ENXIO);
+ }
+ if (code & AAC_UP_AND_RUNNING)
+ break;
+ DELAY(1000);
+ }
+ if (i == AAC_BOOT_TIMEOUT * 1000) {
+ printf("%s: FATAL: controller not coming ready, status %x\n",
+ sc->sc_dev.dv_xname, code);
+ return (ENXIO);
+ }
+
+ if (bus_dmamem_alloc(sc->sc_dmat, sizeof *sc->sc_common, 1, 0, &seg, 1,
+ &nsegs, BUS_DMA_NOWAIT)) {
+ printf("%s: can't allocate common structure\n",
+ sc->sc_dev.dv_xname);
+ return (ENOMEM);
+ }
+ state++;
+ sc->sc_common_busaddr = seg.ds_addr;
+ if (bus_dmamem_map(sc->sc_dmat, &seg, nsegs, sizeof *sc->sc_common,
+ (caddr_t *)&sc->sc_common, 0)) {
+ printf("%s: can't map common structure\n",
+ sc->sc_dev.dv_xname);
+ error = ENOMEM;
+ goto bail_out;
+ }
+ state++;
+ bzero(sc->sc_common, sizeof *sc->sc_common);
+
+ /*
+ * Fill in the init structure. This tells the adapter about
+ * the physical location * of various important shared data
+ * structures.
+ */
+ ip = &sc->sc_common->ac_init;
+ ip->InitStructRevision = AAC_INIT_STRUCT_REVISION;
+
+ ip->AdapterFibsPhysicalAddress =
+ sc->sc_common_busaddr + offsetof(struct aac_common, ac_fibs);
+ ip->AdapterFibsVirtualAddress = &sc->sc_common->ac_fibs[0];
+ ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib);
+ ip->AdapterFibAlign = sizeof(struct aac_fib);
+
+ ip->PrintfBufferAddress =
+ sc->sc_common_busaddr + offsetof(struct aac_common, ac_printf);
+ ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE;
+
+ ip->HostPhysMemPages = 0; /* not used? */
+ ip->HostElapsedSeconds = 0; /* reset later if invalid */
+
+ /*
+ * Initialise FIB queues. Note that it appears that the
+ * layout of the indexes and the segmentation of the entries
+ * is mandated by the adapter, which is only told about the
+ * base of the queue index fields.
+ *
+ * The initial values of the indices are assumed to inform the
+ * adapter of the sizes of the respective queues.
+ *
+ * The Linux driver uses a much more complex scheme whereby
+ * several header * records are kept for each queue. We use a
+ * couple of generic list manipulation functions which
+ * 'know' the size of each list by virtue of a table.
+ */
+ qaddr = &sc->sc_common->ac_qbuf[0] + AAC_QUEUE_ALIGN;
+ qaddr -= (u_int32_t)qaddr % AAC_QUEUE_ALIGN; /* XXX not portable */
+ sc->sc_queues = (struct aac_queue_table *)qaddr;
+ ip->CommHeaderAddress = sc->sc_common_busaddr +
+ ((char *)sc->sc_queues - (char *)sc->sc_common);
+ bzero(sc->sc_queues, sizeof(struct aac_queue_table));
+
+ sc->sc_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
+ AAC_HOST_NORM_CMD_ENTRIES;
+ sc->sc_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
+ AAC_HOST_NORM_CMD_ENTRIES;
+ sc->sc_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
+ AAC_HOST_HIGH_CMD_ENTRIES;
+ sc->sc_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
+ AAC_HOST_HIGH_CMD_ENTRIES;
+ sc->sc_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] =
+ AAC_ADAP_NORM_CMD_ENTRIES;
+ sc->sc_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] =
+ AAC_ADAP_NORM_CMD_ENTRIES;
+ sc->sc_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] =
+ AAC_ADAP_HIGH_CMD_ENTRIES;
+ sc->sc_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] =
+ AAC_ADAP_HIGH_CMD_ENTRIES;
+ sc->sc_queues->
+ qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX] =
+ AAC_HOST_NORM_RESP_ENTRIES;
+ sc->sc_queues->
+ qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX] =
+ AAC_HOST_NORM_RESP_ENTRIES;
+ sc->sc_queues->
+ qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX] =
+ AAC_HOST_HIGH_RESP_ENTRIES;
+ sc->sc_queues->
+ qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX] =
+ AAC_HOST_HIGH_RESP_ENTRIES;
+ sc->sc_queues->
+ qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX] =
+ AAC_ADAP_NORM_RESP_ENTRIES;
+ sc->sc_queues->
+ qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX] =
+ AAC_ADAP_NORM_RESP_ENTRIES;
+ sc->sc_queues->
+ qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX] =
+ AAC_ADAP_HIGH_RESP_ENTRIES;
+ sc->sc_queues->
+ qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX] =
+ AAC_ADAP_HIGH_RESP_ENTRIES;
+ sc->sc_qentries[AAC_HOST_NORM_CMD_QUEUE] =
+ &sc->sc_queues->qt_HostNormCmdQueue[0];
+ sc->sc_qentries[AAC_HOST_HIGH_CMD_QUEUE] =
+ &sc->sc_queues->qt_HostHighCmdQueue[0];
+ sc->sc_qentries[AAC_ADAP_NORM_CMD_QUEUE] =
+ &sc->sc_queues->qt_AdapNormCmdQueue[0];
+ sc->sc_qentries[AAC_ADAP_HIGH_CMD_QUEUE] =
+ &sc->sc_queues->qt_AdapHighCmdQueue[0];
+ sc->sc_qentries[AAC_HOST_NORM_RESP_QUEUE] =
+ &sc->sc_queues->qt_HostNormRespQueue[0];
+ sc->sc_qentries[AAC_HOST_HIGH_RESP_QUEUE] =
+ &sc->sc_queues->qt_HostHighRespQueue[0];
+ sc->sc_qentries[AAC_ADAP_NORM_RESP_QUEUE] =
+ &sc->sc_queues->qt_AdapNormRespQueue[0];
+ sc->sc_qentries[AAC_ADAP_HIGH_RESP_QUEUE] =
+ &sc->sc_queues->qt_AdapHighRespQueue[0];
+
+ /*
+ * Do controller-type-specific initialisation
+ */
+ switch (sc->sc_hwif) {
+ case AAC_HWIF_I960RX:
+ AAC_SETREG4(sc, AAC_RX_ODBR, ~0);
+ break;
+ }
+
+ /*
+ * Give the init structure to the controller.
+ */
+ if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
+ sc->sc_common_busaddr + offsetof(struct aac_common, ac_init), 0, 0,
+ 0, NULL)) {
+ printf("%s: error establishing init structure\n",
+ sc->sc_dev.dv_xname);
+ error = EIO;
+ goto bail_out;
+ }
+
+ aac_startup(sc);
+
+ return (0);
+
+ bail_out:
+ if (state > 1)
+ bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_common,
+ sizeof *sc->sc_common);
+ if (state > 0)
+ bus_dmamem_free(sc->sc_dmat, &seg, 1);
+ return (error);
+}
+
+/*
+ * Probe for containers, create disks.
+ */
+void
+aac_startup (sc)
+ struct aac_softc *sc;
+{
+ struct aac_mntinfo mi;
+ struct aac_mntinforesponse mir;
+ u_int16_t rsize;
+ int i, drv_cyls, drv_hds, drv_secs;
+
+ /* loop over possible containers */
+ mi.Command = VM_NameServe;
+ mi.MntType = FT_FILESYS;
+ for (i = 0; i < AAC_MAX_CONTAINERS; i++) {
+ /* request information on this container */
+ mi.MntCount = i;
+ if (aac_sync_fib(sc, ContainerCommand, 0, &mi, sizeof mi, &mir,
+ &rsize)) {
+ printf("%s: error probing container %d",
+ sc->sc_dev.dv_xname, i);
+ continue;
+ }
+ /* check response size */
+ if (rsize != sizeof mir) {
+ printf("%s: container info response wrong size "
+ "(%d should be %d)",
+ sc->sc_dev.dv_xname, rsize, sizeof mir);
+ continue;
+ }
+
+ /*
+ * Check container volume type for validity. Note
+ * that many of the possible types * may never show
+ * up.
+ */
+ if (mir.Status == ST_OK &&
+ mir.MntTable[0].VolType != CT_NONE) {
+ AAC_DPRINTF(AAC_D_MISC,
+ ("%d: id %x name '%.16s' size %u type %d", i,
+ mir.MntTable[0].ObjectId,
+ mir.MntTable[0].FileSystemName,
+ mir.MntTable[0].Capacity,
+ mir.MntTable[0].VolType));
+
+ sc->sc_hdr[i].hd_present = 1;
+ sc->sc_hdr[i].hd_size = mir.MntTable[0].Capacity;
+
+ /*
+ * Evaluate mapping (sectors per head, heads per cyl)
+ */
+ sc->sc_hdr[i].hd_size &= ~AAC_SECS32;
+ aac_eval_mapping(sc->sc_hdr[i].hd_size, &drv_cyls,
+ &drv_hds, &drv_secs);
+ sc->sc_hdr[i].hd_heads = drv_hds;
+ sc->sc_hdr[i].hd_secs = drv_secs;
+ /* Round the size */
+ sc->sc_hdr[i].hd_size = drv_cyls * drv_hds * drv_secs;
+
+ sc->sc_hdr[i].hd_devtype = mir.MntTable[0].VolType;
+
+ /* XXX Save the name too for use in IDENTIFY later */
+ }
+ }
+
+ /* mark the controller up */
+ sc->sc_state &= ~AAC_STATE_SUSPEND;
+
+ /* enable interrupts now */
+ AAC_UNMASK_INTERRUPTS(sc);
+}
+
+void
+aac_eval_mapping(size, cyls, heads, secs)
+ u_int32_t size;
+ int *cyls, *heads, *secs;
+{
+ *cyls = size / AAC_HEADS / AAC_SECS;
+ if (*cyls < AAC_MAXCYLS) {
+ *heads = AAC_HEADS;
+ *secs = AAC_SECS;
+ } else {
+ /* Too high for 64 * 32 */
+ *cyls = size / AAC_MEDHEADS / AAC_MEDSECS;
+ if (*cyls < AAC_MAXCYLS) {
+ *heads = AAC_MEDHEADS;
+ *secs = AAC_MEDSECS;
+ } else {
+ /* Too high for 127 * 63 */
+ *cyls = size / AAC_BIGHEADS / AAC_BIGSECS;
+ *heads = AAC_BIGHEADS;
+ *secs = AAC_BIGSECS;
+ }
+ }
+}
+
+int
+aac_raw_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ AAC_DPRINTF(AAC_D_CMD, ("aac_raw_scsi_cmd "));
+
+ /* XXX Not yet implemented */
+ xs->error = XS_DRIVER_STUFFUP;
+ return (COMPLETE);
+}
+
+int
+aac_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *link = xs->sc_link;
+ struct aac_softc *sc = link->adapter_softc;
+ u_int8_t target = link->target;
+ struct aac_ccb *ccb;
+ u_int32_t blockno, blockcnt;
+ struct scsi_rw *rw;
+ struct scsi_rw_big *rwb;
+ aac_lock_t lock;
+ int retval = SUCCESSFULLY_QUEUED;
+
+ AAC_DPRINTF(AAC_D_CMD, ("aac_scsi_cmd "));
+
+ xs->error = XS_NOERROR;
+
+ if (target >= AAC_MAX_CONTAINERS || !sc->sc_hdr[target].hd_present ||
+ link->lun != 0) {
+ /*
+ * XXX Should be XS_SENSE but that would require setting up a
+ * faked sense too.
+ */
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ return (COMPLETE);
+ }
+
+ lock = AAC_LOCK(sc);
+
+ /* Don't double enqueue if we came from gdt_chain. */
+ if (xs != LIST_FIRST(&sc->sc_queue))
+ aac_enqueue(sc, xs, 0);
+
+ while ((xs = aac_dequeue(sc))) {
+ xs->error = XS_NOERROR;
+ ccb = NULL;
+
+ switch (xs->cmd->opcode) {
+ case TEST_UNIT_READY:
+ case REQUEST_SENSE:
+ case INQUIRY:
+ case MODE_SENSE:
+ case START_STOP:
+ case READ_CAPACITY:
+#if 0
+ case VERIFY:
+#endif
+ if (!aac_internal_cache_cmd(xs)) {
+ AAC_UNLOCK(sc, lock);
+ return (TRY_AGAIN_LATER);
+ }
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+
+ case PREVENT_ALLOW:
+ AAC_DPRINTF(AAC_D_CMD, ("PREVENT/ALLOW "));
+ /* XXX Not yet implemented */
+ xs->error = XS_NOERROR;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+
+ case SYNCHRONIZE_CACHE:
+ AAC_DPRINTF(AAC_D_CMD, ("SYNCHRONIZE_CACHE "));
+ /* XXX Not yet implemented */
+ xs->error = XS_NOERROR;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+
+ default:
+ AAC_DPRINTF(AAC_D_CMD,
+ ("unknown opc %d ", xs->cmd->opcode));
+ /* XXX Not yet implemented */
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+
+ case READ_COMMAND:
+ case READ_BIG:
+ case WRITE_COMMAND:
+ case WRITE_BIG:
+ AAC_DPRINTF(AAC_D_CMD,
+ ("rw opc %d ", xs->cmd->opcode));
+
+ if (xs->cmd->opcode != SYNCHRONIZE_CACHE) {
+ /* A read or write operation. */
+ if (xs->cmdlen == 6) {
+ rw = (struct scsi_rw *)xs->cmd;
+ blockno = _3btol(rw->addr) &
+ (SRW_TOPADDR << 16 | 0xffff);
+ blockcnt =
+ rw->length ? rw->length : 0x100;
+ } else {
+ rwb = (struct scsi_rw_big *)xs->cmd;
+ blockno = _4btol(rwb->addr);
+ blockcnt = _2btol(rwb->length);
+ }
+ if (blockno >= sc->sc_hdr[target].hd_size ||
+ blockno + blockcnt >
+ sc->sc_hdr[target].hd_size) {
+ printf(
+ "%s: out of bounds %u-%u >= %u\n",
+ sc->sc_dev.dv_xname, blockno,
+ blockcnt,
+ sc->sc_hdr[target].hd_size);
+ /*
+ * XXX Should be XS_SENSE but that
+ * would require setting up a faked
+ * sense too.
+ */
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+ }
+ }
+
+ ccb = aac_get_ccb(sc, xs->flags);
+
+ /*
+ * Are we out of commands, something is wrong.
+ *
+ */
+ if (ccb == NULL) {
+ printf("%s: no ccb in aac_scsi_cmd",
+ sc->sc_dev.dv_xname);
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+ }
+
+ ccb->ac_blockno = blockno;
+ ccb->ac_blockcnt = blockcnt;
+ ccb->ac_xs = xs;
+ ccb->ac_timeout = xs->timeout;
+
+ if (xs->cmd->opcode != SYNCHRONIZE_CACHE &&
+ aac_map_command(ccb)) {
+ aac_free_ccb(sc, ccb);
+ xs->error = XS_DRIVER_STUFFUP;
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+ goto ready;
+ }
+
+ aac_enqueue_ccb(sc, ccb);
+ /* XXX what if enqueue did not start a transfer? */
+ if (xs->flags & SCSI_POLL) {
+#if 0
+ if (!aac_wait(sc, ccb, ccb->ac_timeout)) {
+ AAC_UNLOCK(sc, lock);
+ printf("%s: command timed out\n",
+ sc->sc_dev.dv_xname);
+ xs->error = XS_TIMEOUT;
+ return (TRY_AGAIN_LATER);
+ }
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
+#endif
+ }
+ }
+
+ ready:
+ /*
+ * Don't process the queue if we are polling.
+ */
+ if (xs->flags & SCSI_POLL) {
+ retval = COMPLETE;
+ break;
+ }
+ }
+
+ AAC_UNLOCK(sc, lock);
+ return (retval);
+}
+
+void
+aac_copy_internal_data(xs, data, size)
+ struct scsi_xfer *xs;
+ u_int8_t *data;
+ size_t size;
+{
+ size_t copy_cnt;
+
+ AAC_DPRINTF(AAC_D_MISC, ("aac_copy_internal_data "));
+
+ if (!xs->datalen)
+ printf("uio move not yet supported\n");
+ else {
+ copy_cnt = MIN(size, xs->datalen);
+ bcopy(data, xs->data, copy_cnt);
+ }
+}
+
+/* Emulated SCSI operation on cache device */
+int
+aac_internal_cache_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_link *link = xs->sc_link;
+ struct aac_softc *sc = link->adapter_softc;
+ struct scsi_inquiry_data inq;
+ struct scsi_sense_data sd;
+ struct {
+ struct scsi_mode_header hd;
+ struct scsi_blk_desc bd;
+ union scsi_disk_pages dp;
+ } mpd;
+ struct scsi_read_cap_data rcd;
+ u_int8_t target = link->target;
+
+ AAC_DPRINTF(AAC_D_CMD, ("aac_internal_cache_cmd "));
+
+ switch (xs->cmd->opcode) {
+ case TEST_UNIT_READY:
+ case START_STOP:
+#if 0
+ case VERIFY:
+#endif
+ AAC_DPRINTF(AAC_D_CMD, ("opc %d tgt %d ", xs->cmd->opcode,
+ target));
+ break;
+
+ case REQUEST_SENSE:
+ AAC_DPRINTF(AAC_D_CMD, ("REQUEST SENSE tgt %d ", target));
+ bzero(&sd, sizeof sd);
+ sd.error_code = 0x70;
+ sd.segment = 0;
+ sd.flags = SKEY_NO_SENSE;
+ aac_enc32(sd.info, 0);
+ sd.extra_len = 0;
+ aac_copy_internal_data(xs, (u_int8_t *)&sd, sizeof sd);
+ break;
+
+ case INQUIRY:
+ AAC_DPRINTF(AAC_D_CMD, ("INQUIRY tgt %d devtype %x ", target,
+ sc->sc_hdr[target].hd_devtype));
+ bzero(&inq, sizeof inq);
+ /* XXX How do we detect removable/CD-ROM devices? */
+ inq.device = T_DIRECT;
+ inq.dev_qual2 = 0;
+ inq.version = 2;
+ inq.response_format = 2;
+ inq.additional_length = 32;
+ strcpy(inq.vendor, "Adaptec");
+ sprintf(inq.product, "Container #%02d", target);
+ strcpy(inq.revision, " ");
+ aac_copy_internal_data(xs, (u_int8_t *)&inq, sizeof inq);
+ break;
+
+ case MODE_SENSE:
+ AAC_DPRINTF(AAC_D_CMD, ("MODE SENSE tgt %d ", target));
+
+ bzero(&mpd, sizeof mpd);
+ switch (((struct scsi_mode_sense *)xs->cmd)->page) {
+ case 4:
+ /* scsi_disk.h says this should be 0x16 */
+ mpd.dp.rigid_geometry.pg_length = 0x16;
+ mpd.hd.data_length = sizeof mpd.hd + sizeof mpd.bd +
+ mpd.dp.rigid_geometry.pg_length;
+ mpd.hd.blk_desc_len = sizeof mpd.bd;
+
+ /* XXX */
+ mpd.hd.dev_spec = 0;
+ _lto3b(AAC_BLOCK_SIZE, mpd.bd.blklen);
+ mpd.dp.rigid_geometry.pg_code = 4;
+ _lto3b(sc->sc_hdr[target].hd_size /
+ sc->sc_hdr[target].hd_heads /
+ sc->sc_hdr[target].hd_secs,
+ mpd.dp.rigid_geometry.ncyl);
+ mpd.dp.rigid_geometry.nheads =
+ sc->sc_hdr[target].hd_heads;
+ aac_copy_internal_data(xs, (u_int8_t *)&mpd,
+ sizeof mpd);
+ break;
+
+ default:
+ printf("%s: mode sense page %d not simulated\n",
+ sc->sc_dev.dv_xname,
+ ((struct scsi_mode_sense *)xs->cmd)->page);
+ xs->error = XS_DRIVER_STUFFUP;
+ return (0);
+ }
+ break;
+
+ case READ_CAPACITY:
+ AAC_DPRINTF(AAC_D_CMD, ("READ CAPACITY tgt %d ", target));
+ bzero(&rcd, sizeof rcd);
+ _lto4b(sc->sc_hdr[target].hd_size - 1, rcd.addr);
+ _lto4b(AAC_BLOCK_SIZE, rcd.length);
+ aac_copy_internal_data(xs, (u_int8_t *)&rcd, sizeof rcd);
+ break;
+
+ default:
+ printf("aac_internal_cache_cmd got bad opcode: %d\n",
+ xs->cmd->opcode);
+ xs->error = XS_DRIVER_STUFFUP;
+ return (0);
+ }
+
+ xs->error = XS_NOERROR;
+ return (1);
+}
+
+/*
+ * Take an interrupt.
+ */
+int
+aac_intr(arg)
+ void *arg;
+{
+ struct aac_softc *sc = arg;
+ u_int16_t reason;
+ int claimed = 0;
+
+ AAC_DPRINTF(AAC_D_INTR, ("aac_intr(%p) ", sc));
+
+ reason = AAC_GET_ISTATUS(sc);
+ AAC_DPRINTF(AAC_D_INTR, ("istatus 0x%04x ", reason));
+
+ /* controller wants to talk to the log? XXX should we defer this? */
+ if (reason & AAC_DB_PRINTF) {
+ if (sc->sc_common->ac_printf[0]) {
+ printf("%s: ** %.*s", sc->sc_dev.dv_xname,
+ AAC_PRINTF_BUFSIZE, sc->sc_common->ac_printf);
+ sc->sc_common->ac_printf[0] = 0;
+ }
+ AAC_CLEAR_ISTATUS(sc, AAC_DB_PRINTF);
+ AAC_QNOTIFY(sc, AAC_DB_PRINTF);
+ claimed = 1;
+ }
+
+ /* Controller has a message for us? */
+ if (reason & AAC_DB_COMMAND_READY) {
+ aac_host_command(sc);
+ AAC_CLEAR_ISTATUS(sc, AAC_DB_COMMAND_READY);
+ claimed = 1;
+ }
+
+ /* Controller has a response for us? */
+ if (reason & AAC_DB_RESPONSE_READY) {
+ aac_host_response(sc);
+ AAC_CLEAR_ISTATUS(sc, AAC_DB_RESPONSE_READY);
+ claimed = 1;
+ }
+
+ /*
+ * Spurious interrupts that we don't use - reset the mask and clear
+ * the interrupts.
+ */
+ if (reason & (AAC_DB_SYNC_COMMAND | AAC_DB_COMMAND_NOT_FULL |
+ AAC_DB_RESPONSE_NOT_FULL)) {
+ AAC_UNMASK_INTERRUPTS(sc);
+ AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND |
+ AAC_DB_COMMAND_NOT_FULL | AAC_DB_RESPONSE_NOT_FULL);
+ claimed = 1;
+ }
+
+ return (claimed);
+}
+
+/*
+ * Handle notification of one or more FIBs coming from the controller.
+ */
+void
+aac_host_command(struct aac_softc *sc)
+{
+ struct aac_fib *fib;
+ u_int32_t fib_size;
+
+ for (;;) {
+ if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE, &fib_size,
+ &fib))
+ break; /* nothing to do */
+
+ switch(fib->Header.Command) {
+ case AifRequest:
+#if 0
+ aac_handle_aif(sc,
+ (struct aac_aif_command *)&fib->data[0]);
+#endif
+
+ break;
+ default:
+ printf("%s: unknown command from controller\n",
+ sc->sc_dev.dv_xname);
+ AAC_PRINT_FIB(sc, fib);
+ break;
+ }
+
+ /* XXX reply to FIBs requesting responses ?? */
+ /* XXX how do we return these FIBs to the controller? */
+ }
+}
+
+/*
+ * Handle notification of one or more FIBs completed by the controller
+ */
+void
+aac_host_response(struct aac_softc *sc)
+{
+ struct aac_ccb *ccb;
+ struct aac_fib *fib;
+ u_int32_t fib_size;
+
+ for (;;) {
+ /* look for completed FIBs on our queue */
+ if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size,
+ &fib))
+ break; /* nothing to do */
+
+ /* get the command, unmap and queue for later processing */
+ ccb = (struct aac_ccb *)fib->Header.SenderData;
+ if (ccb == NULL) {
+ AAC_PRINT_FIB(sc, fib);
+ } else {
+ untimeout(aac_timeout, ccb);
+ aac_unmap_command(ccb); /* XXX defer? */
+ aac_enqueue_completed(ccb);
+ }
+ }
+
+ /* handle completion processing */
+ aac_complete(sc, 0);
+}
+
+/*
+ * Process completed commands.
+ */
+void
+aac_complete(void *context, int pending)
+{
+ struct aac_softc *sc = (struct aac_softc *)context;
+ struct aac_ccb *ccb;
+
+ /* pull completed commands off the queue */
+ for (;;) {
+ ccb = aac_dequeue_completed(sc);
+ if (ccb == NULL)
+ return;
+ ccb->ac_flags |= AAC_ACF_COMPLETED;
+
+#if 0
+ /* is there a completion handler? */
+ if (ccb->ac_complete != NULL) {
+ ccb->ac_complete(ccb);
+ } else {
+ /* assume that someone is sleeping on this command */
+ wakeup(ccb);
+ }
+#else
+ aac_bio_complete(ccb);
+#endif
+ }
+}
+
+/*
+ * Handle a bio-instigated command that has been completed.
+ */
+void
+aac_bio_complete(struct aac_ccb *ccb)
+{
+ struct scsi_xfer *xs = ccb->ac_xs;
+ struct aac_softc *sc = xs->sc_link->adapter_softc;
+ struct buf *bp = xs->bp;
+ struct aac_blockread_response *brr;
+ struct aac_blockwrite_response *bwr;
+ AAC_FSAStatus status;
+
+ /* fetch relevant status and then release the command */
+ if (bp->b_flags & B_READ) {
+ brr = (struct aac_blockread_response *)&ccb->ac_fib->data[0];
+ status = brr->Status;
+ } else {
+ bwr = (struct aac_blockwrite_response *)&ccb->ac_fib->data[0];
+ status = bwr->Status;
+ }
+ aac_free_ccb(sc, ccb);
+
+ /* fix up the bio based on status */
+ if (status == ST_OK) {
+ bp->b_resid = 0;
+ } else {
+ bp->b_error = EIO;
+ bp->b_flags |= B_ERROR;
+
+ /* XXX be more verbose? */
+ printf("%s: I/O error %d (%s)\n", status,
+ AAC_COMMAND_STATUS(status));
+ }
+ scsi_done(xs);
+}
+
+/*
+ * Send a synchronous command to the controller and wait for a result.
+ */
+int
+aac_sync_command(sc, command, arg0, arg1, arg2, arg3, sp)
+ struct aac_softc *sc;
+ u_int32_t command;
+ u_int32_t arg0;
+ u_int32_t arg1;
+ u_int32_t arg2;
+ u_int32_t arg3;
+ u_int32_t *sp;
+{
+ int i;
+ u_int32_t status;
+ aac_lock_t lock = AAC_LOCK(sc);
+
+ /* populate the mailbox */
+ AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3);
+
+ /* ensure the sync command doorbell flag is cleared */
+ AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
+
+ /* then set it to signal the adapter */
+ AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND);
+ DELAY(AAC_SYNC_DELAY);
+
+ /* spin waiting for the command to complete */
+ for (i = 0; i < AAC_IMMEDIATE_TIMEOUT * 1000; i++) {
+ if (AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND);
+ break;
+ DELAY(1000);
+ }
+ if (i == AAC_IMMEDIATE_TIMEOUT * 1000) {
+ AAC_UNLOCK(sc, lock);
+ return (EIO);
+ }
+
+ /* clear the completion flag */
+ AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND);
+
+ /* get the command status */
+ status = AAC_GET_MAILBOXSTATUS(sc);
+ AAC_UNLOCK(sc, lock);
+ if (sp != NULL)
+ *sp = status;
+ return (0); /* check command return status? */
+}
+
+/*
+ * Send a synchronous FIB to the controller and wait for a result.
+ */
+int
+aac_sync_fib(sc, command, xferstate, data, datasize, result, resultsize)
+ struct aac_softc *sc;
+ u_int32_t command;
+ u_int32_t xferstate;
+ void *data;
+ u_int16_t datasize;
+ void *result;
+ u_int16_t *resultsize;
+{
+ struct aac_fib *fib = &sc->sc_common->ac_sync_fib;
+
+ if (datasize > AAC_FIB_DATASIZE)
+ return (EINVAL);
+
+ /*
+ * Set up the sync FIB
+ */
+ fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
+ AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_EMPTY;
+ fib->Header.XferState |= xferstate;
+ fib->Header.Command = command;
+ fib->Header.StructType = AAC_FIBTYPE_TFIB;
+ fib->Header.Size = sizeof fib + datasize;
+ fib->Header.SenderSize = sizeof *fib;
+ fib->Header.SenderFibAddress = (u_int32_t)fib;
+ fib->Header.ReceiverFibAddress =
+ sc->sc_common_busaddr + offsetof(struct aac_common, ac_sync_fib);
+
+ /*
+ * Copy in data.
+ */
+ if (data != NULL) {
+ bcopy(data, fib->data, datasize);
+ fib->Header.XferState |=
+ AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_NORM;
+ }
+
+ /*
+ * Give the FIB to the controller, wait for a response.
+ */
+ if (aac_sync_command(sc, AAC_MONKER_SYNCFIB,
+ fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) {
+ return (EIO);
+ }
+
+ /*
+ * Copy out the result
+ */
+ if (result != NULL) {
+ *resultsize = fib->Header.Size - sizeof fib->Header;
+ bcopy(fib->data, result, *resultsize);
+ }
+ return (0);
+}
+
+void
+aacminphys(bp)
+ struct buf *bp;
+{
+#if 0
+ u_int8_t *buf = bp->b_data;
+ paddr_t pa;
+ long off;
+#endif
+
+ AAC_DPRINTF(AAC_D_MISC, ("aacminphys(0x%x) ", bp));
+
+#if 1
+#if 0 /* As this is way more than MAXPHYS it's really not necessary. */
+ if (bp->b_bcount > ((GDT_MAXOFFSETS - 1) * PAGE_SIZE))
+ bp->b_bcount = ((GDT_MAXOFFSETS - 1) * PAGE_SIZE);
+#endif
+#else
+ for (off = PAGE_SIZE, pa = vtophys(buf); off < bp->b_bcount;
+ off += PAGE_SIZE)
+ if (pa + off != vtophys(buf + off)) {
+ bp->b_bcount = off;
+ break;
+ }
+#endif
+ minphys(bp);
+}
+
+/*
+ * Read the current firmware status word.
+ */
+int
+aac_sa_get_fwstatus(sc)
+ struct aac_softc *sc;
+{
+ return (AAC_GETREG4(sc, AAC_SA_FWSTATUS));
+}
+
+int
+aac_rx_get_fwstatus(sc)
+ struct aac_softc *sc;
+{
+ return (AAC_GETREG4(sc, AAC_RX_FWSTATUS));
+}
+
+/*
+ * Notify the controller of a change in a given queue
+ */
+
+void
+aac_sa_qnotify(sc, qbit)
+ struct aac_softc *sc;
+ int qbit;
+{
+ AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit);
+}
+
+void
+aac_rx_qnotify(sc, qbit)
+ struct aac_softc *sc;
+ int qbit;
+{
+ AAC_SETREG4(sc, AAC_RX_IDBR, qbit);
+}
+
+/*
+ * Get the interrupt reason bits
+ */
+int
+aac_sa_get_istatus(sc)
+ struct aac_softc *sc;
+{
+ return (AAC_GETREG2(sc, AAC_SA_DOORBELL0));
+}
+
+int
+aac_rx_get_istatus(sc)
+ struct aac_softc *sc;
+{
+ return (AAC_GETREG4(sc, AAC_RX_ODBR));
+}
+
+/*
+ * Clear some interrupt reason bits
+ */
+void
+aac_sa_clear_istatus(sc, mask)
+ struct aac_softc *sc;
+ int mask;
+{
+ AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask);
+}
+
+void
+aac_rx_clear_istatus(sc, mask)
+ struct aac_softc *sc;
+ int mask;
+{
+ AAC_SETREG4(sc, AAC_RX_ODBR, mask);
+}
+
+/*
+ * Populate the mailbox and set the command word
+ */
+void
+aac_sa_set_mailbox(sc, command, arg0, arg1, arg2, arg3)
+ struct aac_softc *sc;
+ u_int32_t command;
+ u_int32_t arg0;
+ u_int32_t arg1;
+ u_int32_t arg2;
+ u_int32_t arg3;
+{
+ AAC_SETREG4(sc, AAC_SA_MAILBOX, command);
+ AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0);
+ AAC_SETREG4(sc, AAC_SA_MAILBOX + 8, arg1);
+ AAC_SETREG4(sc, AAC_SA_MAILBOX + 12, arg2);
+ AAC_SETREG4(sc, AAC_SA_MAILBOX + 16, arg3);
+}
+
+void
+aac_rx_set_mailbox(sc, command, arg0, arg1, arg2, arg3)
+ struct aac_softc *sc;
+ u_int32_t command;
+ u_int32_t arg0;
+ u_int32_t arg1;
+ u_int32_t arg2;
+ u_int32_t arg3;
+{
+ AAC_SETREG4(sc, AAC_RX_MAILBOX, command);
+ AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0);
+ AAC_SETREG4(sc, AAC_RX_MAILBOX + 8, arg1);
+ AAC_SETREG4(sc, AAC_RX_MAILBOX + 12, arg2);
+ AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3);
+}
+
+/*
+ * Fetch the immediate command status word
+ */
+int
+aac_sa_get_mailboxstatus(sc)
+ struct aac_softc *sc;
+{
+ return (AAC_GETREG4(sc, AAC_SA_MAILBOX));
+}
+
+int
+aac_rx_get_mailboxstatus(sc)
+ struct aac_softc *sc;
+{
+ return (AAC_GETREG4(sc, AAC_RX_MAILBOX));
+}
+
+/*
+ * Set/clear interrupt masks
+ */
+void
+aac_sa_set_interrupts(sc, enable)
+ struct aac_softc *sc;
+ int enable;
+{
+ if (enable)
+ AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS);
+ else
+ AAC_SETREG2((sc), AAC_SA_MASK0_SET, ~0);
+}
+
+void
+aac_rx_set_interrupts(sc, enable)
+ struct aac_softc *sc;
+ int enable;
+{
+ if (enable)
+ AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS);
+ else
+ AAC_SETREG4(sc, AAC_RX_OIMR, ~0);
+}
+
+struct aac_ccb *
+aac_get_ccb(sc, flags)
+ struct aac_softc *sc;
+ int flags;
+{
+ struct aac_ccb *ccb;
+ aac_lock_t lock;
+
+ AAC_DPRINTF(AAC_D_QUEUE, ("aac_get_ccb(%p, 0x%x) ", sc, flags));
+
+ lock = AAC_LOCK(sc);
+
+ for (;;) {
+ ccb = TAILQ_FIRST(&sc->sc_free_ccb);
+ if (ccb != NULL)
+ break;
+ if (flags & SCSI_NOSLEEP)
+ goto bail_out;
+ tsleep(&sc->sc_free_ccb, PRIBIO, "aac_ccb", 0);
+ }
+
+ TAILQ_REMOVE(&sc->sc_free_ccb, ccb, ac_chain);
+
+ /* initialise the command/FIB */
+ ccb->ac_sgtable = NULL;
+ ccb->ac_flags = 0;
+ ccb->ac_fib->Header.XferState = AAC_FIBSTATE_EMPTY;
+ ccb->ac_fib->Header.StructType = AAC_FIBTYPE_TFIB;
+ ccb->ac_fib->Header.Flags = 0;
+ ccb->ac_fib->Header.SenderSize = sizeof(struct aac_fib);
+
+ /*
+ * These are duplicated in aac_start to cover the case where an
+ * intermediate stage may have destroyed them. They're left
+ * initialised here for debugging purposes only.
+ */
+ ccb->ac_fib->Header.SenderFibAddress = (u_int32_t)ccb->ac_fib;
+ ccb->ac_fib->Header.ReceiverFibAddress = ccb->ac_fibphys;
+
+ bail_out:
+ AAC_UNLOCK(sc, lock);
+ return (ccb);
+}
+
+void
+aac_free_ccb(sc, ccb)
+ struct aac_softc *sc;
+ struct aac_ccb *ccb;
+{
+ aac_lock_t lock;
+
+ AAC_DPRINTF(AAC_D_QUEUE, ("aac_free_ccb(%p, %p) ", sc, ccb));
+
+ lock = AAC_LOCK(sc);
+
+ TAILQ_INSERT_HEAD(&sc->sc_free_ccb, ccb, ac_chain);
+
+ /* If the free list was empty, wake up potential waiters. */
+ if (TAILQ_NEXT(ccb, ac_chain) == NULL)
+ wakeup(&sc->sc_free_ccb);
+
+ AAC_UNLOCK(sc, lock);
+}
+
+void
+aac_enqueue_ccb(sc, ccb)
+ struct aac_softc *sc;
+ struct aac_ccb *ccb;
+{
+ AAC_DPRINTF(AAC_D_QUEUE, ("aac_enqueue_ccb(%p, %p) ", sc, ccb));
+
+ TAILQ_INSERT_TAIL(&sc->sc_ccbq, ccb, ac_chain);
+ aac_start_ccbs(sc);
+}
+
+void
+aac_start_ccbs(sc)
+ struct aac_softc *sc;
+{
+ struct aac_ccb *ccb;
+
+ AAC_DPRINTF(AAC_D_QUEUE, ("aac_start_ccbs(%p) ", sc));
+
+ while ((ccb = TAILQ_FIRST(&sc->sc_ccbq)) != NULL) {
+ if (ccb->ac_flags & AAC_ACF_WATCHDOG)
+ untimeout(aac_watchdog, ccb);
+
+ if (aac_exec_ccb(ccb) == 0) {
+ ccb->ac_flags |= AAC_ACF_WATCHDOG;
+ timeout(aac_watchdog, ccb,
+ (AAC_WATCH_TIMEOUT * hz) / 1000);
+ break;
+ }
+ TAILQ_REMOVE(&sc->sc_ccbq, ccb, ac_chain);
+
+ if ((ccb->ac_xs->flags & SCSI_POLL) == 0)
+ timeout(aac_timeout, ccb,
+ (ccb->ac_timeout * hz) / 1000);
+ }
+}
+
+int
+aac_exec_ccb(ccb)
+ struct aac_ccb *ccb;
+{
+ struct scsi_xfer *xs = ccb->ac_xs;
+ struct scsi_link *link = xs->sc_link;
+ u_int8_t target = link->target;
+ int i;
+ struct aac_fib *fib;
+ struct aac_blockread *br;
+ struct aac_blockwrite *bw;
+ bus_dmamap_t xfer;
+
+ AAC_DPRINTF(AAC_D_CMD, ("aac_exec_ccb(%p, %p) ", xs, ccb));
+
+ /* build the FIB */
+ fib = ccb->ac_fib;
+ fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED |
+ AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_FROMHOST |
+ AAC_FIBSTATE_REXPECTED | AAC_FIBSTATE_NORM;
+ fib->Header.Command = ContainerCommand;
+ fib->Header.Size = sizeof(struct aac_fib_header);
+
+ switch (xs->cmd->opcode) {
+ case PREVENT_ALLOW:
+ case SYNCHRONIZE_CACHE:
+ if (xs->cmd->opcode == PREVENT_ALLOW) {
+ /* XXX PREVENT_ALLOW support goes here */
+ } else {
+ AAC_DPRINTF(AAC_D_CMD,
+ ("SYNCHRONIZE CACHE tgt %d ", target));
+ }
+ break;
+
+ case WRITE_COMMAND:
+ case WRITE_BIG:
+ bw = (struct aac_blockwrite *)&fib->data[0];
+ bw->Command = VM_CtBlockWrite;
+ bw->ContainerId = target;
+ bw->BlockNumber = ccb->ac_blockno;
+ bw->ByteCount = ccb->ac_blockcnt * DEV_BSIZE;
+ bw->Stable = CUNSTABLE; /* XXX what's appropriate here? */
+ fib->Header.Size += sizeof(struct aac_blockwrite);
+ ccb->ac_sgtable = &bw->SgMap;
+ break;
+
+ case READ_COMMAND:
+ case READ_BIG:
+ br = (struct aac_blockread *)&fib->data[0];
+ br->Command = VM_CtBlockRead;
+ br->ContainerId = target;
+ br->BlockNumber = ccb->ac_blockno;
+ br->ByteCount = ccb->ac_blockcnt * DEV_BSIZE;
+ fib->Header.Size += sizeof(struct aac_blockread);
+ ccb->ac_sgtable = &br->SgMap;
+ break;
+ }
+
+ if (xs->cmd->opcode != PREVENT_ALLOW &&
+ xs->cmd->opcode != SYNCHRONIZE_CACHE) {
+ xfer = ccb->ac_dmamap_xfer;
+ ccb->ac_sgtable->SgCount = xfer->dm_nsegs;
+ for (i = 0; i < xfer->dm_nsegs; i++) {
+ ccb->ac_sgtable->SgEntry[i].SgAddress =
+ xfer->dm_segs[i].ds_addr;
+ ccb->ac_sgtable->SgEntry[i].SgByteCount =
+ xfer->dm_segs[i].ds_len;
+ AAC_DPRINTF(AAC_D_IO,
+ ("#%d va %p pa %p len %x\n", i, buf,
+ xfer->dm_segs[i].ds_addr,
+ xfer->dm_segs[i].ds_len));
+ }
+
+ /* update the FIB size for the s/g count */
+ fib->Header.Size += xfer->dm_nsegs *
+ sizeof(struct aac_sg_entry);
+ }
+
+ aac_start(ccb);
+
+ xs->error = XS_NOERROR;
+ xs->resid = 0;
+ return (1);
+}
+
+/********************************************************************************
+ * Deliver a command to the controller; allocate controller resources at the
+ * last moment when possible.
+ */
+int
+aac_start(struct aac_ccb *ccb)
+{
+ struct aac_softc *sc = ccb->ac_xs->sc_link->adapter_softc;
+
+#if 0
+ /* get the command mapped */
+ aac_map_command(ccb);
+#endif
+
+ /* fix up the address values */
+ ccb->ac_fib->Header.SenderFibAddress = (u_int32_t)ccb->ac_fib;
+ ccb->ac_fib->Header.ReceiverFibAddress = ccb->ac_fibphys;
+
+ /* save a pointer to the command for speedy reverse-lookup */
+ ccb->ac_fib->Header.SenderData = (u_int32_t)ccb; /* XXX ack, sizing */
+
+ /* put the FIB on the outbound queue */
+ if (aac_enqueue_fib(sc, AAC_ADAP_NORM_CMD_QUEUE,
+ ccb->ac_fib->Header.Size, ccb->ac_fib->Header.ReceiverFibAddress))
+ return (EBUSY);
+
+ return (0);
+}
+
+/*
+ * Map a command into controller-visible space.
+ */
+int
+aac_map_command(struct aac_ccb *ccb)
+{
+ struct scsi_xfer *xs = ccb->ac_xs;
+ struct aac_softc *sc = xs->sc_link->adapter_softc;
+ int error;
+
+#if 0
+ /* don't map more than once */
+ if (ccb->ac_flags & AAC_CMD_MAPPED)
+ return;
+#endif
+
+ if (xs->datalen != 0) {
+ error = bus_dmamap_load(sc->sc_dmat, ccb->ac_dmamap_xfer,
+ xs->data, xs->datalen, NULL,
+ (xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT :
+ BUS_DMA_WAITOK);
+ if (error) {
+ printf("%s: aac_scsi_cmd: ", sc->sc_dev.dv_xname);
+ if (error == EFBIG)
+ printf("more than %d dma segs\n",
+ AAC_MAXSGENTRIES);
+ else
+ printf("error %d loading dma map\n", error);
+ return (error);
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, ccb->ac_dmamap_xfer,
+ (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
+ BUS_DMASYNC_PREWRITE);
+ }
+
+#if 0
+ ccb->ac_flags |= AAC_CMD_MAPPED;
+#endif
+ return (0);
+}
+
+/*
+ * Unmap a command from controller-visible space.
+ */
+void
+aac_unmap_command(struct aac_ccb *ccb)
+{
+ struct scsi_xfer *xs = ccb->ac_xs;
+ struct aac_softc *sc = xs->sc_link->adapter_softc;
+
+#if 0
+ if (!(ccb->ac_flags & AAC_CMD_MAPPED))
+ return;
+#endif
+
+ if (xs->datalen != 0) {
+ bus_dmamap_sync(sc->sc_dmat, ccb->ac_dmamap_xfer,
+ (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD :
+ BUS_DMASYNC_POSTWRITE);
+
+ bus_dmamap_unload(sc->sc_dmat, ccb->ac_dmamap_xfer);
+ }
+#if 0
+ ccb->ac_flags &= ~AAC_CMD_MAPPED;
+#endif
+}
+
+void
+aac_timeout(arg)
+ void *arg;
+{
+ struct aac_ccb *ccb = arg;
+ struct scsi_link *link = ccb->ac_xs->sc_link;
+ struct aac_softc *sc = link->adapter_softc;
+ aac_lock_t lock;
+
+ sc_print_addr(link);
+ printf("timed out\n");
+
+ /* XXX Test for multiple timeouts */
+
+ ccb->ac_xs->error = XS_TIMEOUT;
+ lock = AAC_LOCK(sc);
+ aac_enqueue_ccb(sc, ccb);
+ AAC_UNLOCK(sc, lock);
+}
+
+void
+aac_watchdog(arg)
+ void *arg;
+{
+ struct aac_ccb *ccb = arg;
+ struct scsi_link *link = ccb->ac_xs->sc_link;
+ struct aac_softc *sc = link->adapter_softc;
+ aac_lock_t lock;
+
+ lock = AAC_LOCK(sc);
+ ccb->ac_flags &= ~AAC_ACF_WATCHDOG;
+ aac_start_ccbs(sc);
+ AAC_UNLOCK(sc, lock);
+}
+/*
+ * Insert a command into the driver queue, either at the front or at the tail.
+ * It's ok to overload the freelist link as these structures are never on
+ * the freelist at this time.
+ */
+void
+aac_enqueue(sc, xs, infront)
+ struct aac_softc *sc;
+ struct scsi_xfer *xs;
+ int infront;
+{
+ if (infront || LIST_FIRST(&sc->sc_queue) == NULL) {
+ if (LIST_FIRST(&sc->sc_queue) == NULL)
+ sc->sc_queuelast = xs;
+ LIST_INSERT_HEAD(&sc->sc_queue, xs, free_list);
+ return;
+ }
+ LIST_INSERT_AFTER(sc->sc_queuelast, xs, free_list);
+ sc->sc_queuelast = xs;
+}
+
+/*
+ * Pull a command off the front of the driver queue.
+ */
+struct scsi_xfer *
+aac_dequeue(sc)
+ struct aac_softc *sc;
+{
+ struct scsi_xfer *xs;
+
+ xs = LIST_FIRST(&sc->sc_queue);
+ if (xs == NULL)
+ return (NULL);
+ LIST_REMOVE(xs, free_list);
+
+ if (LIST_FIRST(&sc->sc_queue) == NULL)
+ sc->sc_queuelast = NULL;
+
+ return (xs);
+}
+
+/********************************************************************************
+ * Adapter-space FIB queue manipulation
+ *
+ * Note that the queue implementation here is a little funky; neither the PI or
+ * CI will ever be zero. This behaviour is a controller feature.
+ */
+static struct {
+ int size;
+ int notify;
+} aac_qinfo[] = {
+ { AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL },
+ { AAC_HOST_HIGH_CMD_ENTRIES, 0 },
+ { AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY },
+ { AAC_ADAP_HIGH_CMD_ENTRIES, 0 },
+ { AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL },
+ { AAC_HOST_HIGH_RESP_ENTRIES, 0 },
+ { AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY },
+ { AAC_ADAP_HIGH_RESP_ENTRIES, 0 }
+};
+
+/*
+ * Atomically insert an entry into the nominated queue, returns 0 on success
+ * or EBUSY if the queue is full.
+ *
+ * XXX Note that it would be more efficient to defer notifying the controller
+ * in the case where we may be inserting several entries in rapid succession,
+ * but implementing this usefully is difficult.
+ */
+int
+aac_enqueue_fib(struct aac_softc *sc, int queue, u_int32_t fib_size,
+ u_int32_t fib_addr)
+{
+ u_int32_t pi, ci;
+ int error;
+ aac_lock_t lock;
+
+ lock = AAC_LOCK(sc);
+
+ /* get the producer/consumer indices */
+ pi = sc->sc_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
+ ci = sc->sc_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
+
+ /* wrap the queue? */
+ if (pi >= aac_qinfo[queue].size)
+ pi = 0;
+
+ /* check for queue full */
+ if ((pi + 1) == ci) {
+ error = EBUSY;
+ goto out;
+ }
+
+ /* populate queue entry */
+ (sc->sc_qentries[queue] + pi)->aq_fib_size = fib_size;
+ (sc->sc_qentries[queue] + pi)->aq_fib_addr = fib_addr;
+
+ /* update producer index */
+ sc->sc_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1;
+
+ /* notify the adapter if we know how */
+ if (aac_qinfo[queue].notify != 0)
+ AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
+
+ error = 0;
+
+out:
+ AAC_UNLOCK(sc, lock);
+ return (error);
+}
+
+/*
+ * Atomically remove one entry from the nominated queue, returns 0 on success
+ * or ENOENT if the queue is empty.
+ */
+int
+aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size,
+ struct aac_fib **fib_addr)
+{
+ u_int32_t pi, ci;
+ int error;
+ aac_lock_t lock;
+
+ lock = AAC_LOCK(sc);
+
+ /* get the producer/consumer indices */
+ pi = sc->sc_queues->qt_qindex[queue][AAC_PRODUCER_INDEX];
+ ci = sc->sc_queues->qt_qindex[queue][AAC_CONSUMER_INDEX];
+
+ /* check for queue empty */
+ if (ci == pi) {
+ error = ENOENT;
+ goto out;
+ }
+
+ /* wrap the queue? */
+ if (ci >= aac_qinfo[queue].size)
+ ci = 0;
+
+ /* fetch the entry */
+ *fib_size = (sc->sc_qentries[queue] + ci)->aq_fib_size;
+ *fib_addr =
+ (struct aac_fib *)(sc->sc_qentries[queue] + ci)->aq_fib_addr;
+
+ /* update consumer index */
+ sc->sc_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1;
+
+ /* if we have made the queue un-full, notify the adapter */
+ if (((pi + 1) == ci) && (aac_qinfo[queue].notify != 0))
+ AAC_QNOTIFY(sc, aac_qinfo[queue].notify);
+ error = 0;
+
+out:
+ AAC_UNLOCK(sc, lock);
+ return (error);
+}
+
+#ifdef AAC_DEBUG
+/*
+ * Print a FIB
+ */
+void
+aac_print_fib(struct aac_softc *sc, struct aac_fib *fib, char *caller)
+{
+ printf("%s: FIB @ %p\n", caller, fib);
+ printf(" XferState %b\n", fib->Header.XferState, "\20"
+ "\1HOSTOWNED"
+ "\2ADAPTEROWNED"
+ "\3INITIALISED"
+ "\4EMPTY"
+ "\5FROMPOOL"
+ "\6FROMHOST"
+ "\7FROMADAP"
+ "\10REXPECTED"
+ "\11RNOTEXPECTED"
+ "\12DONEADAP"
+ "\13DONEHOST"
+ "\14HIGH"
+ "\15NORM"
+ "\16ASYNC"
+ "\17PAGEFILEIO"
+ "\20SHUTDOWN"
+ "\21LAZYWRITE"
+ "\22ADAPMICROFIB"
+ "\23BIOSFIB"
+ "\24FAST_RESPONSE"
+ "\25APIFIB\n");
+ printf(" Command %d\n", fib->Header.Command);
+ printf(" StructType %d\n", fib->Header.StructType);
+ printf(" Flags 0x%x\n", fib->Header.Flags);
+ printf(" Size %d\n", fib->Header.Size);
+ printf(" SenderSize %d\n", fib->Header.SenderSize);
+ printf(" SenderAddress 0x%x\n", fib->Header.SenderFibAddress);
+ printf(" ReceiverAddress 0x%x\n", fib->Header.ReceiverFibAddress);
+ printf(" SenderData 0x%x\n", fib->Header.SenderData);
+ switch(fib->Header.Command) {
+ case ContainerCommand: {
+ struct aac_blockread *br = (struct aac_blockread *)fib->data;
+ struct aac_blockwrite *bw = (struct aac_blockwrite *)fib->data;
+ struct aac_sg_table *sg = NULL;
+ int i;
+
+ if (br->Command == VM_CtBlockRead) {
+ printf(" BlockRead: container %d 0x%x/%d\n",
+ br->ContainerId, br->BlockNumber, br->ByteCount);
+ sg = &br->SgMap;
+ }
+ if (bw->Command == VM_CtBlockWrite) {
+ printf(" BlockWrite: container %d 0x%x/%d (%s)\n",
+ bw->ContainerId, bw->BlockNumber, bw->ByteCount,
+ bw->Stable == CSTABLE ? "stable" : "unstable");
+ sg = &bw->SgMap;
+ }
+ if (sg != NULL) {
+ printf(" %d s/g entries\n", sg->SgCount);
+ for (i = 0; i < sg->SgCount; i++)
+ printf(" 0x%08x/%d\n",
+ sg->SgEntry[i].SgAddress,
+ sg->SgEntry[i].SgByteCount);
+ }
+ break;
+ }
+ default:
+ printf(" %16D\n", fib->data, " ");
+ printf(" %16D\n", fib->data + 16, " ");
+ break;
+ }
+}
+#endif
diff --git a/sys/dev/ic/aac_tables.h b/sys/dev/ic/aac_tables.h
new file mode 100644
index 00000000000..ea535ea12ba
--- /dev/null
+++ b/sys/dev/ic/aac_tables.h
@@ -0,0 +1,121 @@
+/* $OpenBSD: aac_tables.h,v 1.1 2000/11/10 09:39:35 niklas Exp $ */
+
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * Copyright (c) 2000 Niklas Hallqvist
+ * 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.
+ *
+ * $FreeBSD: /c/ncvs/src/sys/dev/aac/aac_tables.h,v 1.1 2000/09/13 03:20:34 msmith Exp $
+ */
+
+/*
+ * Status codes for block read/write commands, etc.
+ *
+ * XXX many of these would not normally be returned, as they are
+ * relevant only to FSA operations.
+ */
+static struct aac_code_lookup aac_command_status_table[] = {
+ { "OK", 0 },
+ { "operation not permitted", 1 },
+ { "not found", 2 },
+ { "I/O error", 5 },
+ { "device not configured", 6 },
+ { "too big", 7 },
+ { "permission denied", 13 },
+ { "file exists", 17 },
+ { "cross-device link", 18 },
+ { "operation not supported by device", 19 },
+ { "not a directory", 20 },
+ { "is a directory", 21 },
+ { "invalid argument", 22 },
+ { "file too large", 27 },
+ { "no space on device", 28 },
+ { "readonly filesystem", 30 },
+ { "too many links", 31 },
+ { "operation would block", 35 },
+ { "file name too long", 63 },
+ { "directory not empty", 66 },
+ { "quota exceeded", 69 },
+ { "stale file handle", 70 },
+ { "too many levels of remote in path", 71 },
+ { "bad file handle", 10001 },
+ { "not sync", 10002 },
+ { "bad cookie", 10003 },
+ { "operation not supported", 10004 },
+ { "too small", 10005 },
+ { "server fault", 10006 },
+ { "bad type", 10007 },
+ { "jukebox", 10008 },
+ { "not mounted", 10009 },
+ { "in maintenace mode", 10010 },
+ { "stale ACL", 10011 },
+ { NULL, 0 },
+ { "unknown command status", 0 }
+};
+
+#define AAC_COMMAND_STATUS(x) aac_describe_code(aac_command_status_table, x)
+
+static struct aac_code_lookup aac_cpu_variant[] = {
+ { "i960JX", CPUI960_JX },
+ { "i960CX", CPUI960_CX },
+ { "i960HX", CPUI960_HX },
+ { "i960RX", CPUI960_RX },
+ { "StrongARM SA110", CPUARM_SA110 },
+ { "PowerPC 603e", CPUPPC_603e },
+ { "Unknown StrongARM", CPUARM_xxx },
+ { "Unknown PowerPC", CPUPPC_xxx },
+ { NULL, 0 },
+ { "Unknown processor", 0 }
+};
+
+static struct aac_code_lookup aac_battery_platform[] = {
+ { "required battery present", PLATFORM_BAT_REQ_PRESENT },
+ { "REQUIRED BATTERY NOT PRESENT", PLATFORM_BAT_REQ_NOTPRESENT },
+ { "optional battery present", PLATFORM_BAT_OPT_PRESENT },
+ { "optional battery not installed", PLATFORM_BAT_OPT_NOTPRESENT },
+ { "no battery support", PLATFORM_BAT_NOT_SUPPORTED },
+ { NULL, 0 },
+ { "unknown battery platform", 0 }
+};
+
+#if 0
+static struct aac_code_lookup aac_container_types[] = {
+ { "Volume", CT_VOLUME },
+ { "RAID 1 (Mirror)", CT_MIRROR },
+ { "RAID 0 (Stripe)", CT_STRIPE },
+ { "RAID 5", CT_RAID5 },
+ { "SSRW", CT_SSRW },
+ { "SSRO", CT_SSRO },
+ { "Morph", CT_MORPH },
+ { "Passthrough", CT_PASSTHRU },
+ { "RAID 4", CT_RAID4 },
+ { "RAID 10", CT_RAID10 },
+ { "RAID 00", CT_RAID00 },
+ { "Volume of Mirrors", CT_VOLUME_OF_MIRRORS },
+ { "Pseudo RAID 3", CT_PSEUDO_RAID3 },
+ { NULL, 0 },
+ { "unknown", 0 }
+};
+#endif
diff --git a/sys/dev/ic/aacreg.h b/sys/dev/ic/aacreg.h
new file mode 100644
index 00000000000..f6a6f7bac32
--- /dev/null
+++ b/sys/dev/ic/aacreg.h
@@ -0,0 +1,717 @@
+/* $OpenBSD: aacreg.h,v 1.1 2000/11/10 09:39:36 niklas Exp $ */
+
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 Scott Long
+ * Copyright (c) 2000 BSDi
+ * Copyright (c) 2000 Niklas Hallqvist
+ * 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.
+ *
+ * $FreeBSD: /c/ncvs/src/sys/dev/aac/aacreg.h,v 1.1 2000/09/13 03:20:34 msmith Exp $
+ */
+
+/*
+ * Data structures defining the interface between the driver and the Adaptec
+ * 'FSA' adapters. Note that many field names and comments here are taken
+ * verbatim from the Adaptec driver source in order to make comparing the
+ * two slightly easier.
+ */
+
+/*
+ * Misc. magic numbers.
+ */
+#define AAC_MAX_CONTAINERS 64
+#define AAC_BLOCK_SIZE 512
+
+/*
+ * Communications interface.
+ *
+ * Where datastructure layouts are closely parallel to the Adaptec sample code,
+ * retain their naming conventions (for now) to aid in cross-referencing.
+ */
+
+/*
+ * We establish 4 command queues and matching response queues. Queues must
+ * be 16-byte aligned, and are sized as follows:
+ */
+#define AAC_HOST_NORM_CMD_ENTRIES 8 /* cmd adapter->host, normal pri */
+#define AAC_HOST_HIGH_CMD_ENTRIES 4 /* cmd adapter->host, high pri */
+#define AAC_ADAP_NORM_CMD_ENTRIES 512 /* cmd host->adapter, normal pri */
+#define AAC_ADAP_HIGH_CMD_ENTRIES 4 /* cmd host->adapter, high pri */
+#define AAC_HOST_NORM_RESP_ENTRIES 512 /* resp, adapter->host, normal pri */
+#define AAC_HOST_HIGH_RESP_ENTRIES 4 /* resp, adapter->host, high pri */
+#define AAC_ADAP_NORM_RESP_ENTRIES 8 /* resp, host->adapter, normal pri */
+#define AAC_ADAP_HIGH_RESP_ENTRIES 4 /* resp, host->adapter, high pri */
+
+#define AAC_TOTALQ_LENGTH \
+ (AAC_HOST_HIGH_CMD_ENTRIES + AAC_HOST_NORM_CMD_ENTRIES + \
+ AAC_ADAP_HIGH_CMD_ENTRIES + AAC_ADAP_NORM_CMD_ENTRIES + \
+ AAC_HOST_HIGH_RESP_ENTRIES + AAC_HOST_NORM_RESP_ENTRIES + \
+ AAC_ADAP_HIGH_RESP_ENTRIES + AAC_ADAP_NORM_RESP_ENTRIES)
+
+#define AAC_QUEUE_COUNT 8
+#define AAC_QUEUE_ALIGN 16
+
+struct aac_queue_entry {
+ u_int32_t aq_fib_size; /* FIB size in bytes */
+ u_int32_t aq_fib_addr; /* receiver-space address of the FIB */
+} __attribute__ ((packed));
+
+#define AAC_PRODUCER_INDEX 0
+#define AAC_CONSUMER_INDEX 1
+
+/*
+ * Table of queue indices and queues used to communicate with the
+ * controller. This structure must be aligned to AAC_QUEUE_ALIGN
+ */
+struct aac_queue_table {
+ /* queue consumer/producer indexes (layout mandated by adapter) */
+ u_int32_t qt_qindex[AAC_QUEUE_COUNT][2];
+
+ /* queue entry structures (layout mandated by adapter) */
+ struct aac_queue_entry qt_HostNormCmdQueue[AAC_HOST_NORM_CMD_ENTRIES];
+ struct aac_queue_entry qt_HostHighCmdQueue[AAC_HOST_HIGH_CMD_ENTRIES];
+ struct aac_queue_entry qt_AdapNormCmdQueue[AAC_ADAP_NORM_CMD_ENTRIES];
+ struct aac_queue_entry qt_AdapHighCmdQueue[AAC_ADAP_HIGH_CMD_ENTRIES];
+ struct aac_queue_entry
+ qt_HostNormRespQueue[AAC_HOST_NORM_RESP_ENTRIES];
+ struct aac_queue_entry
+ qt_HostHighRespQueue[AAC_HOST_HIGH_RESP_ENTRIES];
+ struct aac_queue_entry
+ qt_AdapNormRespQueue[AAC_ADAP_NORM_RESP_ENTRIES];
+ struct aac_queue_entry
+ qt_AdapHighRespQueue[AAC_ADAP_HIGH_RESP_ENTRIES];
+} __attribute__ ((packed));
+
+/*
+ * Adapter Init Structure: this is passed to the adapter with the
+ * AAC_MONKER_INITSTRUCT command to point it at our control structures.
+ */
+struct aac_adapter_init {
+ u_int32_t InitStructRevision;
+#define AAC_INIT_STRUCT_REVISION 3
+ u_int32_t MiniPortRevision;
+ u_int32_t FilesystemRevision;
+ u_int32_t CommHeaderAddress;
+ u_int32_t FastIoCommAreaAddress;
+ u_int32_t AdapterFibsPhysicalAddress;
+ void *AdapterFibsVirtualAddress;
+ u_int32_t AdapterFibsSize;
+ u_int32_t AdapterFibAlign;
+ u_int32_t PrintfBufferAddress;
+ u_int32_t PrintfBufferSize;
+ u_int32_t HostPhysMemPages;
+ u_int32_t HostElapsedSeconds;
+} __attribute__((packed));
+
+/*
+ * Shared data types
+ */
+
+/*
+ * Container types
+ */
+typedef enum {
+ CT_NONE = 0,
+ CT_VOLUME,
+ CT_MIRROR,
+ CT_STRIPE,
+ CT_RAID5,
+ CT_SSRW,
+ CT_SSRO,
+ CT_MORPH,
+ CT_PASSTHRU,
+ CT_RAID4,
+ CT_RAID10, /* stripe of mirror */
+ CT_RAID00, /* stripe of stripe */
+ CT_VOLUME_OF_MIRRORS, /* volume of mirror */
+ CT_PSEUDO_RAID3, /* really raid4 */
+} AAC_FSAVolType;
+
+/*
+ * Host-addressable object types
+ */
+typedef enum {
+ FT_REG = 1, /* regular file */
+ FT_DIR, /* directory */
+ FT_BLK, /* "block" device - reserved */
+ FT_CHR, /* "character special" device - reserved */
+ FT_LNK, /* symbolic link */
+ FT_SOCK, /* socket */
+ FT_FIFO, /* fifo */
+ FT_FILESYS, /* ADAPTEC's "FSA"(tm) filesystem */
+ FT_DRIVE, /* phys disk - addressable in scsi by bus/target/lun */
+ FT_SLICE, /* virtual disk - raw volume - slice */
+ FT_PARTITION, /* FSA part, inside slice, container building block */
+ FT_VOLUME, /* Container - Volume Set */
+ FT_STRIPE, /* Container - Stripe Set */
+ FT_MIRROR, /* Container - Mirror Set */
+ FT_RAID5, /* Container - Raid 5 Set */
+ FT_DATABASE /* Storage object with "foreign" content manager */
+} AAC_FType;
+
+/*
+ * Host-side scatter/gather list for 32-bit commands.
+ */
+struct aac_sg_entry {
+ u_int32_t SgAddress;
+ u_int32_t SgByteCount;
+} __attribute__ ((packed));
+
+struct aac_sg_table {
+ u_int32_t SgCount;
+ struct aac_sg_entry SgEntry[0];
+} __attribute__ ((packed));
+
+/*
+ * Host-side scatter/gather list for 64-bit commands.
+ */
+struct aac_sg_table64 {
+ u_int8_t SgCount;
+ u_int8_t SgSectorsPerPage;
+ u_int16_t SgByteOffset;
+ u_int64_t SgEntry[0];
+} __attribute__ ((packed));
+
+/*
+ * Container creation data
+ */
+struct aac_container_creation {
+ u_int8_t ViaBuildNumber;
+ u_int8_t MicroSecond;
+ u_int8_t Via; /* 1 = FSU, 2 = API, etc. */
+ u_int8_t YearsSince1900;
+ u_int32_t Month:4; /* 1-12 */
+ u_int32_t Day:6; /* 1-32 */
+ u_int32_t Hour:6; /* 0-23 */
+ u_int32_t Minute:6; /* 0-59 */
+ u_int32_t Second:6; /* 0-59 */
+ u_int64_t ViaAdapterSerialNumber;
+} __attribute__ ((packed));
+
+struct FsaRevision {
+ union {
+ struct {
+ u_int8_t dash;
+ u_int8_t type;
+ u_int8_t minor;
+ u_int8_t major;
+ } comp;
+ u_int32_t ul;
+ } external;
+ u_int32_t buildNumber;
+} __attribute__((packed));
+
+/*
+ * Adapter Information
+ */
+
+typedef enum {
+ CPU_NTSIM = 1,
+ CPU_I960,
+ CPU_ARM,
+ CPU_SPARC,
+ CPU_POWERPC,
+ CPU_ALPHA,
+ CPU_P7,
+ CPU_I960_RX,
+ CPU__last
+} AAC_CpuType;
+
+typedef enum {
+ CPUI960_JX = 1,
+ CPUI960_CX,
+ CPUI960_HX,
+ CPUI960_RX,
+ CPUARM_SA110,
+ CPUARM_xxx,
+ CPUPPC_603e,
+ CPUPPC_xxx,
+ CPUSUBTYPE__last
+} AAC_CpuSubType;
+
+typedef enum {
+ PLAT_NTSIM = 1,
+ PLAT_V3ADU,
+ PLAT_CYCLONE,
+ PLAT_CYCLONE_HD,
+ PLAT_BATBOARD,
+ PLAT_BATBOARD_HD,
+ PLAT_YOLO,
+ PLAT_COBRA,
+ PLAT_ANAHEIM,
+ PLAT_JALAPENO,
+ PLAT_QUEENS,
+ PLAT_JALAPENO_DELL,
+ PLAT_POBLANO,
+ PLAT_POBLANO_OPAL,
+ PLAT_POBLANO_SL0,
+ PLAT_POBLANO_SL1,
+ PLAT_POBLANO_SL2,
+ PLAT_POBLANO_XXX,
+ PLAT_JALAPENO_P2,
+ PLAT_HABANERO,
+ PLAT__last
+} AAC_Platform;
+
+typedef enum {
+ OEM_FLAVOR_ADAPTEC = 1,
+ OEM_FLAVOR_DELL,
+ OEM_FLAVOR_HP,
+ OEM_FLAVOR_IBM,
+ OEM_FLAVOR_CPQ,
+ OEM_FLAVOR_BRAND_X,
+ OEM_FLAVOR_BRAND_Y,
+ OEM_FLAVOR_BRAND_Z,
+ OEM_FLAVOR__last
+} AAC_OemFlavor;
+
+/*
+ * XXX the aac-2622 with no battery present reports PLATFORM_BAT_OPT_PRESENT
+ */
+typedef enum {
+ PLATFORM_BAT_REQ_PRESENT = 1, /* BATTERY REQUIRED AND PRESENT */
+ PLATFORM_BAT_REQ_NOTPRESENT, /* BATTERY REQUIRED AND NOT PRESENT */
+ PLATFORM_BAT_OPT_PRESENT, /* BATTERY OPTIONAL AND PRESENT */
+ PLATFORM_BAT_OPT_NOTPRESENT, /* BATTERY OPTIONAL AND NOT PRESENT */
+ PLATFORM_BAT_NOT_SUPPORTED /* BATTERY NOT SUPPORTED */
+} AAC_BatteryPlatform;
+
+/*
+ * Structure used to respond to a RequestAdapterInfo fib.
+ */
+struct aac_adapter_info {
+ AAC_Platform PlatformBase; /* adapter type */
+ AAC_CpuType CpuArchitecture; /* adapter CPU type */
+ AAC_CpuSubType CpuVariant; /* adapter CPU subtype */
+ u_int32_t ClockSpeed; /* adapter CPU clockspeed */
+ u_int32_t ExecutionMem; /* adapter Execution Memory size */
+ u_int32_t BufferMem; /* adapter Data Memory */
+ u_int32_t TotalMem; /* adapter Total Memory */
+ struct FsaRevision KernelRevision; /* adapter Kernel SW Revision */
+ struct FsaRevision MonitorRevision; /* adapter Monitor/Diag SW Rev */
+ struct FsaRevision HardwareRevision; /* TDB */
+ struct FsaRevision BIOSRevision; /* adapter BIOS Revision */
+ u_int32_t ClusteringEnabled;
+ u_int32_t ClusterChannelMask;
+ u_int64_t SerialNumber;
+ AAC_BatteryPlatform batteryPlatform;
+ u_int32_t SupportedOptions; /* supported features of this ctrlr */
+ AAC_OemFlavor OemVariant;
+} __attribute__((packed));
+
+/*
+ * Monitor/Kernel interface.
+ */
+
+/*
+ * Synchronous commands to the monitor/kernel.
+ */
+#define AAC_MONKER_INITSTRUCT 0x05
+#define AAC_MONKER_SYNCFIB 0x0c
+
+/*
+ * Command status values
+ */
+typedef enum {
+ ST_OK = 0,
+ ST_PERM = 1,
+ ST_NOENT = 2,
+ ST_IO = 5,
+ ST_NXIO = 6,
+ ST_E2BIG = 7,
+ ST_ACCES = 13,
+ ST_EXIST = 17,
+ ST_XDEV = 18,
+ ST_NODEV = 19,
+ ST_NOTDIR = 20,
+ ST_ISDIR = 21,
+ ST_INVAL = 22,
+ ST_FBIG = 27,
+ ST_NOSPC = 28,
+ ST_ROFS = 30,
+ ST_MLINK = 31,
+ ST_WOULDBLOCK = 35,
+ ST_NAMETOOLONG = 63,
+ ST_NOTEMPTY = 66,
+ ST_DQUOT = 69,
+ ST_STALE = 70,
+ ST_REMOTE = 71,
+ ST_BADHANDLE = 10001,
+ ST_NOT_SYNC = 10002,
+ ST_BAD_COOKIE = 10003,
+ ST_NOTSUPP = 10004,
+ ST_TOOSMALL = 10005,
+ ST_SERVERFAULT = 10006,
+ ST_BADTYPE = 10007,
+ ST_JUKEBOX = 10008,
+ ST_NOTMOUNTED = 10009,
+ ST_MAINTMODE = 10010,
+ ST_STALEACL = 10011
+} AAC_FSAStatus;
+
+/*
+ * Volume manager commands
+ */
+typedef enum _VM_COMMANDS {
+ VM_Null = 0,
+ VM_NameServe,
+ VM_ContainerConfig,
+ VM_Ioctl,
+ VM_FilesystemIoctl,
+ VM_CloseAll,
+ VM_CtBlockRead,
+ VM_CtBlockWrite,
+ VM_SliceBlockRead, /* raw access to configured "storage objects" */
+ VM_SliceBlockWrite,
+ VM_DriveBlockRead, /* raw access to physical devices */
+ VM_DriveBlockWrite,
+ VM_EnclosureMgt, /* enclosure management */
+ VM_Unused, /* used to be diskset management */
+ VM_CtBlockVerify,
+ VM_CtPerf, /* performance test */
+ VM_CtBlockRead64,
+ VM_CtBlockWrite64,
+ VM_CtBlockVerify64,
+} AAC_VMCommand;
+
+/*
+ * "Mountable object"
+ */
+struct aac_mntobj {
+ u_int32_t ObjectId;
+ char FileSystemName[16];
+ struct aac_container_creation CreateInfo;
+ u_int32_t Capacity;
+ AAC_FSAVolType VolType;
+ AAC_FType ObjType;
+ u_int32_t ContentState;
+#define AAC_FSCS_READONLY 0x0002 /* XXX need more information than this */
+ union {
+ u_int32_t pad[8];
+ } ObjExtension;
+ u_int32_t AlterEgoId;
+} __attribute__ ((packed));
+
+struct aac_mntinfo {
+ AAC_VMCommand Command;
+ AAC_FType MntType;
+ u_int32_t MntCount;
+} __attribute__ ((packed));
+
+struct aac_mntinforesponse {
+ AAC_FSAStatus Status;
+ AAC_FType MntType;
+ u_int32_t MntRespCount;
+ struct aac_mntobj MntTable[1];
+} __attribute__ ((packed));
+
+/*
+ * Write 'stability' options.
+ */
+typedef enum {
+ CSTABLE = 1,
+ CUNSTABLE
+} AAC_CacheLevel;
+
+/*
+ * Commit level response for a write request.
+ */
+typedef enum {
+ CMFILE_SYNC_NVRAM = 1,
+ CMDATA_SYNC_NVRAM,
+ CMFILE_SYNC,
+ CMDATA_SYNC,
+ CMUNSTABLE
+} AAC_CommitLevel;
+
+/*
+ * Block read/write operations.
+ * These structures are packed into the 'data' area in the FIB.
+ */
+
+struct aac_blockread {
+ AAC_VMCommand Command; /* not FSACommand! */
+ u_int32_t ContainerId;
+ u_int32_t BlockNumber;
+ u_int32_t ByteCount;
+ struct aac_sg_table SgMap; /* variable size */
+} __attribute__ ((packed));
+
+struct aac_blockread_response {
+ AAC_FSAStatus Status;
+ u_int32_t ByteCount;
+} __attribute__ ((packed));
+
+struct aac_blockwrite {
+ AAC_VMCommand Command; /* not FSACommand! */
+ u_int32_t ContainerId;
+ u_int32_t BlockNumber;
+ u_int32_t ByteCount;
+ AAC_CacheLevel Stable;
+ struct aac_sg_table SgMap; /* variable size */
+} __attribute__ ((packed));
+
+struct aac_blockwrite_response {
+ AAC_FSAStatus Status;
+ u_int32_t ByteCount;
+ AAC_CommitLevel Committed;
+} __attribute__ ((packed));
+
+/*
+ * Register definitions for the Adaptec AAC-364 'Jalapeno I/II' adapters, based
+ * on the SA110 'StrongArm'.
+ */
+
+/* doorbell 0 (adapter->host) */
+#define AAC_SA_DOORBELL0_CLEAR 0x98
+#define AAC_SA_DOORBELL0_SET 0x9c
+#define AAC_SA_DOORBELL0 0x9c
+#define AAC_SA_MASK0_CLEAR 0xa0
+#define AAC_SA_MASK0_SET 0xa4
+
+/* doorbell 1 (host->adapter) */
+#define AAC_SA_DOORBELL1_CLEAR 0x9a
+#define AAC_SA_DOORBELL1_SET 0x9e
+#define AAC_SA_MASK1_CLEAR 0xa2
+#define AAC_SA_MASK1_SET 0xa6
+
+/* mailbox (20 bytes) */
+#define AAC_SA_MAILBOX 0xa8
+#define AAC_SA_FWSTATUS 0xc4
+
+/*
+ * Register definitions for the Adaptec 'Pablano' adapters, based on the
+ * i960Rx, and other related adapters.
+ */
+
+#define AAC_RX_IDBR 0x20 /* inbound doorbell */
+#define AAC_RX_IISR 0x24 /* inbound interrupt status */
+#define AAC_RX_IIMR 0x28 /* inbound interrupt mask */
+#define AAC_RX_ODBR 0x2c /* outbound doorbell */
+#define AAC_RX_OISR 0x30 /* outbound interrupt status */
+#define AAC_RX_OIMR 0x34 /* outbound interrupt mask */
+
+#define AAC_RX_MAILBOX 0x50 /* mailbox (20 bytes) */
+#define AAC_RX_FWSTATUS 0x6c
+
+/*
+ * Common bit definitions for the doorbell registers.
+ */
+
+/*
+ * Status bits in the doorbell registers.
+ */
+#define AAC_DB_SYNC_COMMAND (1<<0) /* send/completed synchronous FIB */
+#define AAC_DB_COMMAND_READY (1<<1) /* posted one or more commands */
+#define AAC_DB_RESPONSE_READY (1<<2) /* one or more commands complete */
+#define AAC_DB_COMMAND_NOT_FULL (1<<3) /* command queue not full */
+#define AAC_DB_RESPONSE_NOT_FULL (1<<4) /* response queue not full */
+
+/*
+ * The adapter can request the host print a message by setting the
+ * DB_PRINTF flag in DOORBELL0. The driver responds by collecting the
+ * message from the printf buffer, clearing the DB_PRINTF flag in
+ * DOORBELL0 and setting it in DOORBELL1.
+ * (ODBR and IDBR respectively for the i960Rx adapters)
+ */
+#define AAC_DB_PRINTF (1<<5)
+
+/*
+ * Mask containing the interrupt bits we care about. We don't anticipate
+ * (or want) interrupts not in this mask.
+ */
+#define AAC_DB_INTERRUPTS \
+ (AAC_DB_COMMAND_READY | AAC_DB_RESPONSE_READY | AAC_DB_PRINTF)
+
+/*
+ * Queue names
+ *
+ * Note that we base these at 0 in order to use them as array indices. Adaptec
+ * used base 1 for some unknown reason, and sorted them in a different order.
+ */
+#define AAC_HOST_NORM_CMD_QUEUE 0
+#define AAC_HOST_HIGH_CMD_QUEUE 1
+#define AAC_ADAP_NORM_CMD_QUEUE 2
+#define AAC_ADAP_HIGH_CMD_QUEUE 3
+#define AAC_HOST_NORM_RESP_QUEUE 4
+#define AAC_HOST_HIGH_RESP_QUEUE 5
+#define AAC_ADAP_NORM_RESP_QUEUE 6
+#define AAC_ADAP_HIGH_RESP_QUEUE 7
+
+/*
+ * List structure used to chain FIBs (used by the adapter - we hang FIBs off
+ * our private command structure and don't touch these)
+ */
+struct aac_fib_list_entry {
+ struct fib_list_entry *Flink;
+ struct fib_list_entry *Blink;
+} __attribute__((packed));
+
+/*
+ * FIB (FSA Interface Block?); this is the datastructure passed between the
+ * host and adapter.
+ */
+struct aac_fib_header {
+ u_int32_t XferState;
+ u_int16_t Command;
+ u_int8_t StructType;
+ u_int8_t Flags;
+ u_int16_t Size;
+ u_int16_t SenderSize;
+ u_int32_t SenderFibAddress;
+ u_int32_t ReceiverFibAddress;
+ u_int32_t SenderData;
+ union {
+ struct {
+ u_int32_t ReceiverTimeStart;
+ u_int32_t ReceiverTimeDone;
+ } _s;
+ struct aac_fib_list_entry FibLinks;
+ } _u;
+} __attribute__((packed));
+
+#define AAC_FIB_DATASIZE (512 - sizeof(struct aac_fib_header))
+
+struct aac_fib {
+ struct aac_fib_header Header;
+ u_int8_t data[AAC_FIB_DATASIZE];
+} __attribute__((packed));
+
+/*
+ * FIB commands
+ */
+typedef enum {
+ TestCommandResponse = 1,
+ TestAdapterCommand = 2,
+
+ /* lowlevel and comm commands */
+ LastTestCommand = 100,
+ ReinitHostNormCommandQueue = 101,
+ ReinitHostHighCommandQueue = 102,
+ ReinitHostHighRespQueue = 103,
+ ReinitHostNormRespQueue = 104,
+ ReinitAdapNormCommandQueue = 105,
+ ReinitAdapHighCommandQueue = 107,
+ ReinitAdapHighRespQueue = 108,
+ ReinitAdapNormRespQueue = 109,
+ InterfaceShutdown = 110,
+ DmaCommandFib = 120,
+ StartProfile = 121,
+ TermProfile = 122,
+ SpeedTest = 123,
+ TakeABreakPt = 124,
+ RequestPerfData = 125,
+ SetInterruptDefTimer= 126,
+ SetInterruptDefCount= 127,
+ GetInterruptDefStatus= 128,
+ LastCommCommand = 129,
+
+ /* filesystem commands */
+ NuFileSystem = 300,
+ UFS = 301,
+ HostFileSystem = 302,
+ LastFileSystemCommand = 303,
+
+ /* Container Commands */
+ ContainerCommand = 500,
+ ContainerCommand64 = 501,
+
+ /* Cluster Commands */
+ ClusterCommand = 550,
+
+ /* Scsi Port commands (scsi passthrough) */
+ ScsiPortCommand = 600,
+
+ /* misc house keeping and generic adapter initiated commands */
+ AifRequest = 700,
+ CheckRevision = 701,
+ FsaHostShutdown = 702,
+ RequestAdapterInfo = 703,
+ IsAdapterPaused = 704,
+ SendHostTime = 705,
+ LastMiscCommand = 706
+} AAC_FibCommands;
+
+/*
+ * FIB types
+ */
+#define AAC_FIBTYPE_TFIB 1
+#define AAC_FIBTYPE_TQE 2
+#define AAC_FIBTYPE_TCTPERF 3
+
+/*
+ * FIB transfer state
+ */
+#define AAC_FIBSTATE_HOSTOWNED (1<<0) /* owned by the host */
+#define AAC_FIBSTATE_ADAPTEROWNED (1<<1) /* owned by the adapter */
+#define AAC_FIBSTATE_INITIALISED (1<<2) /* initialised */
+#define AAC_FIBSTATE_EMPTY (1<<3) /* empty */
+#define AAC_FIBSTATE_FROMPOOL (1<<4) /* allocated from pool */
+#define AAC_FIBSTATE_FROMHOST (1<<5) /* sent from the host */
+#define AAC_FIBSTATE_FROMADAP (1<<6) /* sent from the adapter */
+#define AAC_FIBSTATE_REXPECTED (1<<7) /* response is expected */
+#define AAC_FIBSTATE_RNOTEXPECTED (1<<8) /* response is not expected */
+#define AAC_FIBSTATE_DONEADAP (1<<9) /* processed by the adapter */
+#define AAC_FIBSTATE_DONEHOST (1<<10) /* processed by the host */
+#define AAC_FIBSTATE_HIGH (1<<11) /* high priority */
+#define AAC_FIBSTATE_NORM (1<<12) /* normal priority */
+#define AAC_FIBSTATE_ASYNC (1<<13)
+#define AAC_FIBSTATE_ASYNCIO (1<<13) /* to be removed */
+#define AAC_FIBSTATE_PAGEFILEIO (1<<14) /* to be removed */
+#define AAC_FIBSTATE_SHUTDOWN (1<<15)
+#define AAC_FIBSTATE_LAZYWRITE (1<<16) /* to be removed */
+#define AAC_FIBSTATE_ADAPMICROFIB (1<<17)
+#define AAC_FIBSTATE_BIOSFIB (1<<18)
+#define AAC_FIBSTATE_FAST_RESPONSE (1<<19) /* fast response capable */
+#define AAC_FIBSTATE_APIFIB (1<<20)
+
+/*
+ * FIB error values
+ */
+#define AAC_ERROR_NORMAL 0x00
+#define AAC_ERROR_PENDING 0x01
+#define AAC_ERROR_FATAL 0x02
+#define AAC_ERROR_INVALID_QUEUE 0x03
+#define AAC_ERROR_NOENTRIES 0x04
+#define AAC_ERROR_SENDFAILED 0x05
+#define AAC_ERROR_INVALID_QUEUE_PRIORITY 0x06
+#define AAC_ERROR_FIB_ALLOCATION_FAILED 0x07
+#define AAC_ERROR_FIB_DEALLOCATION_FAILED 0x08
+
+/*
+ * Adapter Status Register
+ *
+ * Phase Staus mailbox is 32bits:
+ * <31:16> = Phase Status
+ * <15:0> = Phase
+ *
+ * The adapter reports its present state through the phase. Only
+ * a single phase should be ever be set. Each phase can have multiple
+ * phase status bits to provide more detailed information about the
+ * state of the adapter.
+ */
+#define AAC_SELF_TEST_FAILED 0x00000004
+#define AAC_UP_AND_RUNNING 0x00000080
+#define AAC_KERNEL_PANIC 0x00000100
diff --git a/sys/dev/ic/aacvar.h b/sys/dev/ic/aacvar.h
new file mode 100644
index 00000000000..65c9aee3eea
--- /dev/null
+++ b/sys/dev/ic/aacvar.h
@@ -0,0 +1,341 @@
+/* $OpenBSD: aacvar.h,v 1.1 2000/11/10 09:39:36 niklas Exp $ */
+
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * Copyright (c) 2000 Niklas Hallqvist
+ * 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.
+ *
+ * $FreeBSD: /c/ncvs/src/sys/dev/aac/aacvar.h,v 1.1 2000/09/13 03:20:34 msmith Exp $
+ */
+
+/*
+ * This driver would not have rewritten for OpenBSD if it was not for the
+ * hardware dontion from Nocom. I want to thank them for their support.
+ * Of course, credit should go to Mike Smith for the original work he did
+ * in the FreeBSD driver where I found lots of inspiration.
+ * - Niklas Hallqvist
+ */
+
+/* Debugging */
+#ifdef AAC_DEBUG
+#define AAC_DPRINTF(mask, args) if (aac_debug & (mask)) printf args
+#define AAC_D_INTR 0x01
+#define AAC_D_MISC 0x02
+#define AAC_D_CMD 0x04
+#define AAC_D_QUEUE 0x08
+#define AAC_D_IO 0x10
+extern int aac_debug;
+
+#define AAC_PRINT_FIB(sc, fib) aac_print_fib((sc), (fib), __FUNCTION__)
+#else
+#define AAC_DPRINTF(mask, args)
+#define AAC_PRINT_FIB(sc, fib)
+#endif
+
+struct aac_code_lookup {
+ char *string;
+ u_int32_t code;
+};
+
+struct aac_softc;
+
+/*
+ * We allocate a small set of FIBs for the adapter to use to send us messages.
+ */
+#define AAC_ADAPTER_FIBS 8
+
+/*
+ * Firmware messages are passed in the printf buffer.
+ */
+#define AAC_PRINTF_BUFSIZE 256
+
+/*
+ * We wait this many seconds for the adapter to come ready if it is still
+ * booting.
+ */
+#define AAC_BOOT_TIMEOUT (3 * 60)
+
+/*
+ * Wait this long for a lost interrupt to get detected.
+ */
+#define AAC_WATCH_TIMEOUT 10000 /* 10000 * 1ms = 10s */
+
+/*
+ * Timeout for immediate commands.
+ */
+#define AAC_IMMEDIATE_TIMEOUT 30
+
+/*
+ * Delay 20ms after the qnotify in sync operations. Experimentally deduced.
+ */
+#define AAC_SYNC_DELAY 20000
+
+/*
+ * The firmware interface allows for a 16-bit s/g list length. We limit
+ * ourselves to a reasonable maximum and ensure alignment.
+ */
+#define AAC_MAXSGENTRIES 64 /* max S/G entries, limit 65535 */
+/*
+ * We gather a number of adapter-visible items into a single structure.
+ *
+ * The ordering of this strucure may be important; we copy the Linux driver:
+ *
+ * Adapter FIBs
+ * Init struct
+ * Queue headers (Comm Area)
+ * Printf buffer
+ *
+ * In addition, we add:
+ * Sync Fib
+ */
+struct aac_common {
+ /* fibs for the controller to send us messages */
+ struct aac_fib ac_fibs[AAC_ADAPTER_FIBS];
+
+ /* the init structure */
+ struct aac_adapter_init ac_init;
+
+ /* arena within which the queue structures are kept */
+ u_int8_t ac_qbuf[sizeof(struct aac_queue_table) + AAC_QUEUE_ALIGN];
+
+ /* buffer for text messages from the controller */
+ char ac_printf[AAC_PRINTF_BUFSIZE];
+
+ /* fib for synchronous commands */
+ struct aac_fib ac_sync_fib;
+};
+
+/*
+ * Interface operations
+ */
+struct aac_interface {
+ int (*aif_get_fwstatus) __P((struct aac_softc *));
+ void (*aif_qnotify) __P((struct aac_softc *, int));
+ int (*aif_get_istatus) __P((struct aac_softc *));
+ void (*aif_set_istatus) __P((struct aac_softc *, int));
+ void (*aif_set_mailbox) __P((struct aac_softc *, u_int32_t,
+ u_int32_t, u_int32_t, u_int32_t, u_int32_t));
+ int (*aif_get_mailboxstatus) __P((struct aac_softc *));
+ void (*aif_set_interrupts) __P((struct aac_softc *, int));
+};
+extern struct aac_interface aac_rx_interface;
+extern struct aac_interface aac_sa_interface;
+
+#define AAC_GET_FWSTATUS(sc) ((sc)->sc_if.aif_get_fwstatus(sc))
+#define AAC_QNOTIFY(sc, qbit) \
+ ((sc)->sc_if.aif_qnotify((sc), (qbit)))
+#define AAC_GET_ISTATUS(sc) ((sc)->sc_if.aif_get_istatus(sc))
+#define AAC_CLEAR_ISTATUS(sc, mask) \
+ ((sc)->sc_if.aif_set_istatus((sc), (mask)))
+#define AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3) \
+ do { \
+ ((sc)->sc_if.aif_set_mailbox((sc), (command), (arg0), \
+ (arg1), (arg2), (arg3))); \
+ } while(0)
+#define AAC_GET_MAILBOXSTATUS(sc) \
+ ((sc)->sc_if.aif_get_mailboxstatus(sc))
+#define AAC_MASK_INTERRUPTS(sc) \
+ ((sc)->sc_if.aif_set_interrupts((sc), 0))
+#define AAC_UNMASK_INTERRUPTS(sc) \
+ ((sc)->sc_if.aif_set_interrupts((sc), 1))
+
+#define AAC_SETREG4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (reg), (val))
+#define AAC_GETREG4(sc, reg) \
+ bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (reg))
+#define AAC_SETREG2(sc, reg, val) \
+ bus_space_write_2((sc)->sc_memt, (sc)->sc_memh, (reg), (val))
+#define AAC_GETREG2(sc, reg) \
+ bus_space_read_2((sc)->sc_memt, (sc)->sc_memh, (reg))
+#define AAC_SETREG1(sc, reg, val) \
+ bus_space_write_1((sc)->sc_memt, (sc)->sc_memh, (reg), (val))
+#define AAC_GETREG1(sc, reg) \
+ bus_space_read_1((sc)->sc_memt, (sc)->sc_memh, (reg))
+
+/*
+ * Per-container data structure
+ */
+struct aac_container
+{
+ struct aac_mntobj co_mntobj;
+ struct device co_disk;
+};
+
+/*
+ * A command contol block, one for each corresponding command index of the
+ * controller.
+ */
+struct aac_ccb {
+ TAILQ_ENTRY(aac_ccb) ac_chain;
+ struct scsi_xfer *ac_xs;
+ struct aac_fib *ac_fib; /* FIB associated with this command */
+ bus_addr_t ac_fibphys; /* bus address of the FIB */
+ bus_dmamap_t ac_dmamap_xfer;
+ struct aac_sg_table *ac_sgtable;/* pointer to s/g table in command */
+ int ac_timeout;
+ u_int32_t ac_blockno;
+ u_int32_t ac_blockcnt;
+ u_int8_t ac_flags;
+#define AAC_ACF_WATCHDOG 0x1
+#define AAC_ACF_COMPLETED 0x2
+};
+
+/*
+ * Per-controller structure.
+ */
+struct aac_softc {
+ struct device sc_dev;
+ void *sc_ih;
+ struct scsi_link sc_link; /* Virtual SCSI bus for cache devs */
+
+ bus_space_tag_t sc_memt;
+ bus_space_handle_t sc_memh;
+ bus_dma_tag_t sc_dmat; /* parent DMA tag */
+
+ /* controller features, limits and status */
+ int sc_state;
+#define AAC_STATE_SUSPEND (1<<0)
+#define AAC_STATE_OPEN (1<<1)
+#define AAC_STATE_INTERRUPTS_ON (1<<2)
+#define AAC_STATE_AIF_SLEEPER (1<<3)
+ struct FsaRevision sc_revision;
+
+ int sc_hwif; /* controller hardware interface */
+#define AAC_HWIF_I960RX 0
+#define AAC_HWIF_STRONGARM 1
+
+ struct aac_common *sc_common;
+ u_int32_t sc_common_busaddr;
+ struct aac_interface sc_if;
+
+ /* XXX This should really be dynamic. It is very wasteful now. */
+ struct aac_ccb sc_ccbs[AAC_ADAP_NORM_CMD_ENTRIES];
+ TAILQ_HEAD(, aac_ccb) sc_free_ccb, sc_ccbq;
+ /* commands on hold for controller resources */
+ TAILQ_HEAD(, aac_ccb) sc_ready;
+ /* commands which have been returned by the controller */
+ TAILQ_HEAD(, aac_ccb) sc_completed;
+ LIST_HEAD(, scsi_xfer) sc_queue;
+ struct scsi_xfer *sc_queuelast;
+
+ /* command management */
+ struct aac_queue_table *sc_queues;
+ struct aac_queue_entry *sc_qentries[AAC_QUEUE_COUNT];
+
+ struct {
+ u_int8_t hd_present;
+ u_int8_t hd_is_logdrv;
+ u_int8_t hd_is_arraydrv;
+ u_int8_t hd_is_master;
+ u_int8_t hd_is_parity;
+ u_int8_t hd_is_hotfix;
+ u_int8_t hd_master_no;
+ u_int8_t hd_lock;
+ u_int8_t hd_heads;
+ u_int8_t hd_secs;
+ u_int16_t hd_devtype;
+ u_int32_t hd_size;
+ u_int8_t hd_ldr_no;
+ u_int8_t hd_rw_attribs;
+ u_int32_t hd_start_sec;
+ } sc_hdr[AAC_MAX_CONTAINERS];
+};
+
+/* XXX These have to become spinlocks in case of SMP */
+#define AAC_LOCK(sc) splbio()
+#define AAC_UNLOCK(sc, lock) splx(lock)
+typedef int aac_lock_t;
+
+void aacminphys __P((struct buf *));
+int aac_attach __P((struct aac_softc *));
+int aac_intr __P((void *));
+
+#ifdef __GNUC__
+/* These all require correctly aligned buffers */
+static __inline__ void aac_enc16 __P((u_int8_t *, u_int16_t));
+static __inline__ void aac_enc32 __P((u_int8_t *, u_int32_t));
+static __inline__ u_int16_t aac_dec16 __P((u_int8_t *));
+static __inline__ u_int32_t aac_dec32 __P((u_int8_t *));
+
+static __inline__ void
+aac_enc16(addr, value)
+ u_int8_t *addr;
+ u_int16_t value;
+{
+ *(u_int16_t *)addr = htole16(value);
+}
+
+static __inline__ void
+aac_enc32(addr, value)
+ u_int8_t *addr;
+ u_int32_t value;
+{
+ *(u_int32_t *)addr = htole32(value);
+}
+
+static __inline__ u_int16_t
+aac_dec16(addr)
+ u_int8_t *addr;
+{
+ return letoh16(*(u_int16_t *)addr);
+}
+
+static __inline__ u_int32_t
+aac_dec32(addr)
+ u_int8_t *addr;
+{
+ return letoh32(*(u_int32_t *)addr);
+}
+
+/*
+ * Queue primitives
+ *
+ * These are broken out individually to make statistics gathering easier.
+ */
+
+static __inline__ void
+aac_enqueue_completed(struct aac_ccb *ccb)
+{
+ struct aac_softc *sc = ccb->ac_xs->sc_link->adapter_softc;
+ aac_lock_t lock;
+
+ lock = AAC_LOCK(sc);
+ TAILQ_INSERT_TAIL(&sc->sc_completed, ccb, ac_chain);
+ AAC_UNLOCK(sc, lock);
+}
+
+static __inline__ struct aac_ccb *
+aac_dequeue_completed(struct aac_softc *sc)
+{
+ struct aac_ccb *ccb;
+ aac_lock_t lock;
+
+ lock = AAC_LOCK(sc);
+ if ((ccb = TAILQ_FIRST(&sc->sc_completed)) != NULL)
+ TAILQ_REMOVE(&sc->sc_completed, ccb, ac_chain);
+ AAC_UNLOCK(sc, lock);
+ return (ccb);
+}
+#endif
diff --git a/sys/dev/pci/aac_pci.c b/sys/dev/pci/aac_pci.c
new file mode 100644
index 00000000000..def32a49fcf
--- /dev/null
+++ b/sys/dev/pci/aac_pci.c
@@ -0,0 +1,203 @@
+/* $OpenBSD: aac_pci.c,v 1.1 2000/11/10 09:39:36 niklas Exp $ */
+
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * Copyright (c) 2000 Niklas Hallqvist
+ * 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.
+ *
+ * $FreeBSD: /c/ncvs/src/sys/dev/aac/aac_pci.c,v 1.1 2000/09/13 03:20:34 msmith Exp $
+ */
+
+/*
+ * This driver would not have rewritten for OpenBSD if it was not for the
+ * hardware dontion from Nocom. I want to thank them for their support.
+ * Of course, credit should go to Mike Smith for the original work he did
+ * in the FreeBSD driver where I found lots of inspiration.
+ * - Niklas Hallqvist
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+#include <machine/intr.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/ic/aacreg.h>
+#include <dev/ic/aacvar.h>
+
+int aac_pci_probe __P((struct device *, void *, void *));
+void aac_pci_attach __P((struct device *, struct device *, void *));
+
+struct aac_ident {
+ u_int16_t vendor;
+ u_int16_t device;
+ int hwif;
+} aac_identifiers[] = {
+ { PCI_VENDOR_DELL, PCI_PRODUCT_DELL_PERC_2SI, AAC_HWIF_I960RX },
+ { PCI_VENDOR_DELL, PCI_PRODUCT_DELL_PERC_3DI, AAC_HWIF_I960RX },
+ { PCI_VENDOR_DELL, PCI_PRODUCT_DELL_PERC_3SI, AAC_HWIF_I960RX },
+ { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_AAC2622, AAC_HWIF_I960RX },
+ { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_AAC364, AAC_HWIF_STRONGARM },
+ { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_AAC3642, AAC_HWIF_STRONGARM },
+ { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_PERC_2QC, AAC_HWIF_STRONGARM },
+ { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_PERC_3QC, AAC_HWIF_STRONGARM },
+ { PCI_VENDOR_HP, PCI_PRODUCT_HP_NETRAID_4M, AAC_HWIF_STRONGARM },
+ { 0, 0, 0 }
+};
+
+struct cfattach aac_pci_ca = {
+ sizeof (struct aac_softc), aac_pci_probe, aac_pci_attach
+};
+
+/*
+ * Determine whether this is one of our supported adapters.
+ */
+int
+aac_pci_probe(parent, match, aux)
+ struct device *parent;
+ void *match;
+ void *aux;
+{
+ struct pci_attach_args *pa = aux;
+ struct aac_ident *m;
+
+ for (m = aac_identifiers; m->vendor != 0; m++)
+ if (m->vendor == PCI_VENDOR(pa->pa_id) &&
+ m->device == PCI_PRODUCT(pa->pa_id))
+ return (1);
+ return (0);
+}
+
+void
+aac_pci_attach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct pci_attach_args *pa = aux;
+ pci_chipset_tag_t pc = pa->pa_pc;
+ struct aac_softc *sc = (void *)self;
+ u_int16_t command;
+ bus_addr_t membase;
+ bus_size_t memsize;
+ pci_intr_handle_t ih;
+ const char *intrstr;
+ int state = 0;
+ struct aac_ident *m;
+
+ printf(": ");
+
+ /*
+ * Verify that the adapter is correctly set up in PCI space.
+ */
+ command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+ command |= PCI_COMMAND_MASTER_ENABLE;
+ pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, command);
+ command = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+ AAC_DPRINTF(AAC_D_MISC, ("pci command status reg 0x08x "));
+ if (!(command & PCI_COMMAND_MASTER_ENABLE)) {
+ printf("can't enable bus-master feature\n");
+ goto bail_out;
+ }
+ if (!(command & PCI_COMMAND_MEM_ENABLE)) {
+ printf("memory window not available\n");
+ goto bail_out;
+ }
+
+ /*
+ * Map control/status registers.
+ */
+ if (pci_mapreg_map(pa, PCI_MAPREG_START,
+ PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->sc_memt,
+ &sc->sc_memh, &membase, &memsize)) {
+ printf("can't find mem space\n");
+ goto bail_out;
+ }
+ state++;
+
+ if (pci_intr_map(pc, pa->pa_intrtag, pa->pa_intrpin, pa->pa_intrline,
+ &ih)) {
+ printf("couldn't map interrupt\n");
+ goto bail_out;
+ }
+ intrstr = pci_intr_string(pc, ih);
+ sc->sc_ih = pci_intr_establish(pc, ih, IPL_BIO, aac_intr, sc,
+ sc->sc_dev.dv_xname);
+ if (sc->sc_ih == NULL) {
+ printf("couldn't establish interrupt");
+ if (intrstr != NULL)
+ printf(" at %s", intrstr);
+ printf("\n");
+ goto bail_out;
+ }
+ state++;
+ if (intrstr != NULL)
+ printf("%s\n", intrstr);
+
+ sc->sc_dmat = pa->pa_dmat;
+
+ for (m = aac_identifiers; m->vendor != 0; m++)
+ if (m->vendor == PCI_VENDOR(pa->pa_id) &&
+ m->device == PCI_PRODUCT(pa->pa_id)) {
+ sc->sc_hwif = m->hwif;
+ switch(sc->sc_hwif) {
+ case AAC_HWIF_I960RX:
+ AAC_DPRINTF(AAC_D_MISC,
+ ("set hardware up for i960Rx"));
+ sc->sc_if = aac_rx_interface;
+ break;
+
+ case AAC_HWIF_STRONGARM:
+ AAC_DPRINTF(AAC_D_MISC,
+ ("set hardware up for StrongARM"));
+ sc->sc_if = aac_sa_interface;
+ break;
+ }
+ break;
+ }
+
+ if (aac_attach(sc))
+ goto bail_out;
+
+ return;
+
+ bail_out:
+ if (state > 1)
+ pci_intr_disestablish(pc, sc->sc_ih);
+ if (state > 0)
+ bus_space_unmap(sc->sc_memt, sc->sc_memh, memsize);
+ return;
+}
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 1c685c5d91f..fbe8aa61feb 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.85 2000/10/17 15:47:12 jason Exp $
+# $OpenBSD: files.pci,v 1.86 2000/11/10 09:39:36 niklas Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -105,6 +105,12 @@ file dev/pci/auvia.c auvia
attach gdt at pci with gdt_pci
file dev/pci/gdt_pci.c gdt_pci
+# Adaptec FSA (file system accelerators) RAID adapters
+device aac: scsi
+attach aac at pci with aac_pci
+file dev/pci/aac_pci.c aac_pci
+file dev/ic/aac.c aac
+
# Qlogic ISP 10x0 (PCI) family
# device declaration in sys/conf/files
attach isp at pci with isp_pci