* Add in support for the IBM ServeRAID controller.
authorDavid Rhodus <drhodus@dragonflybsd.org>
Thu, 15 Jan 2004 15:41:23 +0000 (15:41 +0000)
committerDavid Rhodus <drhodus@dragonflybsd.org>
Thu, 15 Jan 2004 15:41:23 +0000 (15:41 +0000)
Port done and sent in by:
TONETANI Tomokazu <ghwt+dragonfly-kernel@les.ath.cx>

13 files changed:
etc/MAKEDEV
share/man/man4/ips.4 [new file with mode: 0644]
sys/conf/files
sys/dev/raid/Makefile
sys/dev/raid/ips/Makefile [new file with mode: 0644]
sys/dev/raid/ips/ips.c [new file with mode: 0644]
sys/dev/raid/ips/ips.h [new file with mode: 0644]
sys/dev/raid/ips/ips_commands.c [new file with mode: 0644]
sys/dev/raid/ips/ips_disk.c [new file with mode: 0644]
sys/dev/raid/ips/ips_disk.h [new file with mode: 0644]
sys/dev/raid/ips/ips_ioctl.c [new file with mode: 0644]
sys/dev/raid/ips/ips_ioctl.h [new file with mode: 0644]
sys/dev/raid/ips/ips_pci.c [new file with mode: 0644]

index da531ae..e7d2109 100644 (file)
@@ -21,7 +21,7 @@
 #
 #      @(#)MAKEDEV     5.2 (Berkeley) 6/22/90
 # $FreeBSD: src/etc/MAKEDEV,v 1.243.2.57 2003/02/10 11:35:53 simokawa Exp $
-# $DragonFly: src/etc/MAKEDEV,v 1.4 2004/01/04 20:33:44 eirikn Exp $
+# $DragonFly: src/etc/MAKEDEV,v 1.5 2004/01/15 15:41:23 drhodus Exp $
 #
 # Device "make" file.  Valid arguments:
 #      all     makes all known devices, standard number of units (or close)
@@ -48,6 +48,8 @@
 #      fd*     floppy disk drives (3 1/2", 5 1/4")
 #      fla*    M-Systems DiskOnChip
 #      idad*   Compaq Smart-2 RAID arrays
+#      ips*    IBM ServeRAID controller
+#      ipsd*   IBM ServeRAID
 #      mcd*    Mitsumi CD-ROM disks
 #      md*     Memory (or malloc) disk
 #      mlx*    Mylex DAC960 RAID controllers
@@ -449,7 +451,7 @@ wt*)
        ;;
 
 # Individual slices.
-aacd*s*|ad*s*|ar*s*|afd*s*|amrd*s*|da*s*|fla*s*|idad*s*|md*s*|mlxd*s*|pst*s*|twed*s*|vn*s*|wd*s*|wfd*s*)
+aacd*s*|ad*s*|ar*s*|afd*s*|amrd*s*|da*s*|fla*s*|idad*s*|ipsd*s*|md*s*|mlxd*s*|pst*s*|twed*s*|vn*s*|wd*s*|wfd*s*)
        umask $disk_umask
        case $i in
        aacd*s*) name=aacd; chr=151;;
@@ -460,6 +462,7 @@ aacd*s*|ad*s*|ar*s*|afd*s*|amrd*s*|da*s*|fla*s*|idad*s*|md*s*|mlxd*s*|pst*s*|twe
        da*s*) name=da;  chr=13;;
        fla*s*) name=fla; chr=102;;
        idad*s*) name=idad; chr=109;;
+       ipsd*s*) name=ipsd; chr=176;;
        md*s*) name=md;  chr=95;;
        mlxd*s*) name=mlxd; chr=131;;
        pst*s*) name=pst; chr=168;;
@@ -469,7 +472,7 @@ aacd*s*|ad*s*|ar*s*|afd*s*|amrd*s*|da*s*|fla*s*|idad*s*|md*s*|mlxd*s*|pst*s*|twe
        wfd*s*) name=wfd; chr=87;;
        esac
        case $i in
-       aacd*s*|amrd*s*|idad*s*|mlxd*s*|twed*s*)
+       aacd*s*|amrd*s*|idad*s*|ipsd*s*|mlxd*s*|twed*s*)
                unit=`expr $i : '....\([0-9]*\)s'`
                slice=`expr $i : '....[0-9]*s\([0-9]*\)'`
                part=`expr $i : '....[0-9]*s[0-9]*\(.*\)'`
@@ -633,7 +636,7 @@ fd*)
        umask 77
        ;;
 
-aacd*|ad*|ar*|afd*|amrd*|da*|fla*|idad*|md*|mlxd*|pst*|twed*|vn*|wd*|wfd*)
+aacd*|ad*|ar*|afd*|amrd*|da*|fla*|idad*|ipsd*|md*|mlxd*|pst*|twed*|vn*|wd*|wfd*)
        umask $disk_umask
        case $i in
        aacd*) name=aacd; chr=151;;
@@ -644,6 +647,7 @@ aacd*|ad*|ar*|afd*|amrd*|da*|fla*|idad*|md*|mlxd*|pst*|twed*|vn*|wd*|wfd*)
        da*) name=da;  chr=13;;
        fla*) name=fla; chr=102;;
        idad*) name=idad; chr=109;;
+       ipsd*) name=ipsd; chr=176;;
        md*) name=md; chr=95;;
        mlxd*) name=mlxd; chr=131;;
        pst*) name=pst; chr=168;;
@@ -653,7 +657,7 @@ aacd*|ad*|ar*|afd*|amrd*|da*|fla*|idad*|md*|mlxd*|pst*|twed*|vn*|wd*|wfd*)
        wfd*) name=wfd; chr=87;;
        esac
        case $i in
-       aacd*|amrd*|idad*|mlxd*|twed*)
+       aacd*|amrd*|idad*|ipsd*|mlxd*|twed*)
                unit=`expr $i : '....\(.*\)'`
                ;;
        afd*|fla*|pst*|wfd*)
@@ -1814,6 +1818,11 @@ amr*)
        mknod amr$unit c 132 `unit2minor $unit`
        ;;
 
+ips*)
+       unit=`expr $i : 'ips\(.*\)'`
+       mknod mlx$unit c 175 `unit2minor $unit`
+       ;;
+
 ciss*)
        unit=`expr $i : 'ciss\(.*\)'`
        mknod ciss$unit c 166 `unit2minor $unit`
diff --git a/share/man/man4/ips.4 b/share/man/man4/ips.4
new file mode 100644 (file)
index 0000000..a776526
--- /dev/null
@@ -0,0 +1,183 @@
+.\"
+.\" Copyright (c) 2003 Tom Rhodes
+.\" 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: src/share/man/man4/ips.4,v 1.2 2003/11/12 02:26:47 hmp Exp $
+.\" $DragonFly: src/share/man/man4/ips.4,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+.\"
+.Dd November 11, 2003
+.Dt IPS 4
+.Os
+.Sh NAME
+.Nm ips
+.Nd IBM/Adaptec ServeRAID controller driver
+.Sh SYNOPSIS
+.Cd device scbus
+.Cd device ips
+.Sh DESCRIPTION
+The
+.Nm
+driver claims to support the
+.Tn IBM
+(now Adaptec) ServeRAID series
+.Tn SCSI
+controller cards.
+Some supported devices include:
+.Pp
+.Bl -item -compact
+.It
+IBM ServeRAID 3H
+.It
+ServeRAID 4L/4M/4H
+.It
+ServeRAID Series 5
+.It
+ServeRAID 6i/6M
+.El
+.Pp
+These cards come with a built in configuration utility stored in
+the firmware known as the
+.Tn ISPR .
+This utility is accessed with the
+.Em CTRL + I
+key combination during the initial card
+.Tn POST .
+.Pp
+It is highly recommended that this utility be used to configure the card
+before attempting to diagnose the below error messages.
+.Pp
+In some cases, the
+.Nm
+driver can have difficulties attaching during
+the system initialization period.
+To avoid these difficulties, set the
+.Dl hw.ips.0.disable
+tunable to
+.Dq 1 .
+This
+.Tn MIB
+prevents the driver from attaching.
+.Sh DIAGNOSTICS
+Several error codes may be shown when the card initializes the
+.Tn IBM
+.Tn ISPR
+utility and are independent of
+.Fx .
+.Pp
+.Bl -diag
+.It ips%d: failed to get adapter configuration data from device
+.It ips%d: failed to get drive configuration data from device
+.Pp
+Unable to obtain adapter or drive configuration.
+.It ips%d iobuf error
+.Pp
+A buffer input/output error has occurred.
+.Bq Er ENXIO
+.El
+.Ss General adapter errors:
+.Bl -diag
+.It Attaching bus failed
+.Pp
+This message is undocumented.
+.It WARNING: command timeout. Adapter is in toaster mode, resetting
+.Pp
+A command timeout has caused the adapter to be reset.
+.It AIEE! adapter reset failed, giving up and going home! Have a nice day
+.Pp
+An error occurred while attempting to reset the adapter.
+.It unable to get adapter configuration
+.It unable to get drive configuration
+.Pp
+There was an error when attempting to get configuration information.
+.It Adapter error during initialization.
+.It adapter initialization failed
+.Pp
+There was an error while attempting to initialize the adapter.
+.It adapter failed config check
+.It adapter clear failed
+.Pp
+There was an error while checking the adapter.
+.It device is disabled
+.Pp
+The adapter is disabled.
+.It resource allocation failed
+.It irq allocation failed
+.It irq setup failed
+.Pp
+The driver was unable to allocate resources for the device.
+.El
+.Ss Error messages due to DMA:
+.Bl -diag
+.It can't alloc command dma tag
+.It can't alloc SG dma tag
+.It can't alloc dma tag for statue queue
+.It dmamap failed
+.Pp
+Failure to map or allocate DMA resources.
+.El
+.Ss Cache, buffer, and command errors:
+.Bl -diag
+.It failed to initialize command buffers
+.It no mem for command slots!
+.Pp
+The
+.Nm
+driver will return
+.Bq Er ENOMEM
+in such cases.
+.It ERROR: unable to get a command! can't flush cache!
+.It ERROR: cache flush command failed!
+.It ERROR: unable to get a command! can't update nvram
+.It ERROR: nvram update command failed!
+.It ERROR: unable to get a command! can't sync cache!
+.It ERROR: cache sync command failed!
+.It ERROR: unable to get a command! can't sync cache!
+.It ERROR: etable command failed!
+.El
+.Sh COMPATIBILITY
+Unlike many of the other
+.Tn SCSI
+devices in
+.Fx ,
+the
+.Nm
+driver does not use the
+.Xr cam 4
+.Tn SCSI
+subsystem.
+.Sh SEE ALSO
+.Xr ch 4 ,
+.Xr da 4 ,
+.Xr sysctl 8
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An -nosplit
+.An David Jefferys
+and
+.An Scott Long Aq scottl@FreeBSD.org .
+.Pp
+This manual page was written by
+.An Tom Rhodes Aq trhodes@FreeBSD.org .
index 94b2c97..e7c901e 100644 (file)
@@ -1,5 +1,5 @@
 # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $
-# $DragonFly: src/sys/conf/files,v 1.39 2004/01/07 18:13:19 joerg Exp $
+# $DragonFly: src/sys/conf/files,v 1.40 2004/01/15 15:41:23 drhodus Exp $
 #
 # The long compile-with and dependency lines are required because of
 # limitations in config: backslash-newline doesn't work in strings, and
@@ -505,6 +505,11 @@ dev/raid/vinum/vinumrequest.c      optional vinum
 dev/raid/vinum/vinumrevive.c           optional vinum
 dev/raid/vinum/vinumstate.c            optional vinum
 dev/raid/vinum/vinumutil.c             optional vinum
+dev/raid/ips/ips.c                     optional ips
+dev/raid/ips/ips_commands.c            optional ips
+dev/raid/ips/ips_disk.c                        optional ips
+dev/raid/ips/ips_ioctl.c               optional ips
+dev/raid/ips/ips_pci.c                 optional ips
 dev/disk/vn/vn.c                       optional vn
 dev/netif/vx/if_vx.c                   optional vx
 dev/netif/vx/if_vx_eisa.c              optional vx eisa
index 033c622..d1a3ace 100644 (file)
@@ -1,6 +1,6 @@
-# $DragonFly: src/sys/dev/raid/Makefile,v 1.2 2003/10/01 18:20:33 dillon Exp $
+# $DragonFly: src/sys/dev/raid/Makefile,v 1.3 2004/01/15 15:41:23 drhodus Exp $
 #
 
-SUBDIR=aac amr asr ciss iir mlx mly pst twe vinum
+SUBDIR=aac amr asr ciss iir mlx mly pst twe vinum ips
 
 .include <bsd.subdir.mk>
diff --git a/sys/dev/raid/ips/Makefile b/sys/dev/raid/ips/Makefile
new file mode 100644 (file)
index 0000000..64a7014
--- /dev/null
@@ -0,0 +1,12 @@
+# $DragonFly: src/sys/dev/raid/ips/Makefile,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+
+.PATH: ${.CURDIR}/../../dev/raid/ips
+KMOD   = ips
+SRCS   = ips.c ips.h ips_commands.c ips_disk.c ips_disk.h ips_ioctl.c \
+       ips_ioctl.h ips_pci.c device_if.h bus_if.h use_ips.h pci_if.h
+CLEANFILES     = use_ips.h
+
+use_ips.h:
+       echo "#define NIPS 1" > use_ips.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/raid/ips/ips.c b/sys/dev/raid/ips/ips.c
new file mode 100644 (file)
index 0000000..bc4dffc
--- /dev/null
@@ -0,0 +1,830 @@
+/*-
+ * Written by: David Jeffery
+ * Copyright (c) 2002 Adaptec Inc.
+ * 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: src/sys/dev/ips/ips.c,v 1.6 2003/11/27 08:37:36 mbr
+ * $DragonFly: src/sys/dev/raid/ips/ips.c,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+ */
+
+#include <sys/cdefs.h>
+
+#include <dev/raid/ips/ips.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <machine/clock.h>
+
+static d_open_t ips_open;
+static d_close_t ips_close;
+static d_ioctl_t ips_ioctl;
+
+static struct cdevsw ips_cdevsw = {
+       .d_name         = "ips",
+       .d_maj          = IPS_CDEV_MAJOR,
+       .d_flags        = D_DISK,
+       .d_port         = NULL,
+       .d_autoq        = 0,
+       .old_open       = ips_open,
+       .old_close      = ips_close,
+       .old_ioctl      = ips_ioctl,
+};
+
+static const char *ips_adapter_name[] = {
+       "N/A",
+       "ServeRAID (copperhead)",
+       "ServeRAID II (copperhead refresh)",
+       "ServeRAID onboard (copperhead)",
+       "ServeRAID onboard (copperhead)",
+       "ServeRAID 3H (clarinet)",
+       "ServeRAID 3L (clarinet lite)",
+       "ServeRAID 4H (trombone)",
+       "ServeRAID 4M (morpheus)",
+       "ServeRAID 4L (morpheus lite)",
+       "ServeRAID 4Mx (neo)",
+       "ServeRAID 4Lx (neo lite)",
+       "ServeRAID 5i II (sarasota)",
+       "ServeRAID 5i (sarasota)",
+       "ServeRAID 6M (marco)",
+       "ServeRAID 6i (sebring)"
+};
+
+
+static int
+ips_open(dev_t dev, int flags, int fmt, d_thread_t *td)
+{
+       ips_softc_t *sc = dev->si_drv1;
+
+       sc->state |= IPS_DEV_OPEN;
+       return 0;
+}
+
+static int
+ips_close(dev_t dev, int flags, int fmt, d_thread_t *td)
+{
+       ips_softc_t *sc = dev->si_drv1;
+
+       sc->state &= ~IPS_DEV_OPEN;
+       return 0;
+}
+
+static int
+ips_ioctl(dev_t dev, u_long command, caddr_t addr, int32_t flags,
+    d_thread_t *td)
+{
+       ips_softc_t *sc;
+
+       sc = dev->si_drv1;
+       return ips_ioctl_request(sc, command, addr, flags);
+}
+
+static void
+ips_cmd_dmaload(void *cmdptr, bus_dma_segment_t *segments, int segnum,
+    int error)
+{
+       ips_command_t *command = cmdptr;
+
+       PRINTF(10, "ips: in ips_cmd_dmaload\n");
+       if (!error)
+               command->command_phys_addr = segments[0].ds_addr;
+
+}
+
+/* is locking needed? what locking guarentees are there on removal? */
+static __inline__ int
+ips_cmdqueue_free(ips_softc_t *sc)
+{
+       int i, error = -1;
+       intrmask_t mask;
+
+       mask = splbio();
+       if (sc->used_commands == 0) {
+               for (i = 0; i < sc->max_cmds; i++) {
+                       if (sc->commandarray[i].command_phys_addr == 0)
+                               continue;
+                       bus_dmamap_unload(sc->command_dmatag,
+                           sc->commandarray[i].command_dmamap);
+                       bus_dmamem_free(sc->command_dmatag,
+                           sc->commandarray[i].command_buffer,
+                           sc->commandarray[i].command_dmamap);
+               }
+               error = 0;
+               sc->state |= IPS_OFFLINE;
+       }
+       splx(mask);
+       return error;
+}
+
+/* places all ips command structs on the free command queue.  No locking as if someone else tries
+ * to access this during init, we have bigger problems */
+static __inline__ int
+ips_cmdqueue_init(ips_softc_t *sc)
+{
+       int i;
+       ips_command_t *command;
+
+       SLIST_INIT(&sc->free_cmd_list);
+       STAILQ_INIT(&sc->cmd_wait_list);
+       for (i = 0; i < sc->max_cmds; i++) {
+               sc->commandarray[i].id = i;
+               sc->commandarray[i].sc = sc;
+               SLIST_INSERT_HEAD(&sc->free_cmd_list, &sc->commandarray[i],
+                   next);
+       }
+       for (i = 0; i < sc->max_cmds; i++) {
+               command = &sc->commandarray[i];
+               if (bus_dmamem_alloc(sc->command_dmatag,
+                   &command->command_buffer, BUS_DMA_NOWAIT,
+                   &command->command_dmamap))
+                       goto error;
+               bus_dmamap_load(sc->command_dmatag, command->command_dmamap,
+                   command->command_buffer, IPS_COMMAND_LEN, ips_cmd_dmaload,
+                   command, BUS_DMA_NOWAIT);
+               if (command->command_phys_addr == 0) {
+                       bus_dmamem_free(sc->command_dmatag,
+                           command->command_buffer, command->command_dmamap);
+                       goto error;
+               }
+       }
+       sc->state &= ~IPS_OFFLINE;
+       return 0;
+error:
+       ips_cmdqueue_free(sc);
+       return ENOMEM;
+}
+
+static int
+ips_add_waiting_command(ips_softc_t *sc, int (*callback)(ips_command_t *),
+    void *data, unsigned long flags)
+{
+       intrmask_t mask;
+       ips_command_t *command;
+       ips_wait_list_t *waiter;
+       unsigned long memflags = 0;
+
+       if (IPS_NOWAIT_FLAG & flags)
+               memflags = M_NOWAIT;
+       waiter = malloc(sizeof(ips_wait_list_t), M_DEVBUF, memflags);
+       if (waiter == NULL)
+               return ENOMEM;
+       mask = splbio();
+       if (sc->state & IPS_OFFLINE) {
+               splx(mask);
+               return EIO;
+       }
+       command = SLIST_FIRST(&sc->free_cmd_list);
+       if (command && !(sc->state & IPS_TIMEOUT)) {
+               SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
+               sc->used_commands++;
+               splx(mask);
+               clear_ips_command(command);
+               bzero(command->command_buffer, IPS_COMMAND_LEN);
+               free(waiter, M_DEVBUF);
+               command->arg = data;
+               return callback(command);
+       }
+       DEVICE_PRINTF(1, sc->dev, "adding command to the wait queue\n");
+       waiter->callback = callback;
+       waiter->data = data;
+       STAILQ_INSERT_TAIL(&sc->cmd_wait_list, waiter, next);
+       splx(mask);
+       return 0;
+}
+
+static void
+ips_run_waiting_command(ips_softc_t *sc)
+{
+       ips_wait_list_t *waiter;
+       ips_command_t   *command;
+       int (*callback)(ips_command_t*);
+       intrmask_t mask;
+
+       mask = splbio();
+       waiter = STAILQ_FIRST(&sc->cmd_wait_list);
+       command = SLIST_FIRST(&sc->free_cmd_list);
+       if (waiter == NULL || command == NULL) {
+               splx(mask);
+               return;
+       }
+       DEVICE_PRINTF(1, sc->dev, "removing command from wait queue\n");
+       SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
+       STAILQ_REMOVE_HEAD(&sc->cmd_wait_list, next);
+       sc->used_commands++;
+       splx(mask);
+       clear_ips_command(command);
+       bzero(command->command_buffer, IPS_COMMAND_LEN);
+       command->arg = waiter->data;
+       callback = waiter->callback;
+       free(waiter, M_DEVBUF);
+       callback(command);
+       return;
+}
+
+/*
+ * returns a free command struct if one is available.
+ * It also blanks out anything that may be a wild pointer/value.
+ * Also, command buffers are not freed.  They are
+ * small so they are saved and kept dmamapped and loaded.
+ */
+int
+ips_get_free_cmd(ips_softc_t *sc, int (*callback)(ips_command_t *), void *data,
+    unsigned long flags)
+{
+       ips_command_t *command;
+       intrmask_t mask;
+
+       mask = splbio();
+       if (sc->state & IPS_OFFLINE) {
+               splx(mask);
+               return EIO;
+       }
+       command = SLIST_FIRST(&sc->free_cmd_list);
+       if (!command || (sc->state & IPS_TIMEOUT)) {
+               splx(mask);
+               if (flags & IPS_NOWAIT_FLAG)
+                       return EAGAIN;
+               return ips_add_waiting_command(sc, callback, data, flags);
+       }
+       SLIST_REMOVE_HEAD(&sc->free_cmd_list, next);
+       sc->used_commands++;
+       splx(mask);
+       clear_ips_command(command);
+       bzero(command->command_buffer, IPS_COMMAND_LEN);
+       command->arg = data;
+       return callback(command);
+}
+
+/* adds a command back to the free command queue */
+void
+ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command)
+{
+       intrmask_t mask;
+
+       mask = splbio();
+       SLIST_INSERT_HEAD(&sc->free_cmd_list, command, next);
+       sc->used_commands--;
+       splx(mask);
+       if (!(sc->state & IPS_TIMEOUT))
+               ips_run_waiting_command(sc);
+}
+static const char *
+ips_diskdev_statename(u_int8_t state)
+{
+       static char statebuf[20];
+
+       switch(state) {
+       case IPS_LD_OFFLINE:
+               return("OFFLINE");
+               break;
+       case IPS_LD_OKAY:
+               return("OK");
+               break;
+       case IPS_LD_DEGRADED:
+               return("DEGRADED");
+               break;
+       case IPS_LD_FREE:
+               return("FREE");
+               break;
+       case IPS_LD_SYS:
+               return("SYS");
+               break;
+       case IPS_LD_CRS:
+               return("CRS");
+               break;
+       }
+       sprintf(statebuf, "UNKNOWN(0x%02x)", state);
+       return (statebuf);
+}
+
+static int
+ips_diskdev_init(ips_softc_t *sc)
+{
+       int i;
+
+       for (i = 0; i < IPS_MAX_NUM_DRIVES; i++) {
+               if (sc->drives[i].state == IPS_LD_FREE)
+                       continue;
+               device_printf(sc->dev,
+                   "Logical Drive %d: RAID%d sectors: %u, state %s\n", i,
+                   sc->drives[i].raid_lvl, sc->drives[i].sector_count,
+                   ips_diskdev_statename(sc->drives[i].state));
+               if (sc->drives[i].state == IPS_LD_OKAY ||
+                   sc->drives[i].state == IPS_LD_DEGRADED) {
+                       sc->diskdev[i] = device_add_child(sc->dev, NULL, -1);
+                       device_set_ivars(sc->diskdev[i], (void *)(uintptr_t)i);
+               }
+       }
+       if (bus_generic_attach(sc->dev))
+               device_printf(sc->dev, "Attaching bus failed\n");
+       return 0;
+}
+
+static int
+ips_diskdev_free(ips_softc_t *sc)
+{
+       int i;
+       int error = 0;
+
+       for (i = 0; i < IPS_MAX_NUM_DRIVES; i++) {
+               if (sc->diskdev[i] != NULL) {
+                       error = device_delete_child(sc->dev, sc->diskdev[i]);
+                       if (error)
+                               return error;
+               }
+       }
+       bus_generic_detach(sc->dev);
+       return 0;
+}
+
+/*
+ * ips_timeout is periodically called to make sure no commands sent
+ * to the card have become stuck.  If it finds a stuck command, it
+ * sets a flag so the driver won't start any more commands and then
+ * is periodically called to see if all outstanding commands have
+ * either finished or timed out.  Once timed out, an attempt to
+ * reinitialize the card is made.  If that fails, the driver gives
+ * up and declares the card dead.
+ */
+static void
+ips_timeout(void *arg)
+{
+       ips_command_t *command;
+       ips_softc_t *sc = arg;
+       intrmask_t mask;
+       int i, state = 0;
+
+       command = &sc->commandarray[0];
+       mask = splbio();
+       for (i = 0; i < sc->max_cmds; i++) {
+               if (!command[i].timeout)
+                       continue;
+               command[i].timeout--;
+               if (command[i].timeout == 0) {
+                       if (!(sc->state & IPS_TIMEOUT)) {
+                               sc->state |= IPS_TIMEOUT;
+                               device_printf(sc->dev, "WARNING: command timeout. Adapter is in toaster mode, resetting to known state\n");
+                       }
+                       command[i].status.value = IPS_ERROR_STATUS;
+                       command[i].callback(&command[i]);
+                       /* hmm, this should be enough cleanup */
+               } else
+                       state = 1;
+       }
+       if (!state && (sc->state & IPS_TIMEOUT)) {
+               if (sc->ips_adapter_reinit(sc, 1)) {
+                       device_printf(sc->dev, "AIEE! adapter reset failed, "
+                           "giving up and going home! Have a nice day.\n");
+                       sc->state |= IPS_OFFLINE;
+                       sc->state &= ~IPS_TIMEOUT;
+                       /*
+                        * Grr, I hate this solution. I run waiting commands
+                        * one at a time and error them out just before they
+                        * would go to the card. This sucks.
+                        */
+               } else
+                       sc->state &= ~IPS_TIMEOUT;
+               ips_run_waiting_command(sc);
+       }
+       if (sc->state != IPS_OFFLINE)
+               sc->timer = timeout(ips_timeout, sc, 10 * hz);
+       splx(mask);
+}
+
+/* check card and initialize it */
+int
+ips_adapter_init(ips_softc_t *sc)
+{
+       int i;
+
+       DEVICE_PRINTF(1, sc->dev, "initializing\n");
+       if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
+                               /* alignemnt */ 1,
+                               /* boundary  */ 0,
+                               /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
+                               /* highaddr  */ BUS_SPACE_MAXADDR,
+                               /* filter    */ NULL,
+                               /* filterarg */ NULL,
+                               /* maxsize   */ IPS_COMMAND_LEN +
+                                                   IPS_MAX_SG_LEN,
+                               /* numsegs   */ 1,
+                               /* maxsegsize*/ IPS_COMMAND_LEN +
+                                                   IPS_MAX_SG_LEN,
+                               /* flags     */ 0,
+                               &sc->command_dmatag) != 0) {
+               device_printf(sc->dev, "can't alloc command dma tag\n");
+               goto error;
+       }
+       if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
+                               /* alignemnt */ 1,
+                               /* boundary  */ 0,
+                               /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
+                               /* highaddr  */ BUS_SPACE_MAXADDR,
+                               /* filter    */ NULL,
+                               /* filterarg */ NULL,
+                               /* maxsize   */ IPS_MAX_IOBUF_SIZE,
+                               /* numsegs   */ IPS_MAX_SG_ELEMENTS,
+                               /* maxsegsize*/ IPS_MAX_IOBUF_SIZE,
+                               /* flags     */ 0,
+                               &sc->sg_dmatag) != 0) {
+               device_printf(sc->dev, "can't alloc SG dma tag\n");
+               goto error;
+       }
+       /*
+        * create one command buffer until we know how many commands this card
+        * can handle
+        */
+       sc->max_cmds = 1;
+       ips_cmdqueue_init(sc);
+       callout_handle_init(&sc->timer);
+       if (sc->ips_adapter_reinit(sc, 0))
+               goto error;
+       IPS_LOCK_INIT(sc);
+       /* initialize ffdc values */
+       microtime(&sc->ffdc_resettime);
+       sc->ffdc_resetcount = 1;
+       if ((i = ips_ffdc_reset(sc)) != 0) {
+               device_printf(sc->dev,
+                   "failed to send ffdc reset to device (%d)\n", i);
+               goto error;
+       }
+       if ((i = ips_get_adapter_info(sc)) != 0) {
+               device_printf(sc->dev, "failed to get adapter configuration "
+                   "data from device (%d)\n", i);
+               goto error;
+       }
+       /* no error check as failure doesn't matter */
+       ips_update_nvram(sc);
+       if (sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T) {
+               device_printf(sc->dev, "adapter type: %s\n",
+                   ips_adapter_name[sc->adapter_type]);
+       }
+       if ((i = ips_get_drive_info(sc)) != 0) {
+               device_printf(sc->dev, "failed to get drive "
+                   "configuration data from device (%d)\n", i);
+               goto error;
+       }
+       ips_cmdqueue_free(sc);
+       if (sc->adapter_info.max_concurrent_cmds)
+               sc->max_cmds = min(128, sc->adapter_info.max_concurrent_cmds);
+       else
+               sc->max_cmds = 32;
+       if (ips_cmdqueue_init(sc)) {
+               device_printf(sc->dev,
+                   "failed to initialize command buffers\n");
+               goto error;
+       }
+       sc->device_file = make_dev(&ips_cdevsw, device_get_unit(sc->dev),
+                                  UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR,
+                                  "ips%d", device_get_unit(sc->dev));
+       sc->device_file->si_drv1 = sc;
+       ips_diskdev_init(sc);
+       sc->timer = timeout(ips_timeout, sc, 10*hz);
+       return 0;
+error:
+       ips_adapter_free(sc);
+       return ENXIO;
+}
+
+/*
+ * see if we should reinitialize the card and wait for it to timeout
+ * or complete initialization
+ */
+int
+ips_morpheus_reinit(ips_softc_t *sc, int force)
+{
+       u_int32_t tmp;
+       int i;
+
+       tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
+       if (!force && (ips_read_4(sc, MORPHEUS_REG_OMR0) >= IPS_POST1_OK) &&
+           (ips_read_4(sc, MORPHEUS_REG_OMR1) != 0xdeadbeef) && !tmp) {
+               ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
+               return 0;
+       }
+       ips_write_4(sc, MORPHEUS_REG_OIMR, 0xff);
+       ips_read_4(sc, MORPHEUS_REG_OIMR);
+       device_printf(sc->dev,
+           "resetting adapter, this may take up to 5 minutes\n");
+       ips_write_4(sc, MORPHEUS_REG_IDR, 0x80000000);
+       DELAY(5000000);
+       pci_read_config(sc->dev, 0, 4);
+       tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
+       for (i = 0; i < 45 && !(tmp & MORPHEUS_BIT_POST1); i++) {
+               DELAY(1000000);
+               DEVICE_PRINTF(2, sc->dev, "post1: %d\n", i);
+               tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
+       }
+       if (tmp & MORPHEUS_BIT_POST1)
+               ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST1);
+
+       if (i == 45 || ips_read_4(sc, MORPHEUS_REG_OMR0) < IPS_POST1_OK) {
+               device_printf(sc->dev,
+                   "Adapter error during initialization.\n");
+               return 1;
+       }
+       for (i = 0; i < 240 && !(tmp & MORPHEUS_BIT_POST2); i++) {
+               DELAY(1000000);
+               DEVICE_PRINTF(2, sc->dev, "post2: %d\n", i);
+               tmp = ips_read_4(sc, MORPHEUS_REG_OISR);
+       }
+       if (tmp & MORPHEUS_BIT_POST2)
+               ips_write_4(sc, MORPHEUS_REG_OISR, MORPHEUS_BIT_POST2);
+
+       if (i == 240 || !ips_read_4(sc, MORPHEUS_REG_OMR1)) {
+               device_printf(sc->dev, "adapter failed config check\n");
+               return 1;
+       }
+       ips_write_4(sc, MORPHEUS_REG_OIMR, 0);
+       if (force && ips_clear_adapter(sc)) {
+               device_printf(sc->dev, "adapter clear failed\n");
+               return 1;
+       }
+       return 0;
+}
+
+/* clean up so we can unload the driver. */
+int
+ips_adapter_free(ips_softc_t *sc)
+{
+       int error = 0;
+       intrmask_t mask;
+       if (sc->state & IPS_DEV_OPEN)
+               return EBUSY;
+       if ((error = ips_diskdev_free(sc)))
+               return error;
+       if (ips_cmdqueue_free(sc)) {
+               device_printf(sc->dev,
+                   "trying to exit when command queue is not empty!\n");
+               return EBUSY;
+       }
+       DEVICE_PRINTF(1, sc->dev, "free\n");
+       mask = splbio();
+       untimeout(ips_timeout, sc, sc->timer);
+       splx(mask);
+       IPS_LOCK_FREE(sc);
+       if (sc->sg_dmatag)
+               bus_dma_tag_destroy(sc->sg_dmatag);
+       if (sc->command_dmatag)
+               bus_dma_tag_destroy(sc->command_dmatag);
+       if (sc->device_file)
+               destroy_dev(sc->device_file);
+       return 0;
+}
+
+void
+ips_morpheus_intr(void *void_sc)
+{
+       ips_softc_t *sc = (ips_softc_t *)void_sc;
+       u_int32_t oisr, iisr;
+       ips_cmd_status_t status;
+       int cmdnumber;
+
+       iisr =ips_read_4(sc, MORPHEUS_REG_IISR);
+       oisr =ips_read_4(sc, MORPHEUS_REG_OISR);
+       PRINTF(9, "interrupt registers in:%x out:%x\n", iisr, oisr);
+       if (!(oisr & MORPHEUS_BIT_CMD_IRQ)) {
+               DEVICE_PRINTF(2, sc->dev, "got a non-command irq\n");
+               return;
+       }
+       while ((status.value = ips_read_4(sc, MORPHEUS_REG_OQPR))
+              != 0xffffffff) {
+               cmdnumber = status.fields.command_id;
+               sc->commandarray[cmdnumber].status.value = status.value;
+               sc->commandarray[cmdnumber].timeout = 0;
+               sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
+               DEVICE_PRINTF(9, sc->dev, "got command %d\n", cmdnumber);
+       }
+       return;
+}
+
+void
+ips_issue_morpheus_cmd(ips_command_t *command)
+{
+       intrmask_t mask;
+
+       mask = splbio();
+       /* hmmm, is there a cleaner way to do this? */
+       if (command->sc->state & IPS_OFFLINE) {
+               splx(mask);
+               command->status.value = IPS_ERROR_STATUS;
+               command->callback(command);
+               return;
+       }
+       command->timeout = 10;
+       ips_write_4(command->sc, MORPHEUS_REG_IQPR, command->command_phys_addr);
+       splx(mask);
+}
+
+static void
+ips_copperhead_queue_callback(void *queueptr, bus_dma_segment_t *segments,
+                             int segnum, int error)
+{
+       ips_copper_queue_t *queue = queueptr;
+
+       if (error)
+               return;
+       queue->base_phys_addr = segments[0].ds_addr;
+}
+
+static int
+ips_copperhead_queue_init(ips_softc_t *sc)
+{
+       bus_dma_tag_t dmatag;
+       bus_dmamap_t dmamap;
+       int error;
+
+       if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
+                               /* alignemnt */ 1,
+                               /* boundary  */ 0,
+                               /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
+                               /* highaddr  */ BUS_SPACE_MAXADDR,
+                               /* filter    */ NULL,
+                               /* filterarg */ NULL,
+                               /* maxsize   */ sizeof(ips_copper_queue_t),
+                               /* numsegs   */ 1,
+                               /* maxsegsize*/ sizeof(ips_copper_queue_t),
+                               /* flags     */ 0,
+                               &dmatag) != 0) {
+               device_printf(sc->dev, "can't alloc dma tag for statue queue\n");
+               error = ENOMEM;
+               goto exit;
+       }
+       if (bus_dmamem_alloc(dmatag, (void *)&(sc->copper_queue),
+           BUS_DMA_NOWAIT, &dmamap)) {
+               error = ENOMEM;
+               goto exit;
+       }
+       bzero(sc->copper_queue, sizeof(ips_copper_queue_t));
+       sc->copper_queue->dmatag = dmatag;
+       sc->copper_queue->dmamap = dmamap;
+       sc->copper_queue->nextstatus = 1;
+       bus_dmamap_load(dmatag, dmamap, &(sc->copper_queue->status[0]),
+           IPS_MAX_CMD_NUM * 4, ips_copperhead_queue_callback,
+           sc->copper_queue, BUS_DMA_NOWAIT);
+       if (sc->copper_queue->base_phys_addr == 0) {
+               error = ENOMEM;
+               goto exit;
+       }
+       ips_write_4(sc, COPPER_REG_SQSR, sc->copper_queue->base_phys_addr);
+       ips_write_4(sc, COPPER_REG_SQER, sc->copper_queue->base_phys_addr +
+           IPS_MAX_CMD_NUM * 4);
+       ips_write_4(sc, COPPER_REG_SQHR, sc->copper_queue->base_phys_addr + 4);
+       ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr);
+       return 0;
+exit:
+       bus_dmamem_free(dmatag, sc->copper_queue, dmamap);
+       bus_dma_tag_destroy(dmatag);
+       return error;
+}
+
+/*
+ * see if we should reinitialize the card and wait for it to timeout or
+ * complete initialization FIXME
+ */
+int
+ips_copperhead_reinit(ips_softc_t *sc, int force)
+{
+       u_int32_t postcode = 0, configstatus = 0;
+       int       i, j;
+
+       ips_write_1(sc, COPPER_REG_SCPR, 0x80);
+       ips_write_1(sc, COPPER_REG_SCPR, 0);
+       device_printf(sc->dev,
+           "reinitializing adapter, this could take several minutes.\n");
+       for (j = 0; j < 2; j++) {
+               postcode <<= 8;
+               for (i = 0; i < 45; i++) {
+                       if (ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT) {
+                               postcode |= ips_read_1(sc, COPPER_REG_ISPR);
+                               ips_write_1(sc, COPPER_REG_HISR,
+                                   COPPER_GHI_BIT);
+                               break;
+                       } else
+                               DELAY(1000000);
+               }
+               if (i == 45)
+                       return 1;
+       }
+       for (j = 0; j < 2; j++) {
+               configstatus <<= 8;
+               for (i = 0; i < 240; i++) {
+                       if (ips_read_1(sc, COPPER_REG_HISR) & COPPER_GHI_BIT) {
+                               configstatus |= ips_read_1(sc, COPPER_REG_ISPR);
+                               ips_write_1(sc, COPPER_REG_HISR,
+                                   COPPER_GHI_BIT);
+                               break;
+                       } else
+                               DELAY(1000000);
+               }
+               if (i == 240)
+                       return 1;
+       }
+       for (i = 0; i < 240; i++) {
+               if (!(ips_read_1(sc, COPPER_REG_CBSP) & COPPER_OP_BIT))
+                       break;
+               else
+                       DELAY(1000000);
+       }
+       if (i == 240)
+               return 1;
+       ips_write_2(sc, COPPER_REG_CCCR, 0x1000 | COPPER_ILE_BIT);
+       ips_write_1(sc, COPPER_REG_SCPR, COPPER_EBM_BIT);
+       ips_copperhead_queue_init(sc);
+       ips_write_1(sc, COPPER_REG_HISR, COPPER_GHI_BIT);
+       i = ips_read_1(sc, COPPER_REG_SCPR);
+       ips_write_1(sc, COPPER_REG_HISR, COPPER_EI_BIT);
+       if (configstatus == 0) {
+               device_printf(sc->dev, "adapter initialization failed\n");
+               return 1;
+       }
+       if (force && ips_clear_adapter(sc)) {
+               device_printf(sc->dev, "adapter clear failed\n");
+               return 1;
+       }
+       return 0;
+}
+
+static u_int32_t
+ips_copperhead_cmd_status(ips_softc_t *sc)
+{
+       intrmask_t mask;
+       u_int32_t value;
+       int statnum;
+
+       statnum = sc->copper_queue->nextstatus++;
+       if (sc->copper_queue->nextstatus == IPS_MAX_CMD_NUM)
+               sc->copper_queue->nextstatus = 0;
+       mask = splbio();
+       value = sc->copper_queue->status[statnum];
+       ips_write_4(sc, COPPER_REG_SQTR, sc->copper_queue->base_phys_addr +
+           4 * statnum);
+       splx(mask);
+       return value;
+}
+
+void
+ips_copperhead_intr(void *void_sc)
+{
+       ips_softc_t *sc = (ips_softc_t *)void_sc;
+       ips_cmd_status_t status;
+       int cmdnumber;
+
+       while (ips_read_1(sc, COPPER_REG_HISR) & COPPER_SCE_BIT) {
+               status.value = ips_copperhead_cmd_status(sc);
+               cmdnumber = status.fields.command_id;
+               sc->commandarray[cmdnumber].status.value = status.value;
+               sc->commandarray[cmdnumber].timeout = 0;
+               sc->commandarray[cmdnumber].callback(&(sc->commandarray[cmdnumber]));
+               PRINTF(9, "ips: got command %d\n", cmdnumber);
+       }
+       return;
+}
+
+void
+ips_issue_copperhead_cmd(ips_command_t *command)
+{
+       intrmask_t mask;
+       int i;
+
+       mask = splbio();
+       /* hmmm, is there a cleaner way to do this? */
+       if (command->sc->state & IPS_OFFLINE) {
+               splx(mask);
+               command->status.value = IPS_ERROR_STATUS;
+               command->callback(command);
+               return;
+       }
+       command->timeout = 10;
+       for (i = 0; ips_read_4(command->sc, COPPER_REG_CCCR) & COPPER_SEM_BIT;
+           i++) {
+               if (i == 20) {
+                       printf("sem bit still set, can't send a command\n");
+                       splx(mask);
+                       return;
+               }
+               DELAY(500);     /* need to do a delay here */
+       }
+       ips_write_4(command->sc, COPPER_REG_CCSAR, command->command_phys_addr);
+       ips_write_2(command->sc, COPPER_REG_CCCR, COPPER_CMD_START);
+       splx(mask);
+}
diff --git a/sys/dev/raid/ips/ips.h b/sys/dev/raid/ips/ips.h
new file mode 100644 (file)
index 0000000..f730251
--- /dev/null
@@ -0,0 +1,515 @@
+/*-
+ * Copyright (c) 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Written by: David Jeffery
+ *
+ * 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: src/sys/dev/ips/ips.h,v 1.5 2003/11/27 08:37:36 mbr Exp $
+ * $DragonFly: src/sys/dev/raid/ips/ips.h,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+ */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/buf.h>
+#include <sys/malloc.h>
+#include <sys/time.h>
+
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+
+/*
+ *   IPS CONSTANTS
+ */
+#define IPS_VENDOR_ID                  0x1014
+#define IPS_MORPHEUS_DEVICE_ID         0x01BD
+#define IPS_COPPERHEAD_DEVICE_ID       0x002E
+#define IPS_CSL                                0xff
+#define IPS_POCL                       0x30
+
+/* amounts of memory to allocate for certain commands */
+#define IPS_ADAPTER_INFO_LEN           (sizeof(ips_adapter_info_t))
+#define IPS_DRIVE_INFO_LEN             (sizeof(ips_drive_info_t))
+#define IPS_COMMAND_LEN                        24
+#define IPS_MAX_SG_LEN                 (sizeof(ips_sg_element_t) * IPS_MAX_SG_ELEMENTS)
+#define IPS_NVRAM_PAGE_SIZE            128
+/* various flags */
+#define IPS_NOWAIT_FLAG                        1
+
+/* states for the card to be in */
+#define IPS_DEV_OPEN                   0x01
+#define IPS_TIMEOUT                    0x02 /* command time out, need reset */
+#define IPS_OFFLINE                    0x04 /* can't reset card/card failure */
+
+/* max number of commands set to something low for now */
+#define IPS_MAX_CMD_NUM                        128
+#define IPS_MAX_NUM_DRIVES             8
+#define IPS_MAX_SG_ELEMENTS            32
+#define IPS_MAX_IOBUF_SIZE             (64 * 1024)
+#define IPS_BLKSIZE                    512
+
+/* logical drive states */
+
+#define IPS_LD_OFFLINE                 0x02
+#define IPS_LD_OKAY                    0x03
+#define IPS_LD_DEGRADED                        0x04
+#define IPS_LD_FREE                    0x00
+#define IPS_LD_SYS                     0x06
+#define IPS_LD_CRS                     0x24
+
+/* register offsets */
+#define MORPHEUS_REG_OMR0              0x0018 /* Outbound Msg. Reg. 0 */
+#define MORPHEUS_REG_OMR1              0x001C /* Outbound Msg. Reg. 1 */
+#define MORPHEUS_REG_IDR               0x0020 /* Inbound Doorbell Reg. */
+#define MORPHEUS_REG_IISR              0x0024 /* Inbound IRQ Status Reg. */
+#define MORPHEUS_REG_IIMR              0x0028 /* Inbound IRQ Mask Reg. */
+#define MORPHEUS_REG_OISR              0x0030 /* Outbound IRQ Status Reg. */
+#define MORPHEUS_REG_OIMR              0x0034 /* Outbound IRQ Status Reg. */
+#define MORPHEUS_REG_IQPR              0x0040 /* Inbound Queue Port Reg. */
+#define MORPHEUS_REG_OQPR              0x0044 /* Outbound Queue Port Reg. */
+
+#define COPPER_REG_SCPR                        0x05    /* Subsystem Ctrl. Port Reg. */
+#define COPPER_REG_ISPR                        0x06    /* IRQ Status Port Reg. */
+#define COPPER_REG_CBSP                        0x07    /* ? Reg. */
+#define COPPER_REG_HISR                        0x08    /* Host IRQ Status Reg.    */
+#define COPPER_REG_CCSAR               0x10    /* Cmd. Channel Sys Addr Reg.*/
+#define COPPER_REG_CCCR                        0x14    /* Cmd. Channel Ctrl. Reg. */
+#define COPPER_REG_SQHR                        0x20    /* Status Queue Head Reg.  */
+#define COPPER_REG_SQTR                        0x24    /* Status Queue Tail Reg.  */
+#define COPPER_REG_SQER                        0x28    /* Status Queue End Reg.   */
+#define COPPER_REG_SQSR                        0x2C    /* Status Queue Start Reg. */
+
+/* bit definitions */
+#define MORPHEUS_BIT_POST1             0x01
+#define MORPHEUS_BIT_POST2             0x02
+#define MORPHEUS_BIT_CMD_IRQ           0x08
+
+#define COPPER_CMD_START               0x101A
+#define COPPER_SEM_BIT                 0x08
+#define COPPER_EI_BIT                  0x80
+#define COPPER_EBM_BIT                 0x02
+#define COPPER_RESET_BIT               0x80
+#define COPPER_GHI_BIT                 0x04
+#define COPPER_SCE_BIT                 0x01
+#define COPPER_OP_BIT                  0x01
+#define COPPER_ILE_BIT                 0x10
+
+/* status defines */
+#define IPS_POST1_OK                   0x8000
+#define IPS_POST2_OK                   0x000f
+
+/* command op codes */
+#define IPS_READ_CMD                   0x02
+#define IPS_WRITE_CMD                  0x03
+#define IPS_ADAPTER_INFO_CMD           0x05
+#define IPS_CACHE_FLUSH_CMD            0x0A
+#define IPS_REBUILD_STATUS_CMD         0x0C
+#define IPS_ERROR_TABLE_CMD            0x17
+#define IPS_DRIVE_INFO_CMD             0x19
+#define IPS_SUBSYS_PARAM_CMD           0x40
+#define IPS_CONFIG_SYNC_CMD            0x58
+#define IPS_SG_READ_CMD                        0x82
+#define IPS_SG_WRITE_CMD               0x83
+#define IPS_RW_NVRAM_CMD               0xBC
+#define IPS_FFDC_CMD                   0xD7
+
+/* error information returned by the adapter */
+#define IPS_MIN_ERROR                  0x02
+#define IPS_ERROR_STATUS               0x13000200 /* ahh, magic numbers */
+
+#define IPS_OS_FREEBSD                 8
+#define IPS_VERSION_MAJOR              "0.90"
+#define IPS_VERSION_MINOR              ".10"
+
+/* Adapter Types */
+#define IPS_ADAPTER_COPPERHEAD         0x01
+#define IPS_ADAPTER_COPPERHEAD2                0x02
+#define IPS_ADAPTER_COPPERHEADOB1      0x03
+#define IPS_ADAPTER_COPPERHEADOB2      0x04
+#define IPS_ADAPTER_CLARINET           0x05
+#define IPS_ADAPTER_CLARINETLITE       0x06
+#define IPS_ADAPTER_TROMBONE           0x07
+#define IPS_ADAPTER_MORPHEUS           0x08
+#define IPS_ADAPTER_MORPHEUSLITE       0x09
+#define IPS_ADAPTER_NEO                        0x0A
+#define IPS_ADAPTER_NEOLITE            0x0B
+#define IPS_ADAPTER_SARASOTA2          0x0C
+#define IPS_ADAPTER_SARASOTA1          0x0D
+#define IPS_ADAPTER_MARCO              0x0E
+#define IPS_ADAPTER_SEBRING            0x0F
+#define IPS_ADAPTER_MAX_T              IPS_ADAPTER_SEBRING
+
+/* values for ffdc_settime (from gmtime) */
+#define IPS_SECSPERMIN      60
+#define IPS_MINSPERHOUR     60
+#define IPS_HOURSPERDAY     24
+#define IPS_DAYSPERWEEK     7
+#define IPS_DAYSPERNYEAR    365
+#define IPS_DAYSPERLYEAR    366
+#define IPS_SECSPERHOUR     (IPS_SECSPERMIN * IPS_MINSPERHOUR)
+#define IPS_SECSPERDAY      ((long) IPS_SECSPERHOUR * IPS_HOURSPERDAY)
+#define IPS_MONSPERYEAR     12
+#define IPS_EPOCH_YEAR      1970
+#define IPS_LEAPS_THRU_END_OF(y)    ((y) / 4 - (y) / 100 + (y) / 400)
+#define ips_isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+
+/*
+ * for compatibility
+ */
+/* struct buf to struct bio changes */
+#define BIO_ERROR      B_ERROR
+#define BIO_READ       B_READ
+#define bio            buf
+#define bio_error      b_error
+#define bio_flags      b_flags
+#define bio_driver1    b_driver1
+#define bio_pblkno     b_pblkno
+#define bio_data       b_data
+#define bio_bcount     b_bcount
+#define bio_dev                b_dev
+#define bio_resid      b_resid
+
+/* geom */
+#define bio_disk       bio_dev
+#define d_drv1         si_drv1
+#define d_maxsize      si_iosize_max
+
+struct mtx {
+       volatile int    locked;
+       intrmask_t      spl;
+};
+
+#define IPS_LOCK_INIT(sc)      (sc)->cmd_mtx.locked = 0
+#define IPS_LOCK(sc) do {                      \
+       int     s = splbio();                   \
+       if ((sc)->cmd_mtx.locked++ == 0)        \
+               (sc)->cmd_mtx.spl = s;          \
+       else                                    \
+               splx(s);                        \
+} while (0)
+#define IPS_UNLOCK(sc) do {                            \
+       if ((sc)->cmd_mtx.locked) {                     \
+               if (--((sc)->cmd_mtx.locked) == 0)      \
+                       splx((sc)->cmd_mtx.spl);        \
+       }                                               \
+} while (0)
+#define IPS_LOCK_FREE(sc)
+
+#define disk_open_t    d_open_t
+#define disk_close_t   d_close_t
+#define disk_strategy_t        d_strategy_t
+
+#if defined(PCIR_MAPS) && !defined(PCIR_BARS)
+# define PCIR_BAR(x)   (PCIR_BARS + (x) * 4)
+# define PCIR_BARS     PCIR_MAPS
+#endif
+
+
+/*
+ *  IPS MACROS
+ */
+
+#define ips_read_1(sc,offset)          bus_space_read_1(sc->bustag, sc->bushandle, offset)
+#define ips_read_2(sc,offset)          bus_space_read_2(sc->bustag, sc->bushandle, offset)
+#define ips_read_4(sc,offset)          bus_space_read_4(sc->bustag, sc->bushandle, offset)
+
+#define ips_write_1(sc,offset,value)   bus_space_write_1(sc->bustag, sc->bushandle, offset, value)
+#define ips_write_2(sc,offset,value)   bus_space_write_2(sc->bustag, sc->bushandle, offset, value)
+#define ips_write_4(sc,offset,value)   bus_space_write_4(sc->bustag, sc->bushandle, offset, value)
+
+#define ips_read_request(iobuf)                ((iobuf)->b_flags & B_READ)
+
+/* this is ugly.  It zeros the end elements in an ips_command_t struct starting with the status element */
+#define clear_ips_command(command)     bzero(&((command)->status), (unsigned long)(&(command)[1])-(unsigned long)&((command)->status))
+
+#define COMMAND_ERROR(status)          (((status)->fields.basic_status & 0x0f) >= IPS_MIN_ERROR)
+
+#ifndef IPS_DEBUG
+#define DEVICE_PRINTF(x...)
+#define PRINTF(x...)
+#else
+#define DEVICE_PRINTF(level,x...)      if(IPS_DEBUG >= level)device_printf(x)
+#define PRINTF(level,x...)             if(IPS_DEBUG >= level)printf(x)
+#endif
+
+/*
+ *   IPS STRUCTS
+ */
+struct ips_softc;
+
+typedef struct {
+       u_int8_t        command;
+       u_int8_t        id;
+       u_int8_t        drivenum;
+       u_int8_t        reserve2;
+       u_int32_t       lba;
+       u_int32_t       buffaddr;
+       u_int32_t       reserve3;
+} __attribute__ ((packed)) ips_generic_cmd;
+
+typedef struct {
+       u_int8_t        command;
+       u_int8_t        id;
+       u_int8_t        drivenum;
+       u_int8_t        segnum;
+       u_int32_t       lba;
+       u_int32_t       buffaddr;
+       u_int16_t       length;
+       u_int16_t       reserve1;
+} __attribute__ ((packed)) ips_io_cmd;
+
+typedef struct {
+       u_int8_t        command;
+       u_int8_t        id;
+       u_int8_t        pagenum;
+       u_int8_t        rw;
+       u_int32_t       reserve1;
+       u_int32_t       buffaddr;
+       u_int32_t       reserve3;
+} __attribute__ ((packed)) ips_rw_nvram_cmd;
+
+typedef struct {
+       u_int8_t        command;
+       u_int8_t        id;
+       u_int8_t        drivenum;
+       u_int8_t        reserve1;
+       u_int32_t       reserve2;
+       u_int32_t       buffaddr;
+       u_int32_t       reserve3;
+} __attribute__ ((packed)) ips_drive_cmd;
+
+typedef struct {
+       u_int8_t        command;
+       u_int8_t        id;
+       u_int8_t        reserve1;
+       u_int8_t        commandtype;
+       u_int32_t       reserve2;
+       u_int32_t       buffaddr;
+       u_int32_t       reserve3;
+} __attribute__((packed)) ips_adapter_info_cmd;
+
+typedef struct {
+       u_int8_t        command;
+       u_int8_t        id;
+       u_int8_t        reset_count;
+       u_int8_t        reset_type;
+       u_int8_t        second;
+       u_int8_t        minute;
+       u_int8_t        hour;
+       u_int8_t        day;
+       u_int8_t        reserve1[4];
+       u_int8_t        month;
+       u_int8_t        yearH;
+       u_int8_t        yearL;
+       u_int8_t        reserve2;
+} __attribute__((packed)) ips_adapter_ffdc_cmd;
+
+typedef union{
+       ips_generic_cmd         generic_cmd;
+       ips_drive_cmd           drive_cmd;
+       ips_adapter_info_cmd    adapter_info_cmd;
+} ips_cmd_buff_t;
+
+typedef struct {
+   u_int32_t  signature;
+   u_int8_t   reserved;
+   u_int8_t   adapter_slot;
+   u_int16_t  adapter_type;
+   u_int8_t   bios_high[4];
+   u_int8_t   bios_low[4];
+   u_int16_t  reserve2;
+   u_int8_t   reserve3;
+   u_int8_t   operating_system;
+   u_int8_t   driver_high[4];
+   u_int8_t   driver_low[4];
+   u_int8_t   reserve4[100];
+} __attribute__((packed)) ips_nvram_page5;
+
+typedef struct {
+       u_int32_t       addr;
+       u_int32_t       len;
+} ips_sg_element_t;
+
+typedef struct {
+       u_int8_t        drivenum;
+       u_int8_t        merge_id;
+       u_int8_t        raid_lvl;
+       u_int8_t        state;
+       u_int32_t       sector_count;
+} __attribute__((packed)) ips_drive_t;
+
+typedef struct {
+       u_int8_t        drivecount;
+       u_int8_t        reserve1;
+       u_int16_t       reserve2;
+       ips_drive_t drives[IPS_MAX_NUM_DRIVES];
+} __attribute__((packed)) ips_drive_info_t;
+
+typedef struct {
+       u_int8_t        drivecount;
+       u_int8_t        miscflags;
+       u_int8_t        SLTflags;
+       u_int8_t        BSTflags;
+       u_int8_t        pwr_chg_count;
+       u_int8_t        wrong_addr_count;
+       u_int8_t        unident_count;
+       u_int8_t        nvram_dev_chg_count;
+       u_int8_t        codeblock_version[8];
+       u_int8_t        bootblock_version[8];
+       u_int32_t       drive_sector_count[IPS_MAX_NUM_DRIVES];
+       u_int8_t        max_concurrent_cmds;
+       u_int8_t        max_phys_devices;
+       u_int16_t       flash_prog_count;
+       u_int8_t        defunct_disks;
+       u_int8_t        rebuildflags;
+       u_int8_t        offline_drivecount;
+       u_int8_t        critical_drivecount;
+       u_int16_t       config_update_count;
+       u_int8_t        blockedflags;
+       u_int8_t        psdn_error;
+       u_int16_t       addr_dead_disk[4*16];   /* ugly, max # channels * max # scsi devices per channel */
+} __attribute__((packed)) ips_adapter_info_t;
+
+typedef struct {
+       u_int32_t       status[IPS_MAX_CMD_NUM];
+       u_int32_t       base_phys_addr;
+       int             nextstatus;
+       bus_dma_tag_t   dmatag;
+       bus_dmamap_t    dmamap;
+} ips_copper_queue_t;
+
+typedef union {
+   struct {
+      u_int8_t  reserved;
+      u_int8_t  command_id;
+      u_int8_t  basic_status;
+      u_int8_t  extended_status;
+   } fields;
+   volatile u_int32_t    value;
+} ips_cmd_status_t;
+
+/* used to keep track of current commands to the card */
+typedef struct ips_command {
+       u_int8_t                command_number;
+       u_int8_t                id;
+       u_int8_t                timeout;
+       struct ips_softc        *sc;
+       bus_dmamap_t            command_dmamap;
+       void                    *command_buffer;
+       u_int32_t               command_phys_addr;      /*WARNING! must be changed if 64bit addressing ever used*/
+       ips_cmd_status_t        status;
+       SLIST_ENTRY(ips_command)        next;
+       bus_dma_tag_t           data_dmatag;
+       bus_dmamap_t            data_dmamap;
+       void                    *data_buffer;
+       void                    *arg;
+       void                    (*callback)(struct ips_command *command);
+} ips_command_t;
+
+typedef struct ips_wait_list {
+       STAILQ_ENTRY(ips_wait_list) next;
+       void                    *data;
+       int                     (* callback)(ips_command_t *command);
+} ips_wait_list_t;
+
+typedef struct ips_softc {
+       struct resource         *iores;
+       struct resource         *irqres;
+       struct intr_config_hook ips_ich;
+       int                     configured;
+       int                     state;
+       int                     iotype;
+       int                     rid;
+       int                     irqrid;
+       void                    *irqcookie;
+       bus_space_tag_t         bustag;
+       bus_space_handle_t      bushandle;
+       bus_dma_tag_t           adapter_dmatag;
+       bus_dma_tag_t           command_dmatag;
+       bus_dma_tag_t           sg_dmatag;
+       device_t                dev;
+       dev_t                   device_file;
+       struct callout_handle   timer;
+       u_int16_t               adapter_type;
+       ips_adapter_info_t      adapter_info;
+       device_t                diskdev[IPS_MAX_NUM_DRIVES];
+       ips_drive_t             drives[IPS_MAX_NUM_DRIVES];
+       u_int8_t                drivecount;
+       u_int16_t               ffdc_resetcount;
+       struct timeval          ffdc_resettime;
+       u_int8_t                next_drive;
+       u_int8_t                max_cmds;
+       volatile u_int8_t       used_commands;
+       ips_command_t           commandarray[IPS_MAX_CMD_NUM];
+       SLIST_HEAD(command_list, ips_command) free_cmd_list;
+       STAILQ_HEAD(command_wait_list,ips_wait_list)  cmd_wait_list;
+       int                     (*ips_adapter_reinit)(struct ips_softc *sc,
+                                                      int force);
+       void                    (*ips_adapter_intr)(void *sc);
+       void                    (*ips_issue_cmd)(ips_command_t *command);
+       ips_copper_queue_t      *copper_queue;
+       struct mtx              cmd_mtx;
+} ips_softc_t;
+
+/* function defines from ips_ioctl.c */
+extern int ips_ioctl_request(ips_softc_t *sc, u_long ioctl_cmd, caddr_t addr,
+                               int32_t flags);
+/* function defines from ips_disk.c */
+extern void ipsd_finish(struct bio *iobuf);
+
+/* function defines from ips_commands.c */
+extern int ips_flush_cache(ips_softc_t *sc);
+extern void ips_start_io_request(ips_softc_t *sc, struct bio *iobuf);
+extern int ips_get_drive_info(ips_softc_t *sc);
+extern int ips_get_adapter_info(ips_softc_t *sc);
+extern int ips_ffdc_reset(ips_softc_t *sc);
+extern int ips_update_nvram(ips_softc_t *sc);
+extern int ips_clear_adapter(ips_softc_t *sc);
+
+/* function defines from ips.c */
+extern int ips_get_free_cmd(ips_softc_t *sc, int (*callback)(ips_command_t *),
+                               void *data, unsigned long flags);
+extern void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command);
+extern int ips_adapter_init(ips_softc_t *sc);
+extern int ips_morpheus_reinit(ips_softc_t *sc, int force);
+extern int ips_adapter_free(ips_softc_t *sc);
+extern void ips_morpheus_intr(void *sc);
+extern void ips_issue_morpheus_cmd(ips_command_t *command);
+extern int ips_copperhead_reinit(ips_softc_t *sc, int force);
+extern void ips_copperhead_intr(void *sc);
+extern void ips_issue_copperhead_cmd(ips_command_t *command);
+
+#define IPS_CDEV_MAJOR 175
+#define IPSD_CDEV_MAJOR 176
diff --git a/sys/dev/raid/ips/ips_commands.c b/sys/dev/raid/ips/ips_commands.c
new file mode 100644 (file)
index 0000000..5fc86b9
--- /dev/null
@@ -0,0 +1,774 @@
+/*-
+ * Written by: David Jeffery
+ * Copyright (c) 2002 Adaptec Inc.
+ * 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: src/sys/dev/ips/ips_commands.c,v 1.8 2004/01/01 10:22:10 mbr 
+ * $DragonFly: src/sys/dev/raid/ips/ips_commands.c,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+ */
+
+#include <sys/cdefs.h>
+
+#include <dev/raid/ips/ips.h>
+
+static int
+ips_msleep(void *ident, struct ips_softc *sc, int priority, const char *wmesg,
+          int timo)
+{
+       int     r;
+
+       IPS_UNLOCK(sc);
+       r = tsleep(ident, priority, wmesg, timo);
+       IPS_LOCK(sc);
+       return r;
+}
+
+/*
+ * This is an interrupt callback.  It is called from
+ * interrupt context when the adapter has completed the
+ * command.  This very generic callback simply stores
+ * the command's return value in command->arg and wake's
+ * up anyone waiting on the command.
+ */
+static void
+ips_wakeup_callback(ips_command_t *command)
+{
+       ips_cmd_status_t *status;
+
+       status = command->arg;
+       status->value = command->status.value;
+       bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
+                       BUS_DMASYNC_POSTWRITE);
+       IPS_LOCK(command->sc);
+       wakeup(status);
+       IPS_UNLOCK(command->sc);
+}
+/* Below are a series of functions for sending an IO request
+ * to the adapter.  The flow order is: start, send, callback, finish.
+ * The caller must have already assembled an iorequest struct to hold
+ * the details of the IO request. */
+static void ips_io_request_finish(ips_command_t *command)
+{
+       struct bio *iobuf = command->arg;
+
+       if (ips_read_request(iobuf)) {
+               bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+                               BUS_DMASYNC_POSTREAD);
+       } else {
+               bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+                               BUS_DMASYNC_POSTWRITE);
+       }
+       bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
+       bus_dmamap_destroy(command->data_dmatag, command->data_dmamap);
+       if (COMMAND_ERROR(&command->status)) {
+               iobuf->bio_flags |=BIO_ERROR;
+               iobuf->bio_error = EIO;
+       }
+       ips_insert_free_cmd(command->sc, command);
+       ipsd_finish(iobuf);
+}
+
+static void
+ips_io_request_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
+                       int error)
+{
+       ips_softc_t *sc;
+       ips_command_t *command = cmdptr;
+       ips_sg_element_t *sg_list;
+       ips_io_cmd *command_struct;
+       struct bio *iobuf = command->arg;
+       int i, length = 0;
+       u_int8_t cmdtype;
+
+       sc = command->sc;
+       if (error) {
+               printf("ips: error = %d in ips_sg_request_callback\n", error);
+               bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
+               bus_dmamap_destroy(command->data_dmatag, command->data_dmamap);
+               iobuf->bio_flags |= BIO_ERROR;
+               iobuf->bio_error = ENOMEM;
+               ips_insert_free_cmd(sc, command);
+               ipsd_finish(iobuf);
+               return;
+       }
+       command_struct = (ips_io_cmd *)command->command_buffer;
+       command_struct->id = command->id;
+       command_struct->drivenum = (uintptr_t)iobuf->bio_driver1;
+       if (segnum != 1) {
+               if (ips_read_request(iobuf))
+                       cmdtype = IPS_SG_READ_CMD;
+               else
+                       cmdtype = IPS_SG_WRITE_CMD;
+               command_struct->segnum = segnum;
+               sg_list = (ips_sg_element_t *)((u_int8_t *)
+                          command->command_buffer + IPS_COMMAND_LEN);
+               for (i = 0; i < segnum; i++) {
+                       sg_list[i].addr = segments[i].ds_addr;
+                       sg_list[i].len = segments[i].ds_len;
+                       length += segments[i].ds_len;
+               }
+               command_struct->buffaddr =
+                   (u_int32_t)command->command_phys_addr + IPS_COMMAND_LEN;
+       } else {
+               if (ips_read_request(iobuf))
+                       cmdtype = IPS_READ_CMD;
+               else
+                       cmdtype = IPS_WRITE_CMD;
+               command_struct->buffaddr = segments[0].ds_addr;
+               length = segments[0].ds_len;
+       }
+       command_struct->command = cmdtype;
+       command_struct->lba = iobuf->bio_pblkno;
+       length = (length + IPS_BLKSIZE - 1)/IPS_BLKSIZE;
+       command_struct->length = length;
+       bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+                       BUS_DMASYNC_PREWRITE);
+       if (ips_read_request(iobuf)) {
+               bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+                               BUS_DMASYNC_PREREAD);
+       } else {
+               bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+                               BUS_DMASYNC_PREWRITE);
+       }
+       /*
+        * the cast to long long below is necessary because our b_pblkno
+        * is 32bit wide whereas it's 64bit on FreeBSD-CURRENT.
+        */
+       PRINTF(10, "ips test: command id: %d segments: %d "
+               "pblkno: %lld length: %d, ds_len: %d\n", command->id, segnum,
+               (long long)iobuf->bio_pblkno,
+               length, segments[0].ds_len);
+
+       sc->ips_issue_cmd(command);
+       return;
+}
+
+static int
+ips_send_io_request(ips_command_t *command)
+{
+       ips_softc_t *sc = command->sc;
+       struct bio *iobuf = command->arg;
+       command->data_dmatag = sc->sg_dmatag;
+
+       if (bus_dmamap_create(command->data_dmatag, 0, &command->data_dmamap)) {
+               device_printf(sc->dev, "dmamap failed\n");
+               iobuf->bio_flags |= BIO_ERROR;
+               iobuf->bio_error = ENOMEM;
+               ips_insert_free_cmd(sc, command);
+               ipsd_finish(iobuf);
+               return 0;
+       }
+       command->callback = ips_io_request_finish;
+       PRINTF(10, "ips test: : bcount %ld\n", iobuf->bio_bcount);
+       bus_dmamap_load(command->data_dmatag, command->data_dmamap,
+                       iobuf->bio_data, iobuf->bio_bcount,
+                       ips_io_request_callback, command, 0);
+       return 0;
+}
+
+void
+ips_start_io_request(ips_softc_t *sc, struct bio *iobuf)
+{
+       if (ips_get_free_cmd(sc, ips_send_io_request, iobuf, 0)) {
+               device_printf(sc->dev, "no mem for command slots!\n");
+               iobuf->bio_flags |= BIO_ERROR;
+               iobuf->bio_error = ENOMEM;
+               ipsd_finish(iobuf);
+               return;
+       }
+       return;
+}
+
+/*
+ * Below are a series of functions for sending an adapter info request
+ * to the adapter.  The flow order is: get, send, callback. It uses
+ * the generic finish callback at the top of this file.
+ * This can be used to get configuration/status info from the card
+ */
+static void
+ips_adapter_info_callback(void *cmdptr, bus_dma_segment_t *segments,int segnum,
+                         int error)
+{
+       ips_softc_t *sc;
+       ips_command_t *command = cmdptr;
+       ips_adapter_info_cmd *command_struct;
+       sc = command->sc;
+       if (error) {
+               ips_cmd_status_t * status = command->arg;
+               status->value = IPS_ERROR_STATUS; /* a lovely error value */
+               ips_insert_free_cmd(sc, command);
+               printf("ips: error = %d in ips_get_adapter_info\n", error);
+               return;
+       }
+       command_struct = (ips_adapter_info_cmd *)command->command_buffer;
+       command_struct->command = IPS_ADAPTER_INFO_CMD;
+       command_struct->id = command->id;
+       command_struct->buffaddr = segments[0].ds_addr;
+
+       bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+                       BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+                       BUS_DMASYNC_PREREAD);
+       sc->ips_issue_cmd(command);
+}
+
+static int
+ips_send_adapter_info_cmd(ips_command_t *command)
+{
+       ips_softc_t *sc = command->sc;
+       ips_cmd_status_t *status = command->arg;
+       int error = 0;
+
+       if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
+                               /* alignemnt */ 1,
+                               /* boundary  */ 0,
+                               /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
+                               /* highaddr  */ BUS_SPACE_MAXADDR,
+                               /* filter    */ NULL,
+                               /* filterarg */ NULL,
+                               /* maxsize   */ IPS_ADAPTER_INFO_LEN,
+                               /* numsegs   */ 1,
+                               /* maxsegsize*/ IPS_ADAPTER_INFO_LEN,
+                               /* flags     */ 0,
+                               &command->data_dmatag) != 0) {
+               printf("ips: can't alloc dma tag for adapter status\n");
+               error = ENOMEM;
+               goto exit;
+       }
+       if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
+          BUS_DMA_NOWAIT, &command->data_dmamap)) {
+               error = ENOMEM;
+               goto exit;
+       }
+       command->callback = ips_wakeup_callback;
+       IPS_LOCK(sc);
+       bus_dmamap_load(command->data_dmatag, command->data_dmamap,
+           command->data_buffer, IPS_ADAPTER_INFO_LEN,
+           ips_adapter_info_callback, command, BUS_DMA_NOWAIT);
+       if ((status->value == IPS_ERROR_STATUS) ||
+           ips_msleep(status, sc, 0, "ips", 30 * hz) == EWOULDBLOCK)
+               error = ETIMEDOUT;
+       IPS_UNLOCK(sc);
+       if (error == 0) {
+               bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+                   BUS_DMASYNC_POSTREAD);
+               memcpy(&(sc->adapter_info), command->data_buffer,
+                       IPS_ADAPTER_INFO_LEN);
+       }
+       bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
+exit:
+       /* I suppose I should clean up my memory allocations */
+       bus_dmamem_free(command->data_dmatag, command->data_buffer,
+           command->data_dmamap);
+       bus_dma_tag_destroy(command->data_dmatag);
+       ips_insert_free_cmd(sc, command);
+       return error;
+}
+
+int
+ips_get_adapter_info(ips_softc_t *sc)
+{
+       int error = 0;
+       ips_cmd_status_t *status;
+
+       status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (status == NULL)
+               return ENOMEM;
+       if (ips_get_free_cmd(sc, ips_send_adapter_info_cmd, status,
+           IPS_NOWAIT_FLAG) > 0) {
+               device_printf(sc->dev, "unable to get adapter configuration\n");
+               free(status, M_DEVBUF);
+               return ENXIO;
+       }
+       if (COMMAND_ERROR(status))
+               error = ENXIO;
+       free(status, M_DEVBUF);
+       return error;
+}
+
+/*
+ * Below are a series of functions for sending a drive info request
+ * to the adapter.  The flow order is: get, send, callback. It uses
+ * the generic finish callback at the top of this file.
+ * This can be used to get drive status info from the card
+ */
+static void
+ips_drive_info_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
+                       int error)
+{
+       ips_softc_t *sc;
+       ips_command_t *command = cmdptr;
+       ips_drive_cmd *command_struct;
+
+       sc = command->sc;
+       if (error) {
+               ips_cmd_status_t *status = command->arg;
+
+               status->value = IPS_ERROR_STATUS;
+               ips_insert_free_cmd(sc, command);
+               printf("ips: error = %d in ips_get_drive_info\n", error);
+               return;
+       }
+       command_struct = (ips_drive_cmd *)command->command_buffer;
+       command_struct->command = IPS_DRIVE_INFO_CMD;
+       command_struct->id = command->id;
+       command_struct->buffaddr = segments[0].ds_addr;
+       bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+           BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+           BUS_DMASYNC_PREREAD);
+       sc->ips_issue_cmd(command);
+}
+
+static int
+ips_send_drive_info_cmd(ips_command_t *command)
+{
+       int error = 0;
+       ips_softc_t *sc = command->sc;
+       ips_cmd_status_t *status = command->arg;
+       ips_drive_info_t *driveinfo;
+
+       if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
+                               /* alignemnt */ 1,
+                               /* boundary  */ 0,
+                               /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
+                               /* highaddr  */ BUS_SPACE_MAXADDR,
+                               /* filter    */ NULL,
+                               /* filterarg */ NULL,
+                               /* maxsize   */ IPS_DRIVE_INFO_LEN,
+                               /* numsegs   */ 1,
+                               /* maxsegsize*/ IPS_DRIVE_INFO_LEN,
+                               /* flags     */ 0,
+                               &command->data_dmatag) != 0) {
+               printf("ips: can't alloc dma tag for drive status\n");
+               error = ENOMEM;
+               goto exit;
+       }
+       if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
+           BUS_DMA_NOWAIT, &command->data_dmamap)) {
+               error = ENOMEM;
+               goto exit;
+       }
+       command->callback = ips_wakeup_callback;
+       IPS_LOCK(sc);
+       bus_dmamap_load(command->data_dmatag, command->data_dmamap,
+           command->data_buffer,IPS_DRIVE_INFO_LEN,
+           ips_drive_info_callback, command, BUS_DMA_NOWAIT);
+       if ((status->value == IPS_ERROR_STATUS) ||
+           ips_msleep(status, sc, 0, "ips", 10 * hz) == EWOULDBLOCK)
+               error = ETIMEDOUT;
+       IPS_UNLOCK(sc);
+
+       if (error == 0) {
+               bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+                   BUS_DMASYNC_POSTREAD);
+               driveinfo = command->data_buffer;
+               memcpy(sc->drives, driveinfo->drives, sizeof(ips_drive_t) * 8);
+               sc->drivecount = driveinfo->drivecount;
+               device_printf(sc->dev, "logical drives: %d\n", sc->drivecount);
+       }
+       bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
+exit:
+       /* I suppose I should clean up my memory allocations */
+       bus_dmamem_free(command->data_dmatag, command->data_buffer,
+                       command->data_dmamap);
+       bus_dma_tag_destroy(command->data_dmatag);
+       ips_insert_free_cmd(sc, command);
+       return error;
+
+}
+int
+ips_get_drive_info(ips_softc_t *sc)
+{
+       int error = 0;
+       ips_cmd_status_t *status;
+
+       status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (status == NULL)
+               return ENOMEM;
+       if (ips_get_free_cmd(sc, ips_send_drive_info_cmd, status,
+           IPS_NOWAIT_FLAG) > 0) {
+               free(status, M_DEVBUF);
+               device_printf(sc->dev, "unable to get drive configuration\n");
+               return ENXIO;
+       }
+       if (COMMAND_ERROR(status))
+               error = ENXIO;
+       free(status, M_DEVBUF);
+       return error;
+}
+
+/*
+ * Below is a pair of functions for making sure data is safely
+ * on disk by flushing the adapter's cache.
+ */
+static int
+ips_send_flush_cache_cmd(ips_command_t *command)
+{
+       ips_softc_t *sc = command->sc;
+       ips_cmd_status_t *status = command->arg;
+       ips_generic_cmd *command_struct;
+
+       PRINTF(10,"ips test: got a command, building flush command\n");
+       command->callback = ips_wakeup_callback;
+       command_struct = (ips_generic_cmd *)command->command_buffer;
+       command_struct->command = IPS_CACHE_FLUSH_CMD;
+       command_struct->id = command->id;
+       bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+           BUS_DMASYNC_PREWRITE);
+       IPS_LOCK(sc);
+       sc->ips_issue_cmd(command);
+       if (status->value != IPS_ERROR_STATUS)
+               ips_msleep(status, sc, 0, "flush2", 0);
+       IPS_UNLOCK(sc);
+       ips_insert_free_cmd(sc, command);
+       return 0;
+}
+
+int
+ips_flush_cache(ips_softc_t *sc)
+{
+       ips_cmd_status_t *status;
+
+       status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (status == NULL)
+               return ENOMEM;
+       device_printf(sc->dev, "flushing cache\n");
+       if (ips_get_free_cmd(sc, ips_send_flush_cache_cmd, status,
+           IPS_NOWAIT_FLAG)) {
+               free(status, M_DEVBUF);
+               device_printf(sc->dev, "ERROR: unable to get a command! "
+                   "can't flush cache!\n");
+       }
+       if (COMMAND_ERROR(status)) {
+               device_printf(sc->dev, "ERROR: cache flush command failed!\n");
+       }
+       free(status, M_DEVBUF);
+       return 0;
+}
+
+/*
+ * Simplified localtime to provide timevalues for ffdc.
+ * Taken from libc/stdtime/localtime.c
+ */
+static void
+ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime)
+{
+       long    days, rem, y;
+       int     yleap, *ip, month;
+       int     year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR };
+       int     mon_lengths[2][IPS_MONSPERYEAR] = {
+               { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+               { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+       };
+
+       days = sctime / IPS_SECSPERDAY;
+       rem  = sctime % IPS_SECSPERDAY;
+
+       command->hour = rem / IPS_SECSPERHOUR;
+       rem           = rem % IPS_SECSPERHOUR;
+
+       command->minute = rem / IPS_SECSPERMIN;
+       command->second = rem % IPS_SECSPERMIN;
+
+       y = IPS_EPOCH_YEAR;
+       while (days < 0 || days >= (long)year_lengths[yleap = ips_isleap(y)]) {
+               long    newy;
+
+               newy = y + days / IPS_DAYSPERNYEAR;
+               if (days < 0)
+                       --newy;
+               days -= (newy - y) * IPS_DAYSPERNYEAR +
+                   IPS_LEAPS_THRU_END_OF(newy - 1) -
+                   IPS_LEAPS_THRU_END_OF(y - 1);
+               y = newy;
+       }
+       command->yearH = y / 100;
+       command->yearL = y % 100;
+       ip = mon_lengths[yleap];
+       for (month = 0; days >= (long)ip[month]; ++month)
+               days = days - (long)ip[month];
+       command->month = month + 1;
+       command->day = days + 1;
+}
+
+static int
+ips_send_ffdc_reset_cmd(ips_command_t *command)
+{
+       ips_softc_t *sc = command->sc;
+       ips_cmd_status_t *status = command->arg;
+       ips_adapter_ffdc_cmd *command_struct;
+
+       PRINTF(10, "ips test: got a command, building ffdc reset command\n");
+       command->callback = ips_wakeup_callback;
+       command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer;
+       command_struct->command = IPS_FFDC_CMD;
+       command_struct->id = command->id;
+       command_struct->reset_count = sc->ffdc_resetcount;
+       command_struct->reset_type  = 0x0;
+       ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec);
+       bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+           BUS_DMASYNC_PREWRITE);
+       IPS_LOCK(sc);
+       sc->ips_issue_cmd(command);
+       if (status->value != IPS_ERROR_STATUS)
+               ips_msleep(status, sc, 0, "ffdc", 0);
+       IPS_UNLOCK(sc);
+       ips_insert_free_cmd(sc, command);
+       return 0;
+}
+
+int
+ips_ffdc_reset(ips_softc_t *sc)
+{
+       ips_cmd_status_t *status;
+
+       status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (status == NULL)
+               return ENOMEM;
+       if (ips_get_free_cmd(sc, ips_send_ffdc_reset_cmd, status,
+           IPS_NOWAIT_FLAG)) {
+               free(status, M_DEVBUF);
+               device_printf(sc->dev, "ERROR: unable to get a command! "
+                   "can't send ffdc reset!\n");
+       }
+       if (COMMAND_ERROR(status))
+               device_printf(sc->dev, "ERROR: ffdc reset command failed!\n");
+       free(status, M_DEVBUF);
+       return 0;
+}
+
+static void
+ips_write_nvram(ips_command_t *command)
+{
+       ips_softc_t *sc = command->sc;
+       ips_rw_nvram_cmd *command_struct;
+       ips_nvram_page5 *nvram;
+
+       /*FIXME check for error */
+       command->callback = ips_wakeup_callback;
+       command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
+       command_struct->command = IPS_RW_NVRAM_CMD;
+       command_struct->id = command->id;
+       command_struct->pagenum = 5;
+       command_struct->rw      = 1;    /* write */
+       bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+           BUS_DMASYNC_POSTREAD);
+       nvram = command->data_buffer;
+       /* retrieve adapter info and save in sc */
+       sc->adapter_type = nvram->adapter_type;
+       strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4);
+       strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4);
+       nvram->operating_system = IPS_OS_FREEBSD;
+       bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+           BUS_DMASYNC_PREWRITE);
+       sc->ips_issue_cmd(command);
+}
+
+static void
+ips_read_nvram_callback(void *cmdptr, bus_dma_segment_t *segments, int segnum,
+                       int error)
+{
+       ips_softc_t *sc;
+       ips_command_t *command = cmdptr;
+       ips_rw_nvram_cmd *command_struct;
+
+       sc = command->sc;
+       if (error) {
+               ips_cmd_status_t *status = command->arg;
+
+               status->value = IPS_ERROR_STATUS;
+               ips_insert_free_cmd(sc, command);
+               printf("ips: error = %d in ips_read_nvram_callback\n", error);
+               return;
+       }
+       command_struct = (ips_rw_nvram_cmd *)command->command_buffer;
+       command_struct->command = IPS_RW_NVRAM_CMD;
+       command_struct->id = command->id;
+       command_struct->pagenum = 5;
+       command_struct->rw = 0;
+       command_struct->buffaddr = segments[0].ds_addr;
+
+       bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+           BUS_DMASYNC_PREWRITE);
+       bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+           BUS_DMASYNC_PREREAD);
+       sc->ips_issue_cmd(command);
+}
+
+static int
+ips_read_nvram(ips_command_t *command)
+{
+       int error = 0;
+       ips_softc_t *sc = command->sc;
+       ips_cmd_status_t *status = command->arg;
+
+       if (bus_dma_tag_create( /* parent    */ sc->adapter_dmatag,
+                               /* alignemnt */ 1,
+                               /* boundary  */ 0,
+                               /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
+                               /* highaddr  */ BUS_SPACE_MAXADDR,
+                               /* filter    */ NULL,
+                               /* filterarg */ NULL,
+                               /* maxsize   */ IPS_NVRAM_PAGE_SIZE,
+                               /* numsegs   */ 1,
+                               /* maxsegsize*/ IPS_NVRAM_PAGE_SIZE,
+                               /* flags     */ 0,
+                               &command->data_dmatag) != 0) {
+               printf("ips: can't alloc dma tag for nvram\n");
+               error = ENOMEM;
+               goto exit;
+       }
+       if (bus_dmamem_alloc(command->data_dmatag, &command->data_buffer,
+           BUS_DMA_NOWAIT, &command->data_dmamap)) {
+               error = ENOMEM;
+               goto exit;
+       }
+       command->callback = ips_write_nvram;
+       IPS_LOCK(sc);
+       bus_dmamap_load(command->data_dmatag, command->data_dmamap,
+           command->data_buffer, IPS_NVRAM_PAGE_SIZE, ips_read_nvram_callback,
+           command, BUS_DMA_NOWAIT);
+       if ((status->value == IPS_ERROR_STATUS) ||
+           ips_msleep(status, sc, 0, "ips", 0) == EWOULDBLOCK)
+               error = ETIMEDOUT;
+       IPS_UNLOCK(sc);
+       if (error == 0) {
+               bus_dmamap_sync(command->data_dmatag, command->data_dmamap,
+                               BUS_DMASYNC_POSTWRITE);
+       }
+       bus_dmamap_unload(command->data_dmatag, command->data_dmamap);
+exit:
+       bus_dmamem_free(command->data_dmatag, command->data_buffer,
+                       command->data_dmamap);
+       bus_dma_tag_destroy(command->data_dmatag);
+       ips_insert_free_cmd(sc, command);
+       return error;
+}
+
+int
+ips_update_nvram(ips_softc_t *sc)
+{
+       ips_cmd_status_t *status;
+
+       status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (status == NULL)
+               return ENOMEM;
+       if (ips_get_free_cmd(sc, ips_read_nvram, status, IPS_NOWAIT_FLAG)) {
+               free(status, M_DEVBUF);
+               device_printf(sc->dev, "ERROR: unable to get a command! "
+                   "can't update nvram\n");
+               return 1;
+       }
+       if (COMMAND_ERROR(status))
+               device_printf(sc->dev, "ERROR: nvram update command failed!\n");
+       free(status, M_DEVBUF);
+       return 0;
+}
+
+static int
+ips_send_config_sync_cmd(ips_command_t *command)
+{
+       ips_softc_t *sc = command->sc;
+       ips_cmd_status_t *status = command->arg;
+       ips_generic_cmd *command_struct;
+
+       PRINTF(10, "ips test: got a command, building flush command\n");
+       command->callback = ips_wakeup_callback;
+       command_struct = (ips_generic_cmd *)command->command_buffer;
+       command_struct->command = IPS_CONFIG_SYNC_CMD;
+       command_struct->id = command->id;
+       command_struct->reserve2 = IPS_POCL;
+       bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+           BUS_DMASYNC_PREWRITE);
+       IPS_LOCK(sc);
+       sc->ips_issue_cmd(command);
+       if (status->value != IPS_ERROR_STATUS)
+               ips_msleep(status, sc, 0, "ipssyn", 0);
+       IPS_UNLOCK(sc);
+       ips_insert_free_cmd(sc, command);
+       return 0;
+}
+
+static int
+ips_send_error_table_cmd(ips_command_t *command)
+{
+       ips_softc_t *sc = command->sc;
+       ips_cmd_status_t *status = command->arg;
+       ips_generic_cmd *command_struct;
+
+       PRINTF(10, "ips test: got a command, building errortable command\n");
+       command->callback = ips_wakeup_callback;
+       command_struct = (ips_generic_cmd *)command->command_buffer;
+       command_struct->command = IPS_ERROR_TABLE_CMD;
+       command_struct->id = command->id;
+       command_struct->reserve2 = IPS_CSL;
+       bus_dmamap_sync(sc->command_dmatag, command->command_dmamap,
+           BUS_DMASYNC_PREWRITE);
+       IPS_LOCK(sc);
+       sc->ips_issue_cmd(command);
+       if (status->value != IPS_ERROR_STATUS)
+               ips_msleep(status, sc, 0, "ipsetc", 0);
+       IPS_UNLOCK(sc);
+       ips_insert_free_cmd(sc, command);
+       return 0;
+}
+
+int
+ips_clear_adapter(ips_softc_t *sc)
+{
+       ips_cmd_status_t *status;
+
+       status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT | M_ZERO);
+       if (status == NULL)
+               return ENOMEM;
+       device_printf(sc->dev, "syncing config\n");
+       if (ips_get_free_cmd(sc, ips_send_config_sync_cmd, status,
+           IPS_NOWAIT_FLAG)) {
+               free(status, M_DEVBUF);
+               device_printf(sc->dev, "ERROR: unable to get a command! "
+                   "can't sync cache!\n");
+               return 1;
+       }
+       if (COMMAND_ERROR(status)) {
+               free(status, M_DEVBUF);
+               device_printf(sc->dev, "ERROR: cache sync command failed!\n");
+               return 1;
+       }
+       device_printf(sc->dev, "clearing error table\n");
+       if (ips_get_free_cmd(sc, ips_send_error_table_cmd, status,
+           IPS_NOWAIT_FLAG)) {
+               free(status, M_DEVBUF);
+               device_printf(sc->dev, "ERROR: unable to get a command! "
+                   "can't sync cache!\n");
+               return 1;
+       }
+       if (COMMAND_ERROR(status)) {
+               device_printf(sc->dev, "ERROR: etable command failed!\n");
+               free(status, M_DEVBUF);
+               return 1;
+       }
+       free(status, M_DEVBUF);
+       return 0;
+}
diff --git a/sys/dev/raid/ips/ips_disk.c b/sys/dev/raid/ips/ips_disk.c
new file mode 100644 (file)
index 0000000..c03ecb5
--- /dev/null
@@ -0,0 +1,191 @@
+/*-
+ * Written by: David Jeffery
+ * Copyright (c) 2002 Adaptec Inc.
+ * 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: src/sys/dev/ips/ips_disk.c,v 1.4 2003/09/22 04:59:07 njl 
+ * $DragonFly: src/sys/dev/raid/ips/ips_disk.c,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+ */
+
+#include <sys/cdefs.h>
+
+#include <dev/raid/ips/ips.h>
+#include <dev/raid/ips/ips_disk.h>
+#include <sys/stat.h>
+
+static int ipsd_probe(device_t dev);
+static int ipsd_attach(device_t dev);
+static int ipsd_detach(device_t dev);
+
+static disk_open_t ipsd_open;
+static disk_close_t ipsd_close;
+static disk_strategy_t ipsd_strategy;
+
+static struct cdevsw ipsd_cdevsw = {
+       .d_name         = "ipsd",
+       .d_maj          = IPSD_CDEV_MAJOR,
+       .d_flags        = D_DISK,
+       .d_port         = NULL,
+       .d_autoq        = 0,
+       .old_open       = ipsd_open,
+       .old_close      = ipsd_close,
+       .old_strategy   = ipsd_strategy,
+       .old_read       = physread,
+       .old_write      = physwrite,
+};
+
+static device_method_t ipsd_methods[] = {
+       DEVMETHOD(device_probe,         ipsd_probe),
+       DEVMETHOD(device_attach,        ipsd_attach),
+       DEVMETHOD(device_detach,        ipsd_detach),
+       { 0, 0 }
+};
+
+
+static driver_t ipsd_driver = {
+       "ipsd",
+       ipsd_methods,
+       sizeof(ipsdisk_softc_t)
+};
+
+static devclass_t ipsd_devclass;
+DRIVER_MODULE(ipsd, ips, ipsd_driver, ipsd_devclass, 0, 0);
+
+/*
+ * handle opening of disk device.  It must set up all information about
+ * the geometry and size of the disk
+ */
+static int
+ipsd_open(dev_t dev, int oflags, int devtype, d_thread_t *td)
+{
+       ipsdisk_softc_t *dsc = dev->si_drv1;
+
+       if (dsc == NULL)
+               return (ENXIO);
+       dsc->state |= IPS_DEV_OPEN;
+       DEVICE_PRINTF(2, dsc->dev, "I'm open\n");
+       return 0;
+}
+
+static int
+ipsd_close(dev_t dev, int oflags, int devtype, d_thread_t *td)
+{
+       ipsdisk_softc_t *dsc = dev->si_drv1;
+
+       dsc->state &= ~IPS_DEV_OPEN;
+       DEVICE_PRINTF(2, dsc->dev, "I'm closed for the day\n");
+       return 0;
+}
+
+/* ipsd_finish is called to clean up and return a completed IO request */
+void
+ipsd_finish(struct bio *iobuf)
+{
+       if (iobuf->bio_flags & BIO_ERROR) {
+               ipsdisk_softc_t *dsc;
+               dsc = iobuf->bio_disk->d_drv1;
+               device_printf(dsc->dev, "iobuf error %d\n", iobuf->bio_error);
+       } else
+               iobuf->bio_resid = 0;
+
+       biodone(iobuf);
+}
+
+
+static void
+ipsd_strategy(struct bio *iobuf)
+{
+       ipsdisk_softc_t *dsc;
+
+       dsc = iobuf->bio_disk->d_drv1;
+       DEVICE_PRINTF(8, dsc->dev, "in strategy\n");
+       iobuf->bio_driver1 = (void *)(uintptr_t)dsc->sc->drives[dsc->disk_number].drivenum;
+       ips_start_io_request(dsc->sc, iobuf);
+}
+
+static int
+ipsd_probe(device_t dev)
+{
+       DEVICE_PRINTF(2, dev, "in probe\n");
+       device_set_desc(dev, "Logical Drive");
+       return 0;
+}
+
+static int
+ipsd_attach(device_t dev)
+{
+       device_t adapter;
+       ipsdisk_softc_t *dsc;
+       struct  disklabel *label;
+       u_int totalsectors;
+       u_int nheads, nsectors;
+
+       DEVICE_PRINTF(2, dev, "in attach\n");
+       dsc = (ipsdisk_softc_t *)device_get_softc(dev);
+       bzero(dsc, sizeof(ipsdisk_softc_t));
+       adapter = device_get_parent(dev);
+       dsc->dev = dev;
+       dsc->sc = device_get_softc(adapter);
+       dsc->unit = device_get_unit(dev);
+       dsc->disk_number = (uintptr_t) device_get_ivars(dev);
+       totalsectors = dsc->sc->drives[dsc->disk_number].sector_count;
+       if ((totalsectors > 0x400000) &&
+           ((dsc->sc->adapter_info.miscflags & 0x8) == 0)) {
+               nheads = IPS_NORM_HEADS;
+               nsectors = IPS_NORM_SECTORS;
+       } else {
+               nheads = IPS_COMP_HEADS;
+               nsectors = IPS_COMP_SECTORS;
+       }
+       dsc->ipsd_dev_t = disk_create(dsc->unit, &dsc->ipsd_disk, 0,
+           &ipsd_cdevsw);
+       dsc->ipsd_dev_t->si_drv1 = dsc;
+       dsc->ipsd_dev_t->si_iosize_max = IPS_MAX_IO_SIZE;
+       label = &dsc->ipsd_disk.d_label;
+       bzero(label, sizeof(*label));
+       label->d_ntracks    = nheads;
+       label->d_nsectors   = nsectors;
+       label->d_type       = DTYPE_ESDI;
+       label->d_secsize    = IPS_BLKSIZE;
+       label->d_ncylinders = totalsectors / nheads / nsectors;
+       label->d_secpercyl  = nsectors / nheads;
+       label->d_secperunit = totalsectors;
+       device_printf(dev, "Logical Drive  (%dMB)\n",
+           dsc->sc->drives[dsc->disk_number].sector_count >> 11);
+       return 0;
+}
+
+static int
+ipsd_detach(device_t dev)
+{
+       ipsdisk_softc_t *dsc;
+
+       DEVICE_PRINTF(2, dev, "in detach\n");
+       dsc = (ipsdisk_softc_t *)device_get_softc(dev);
+       if (dsc->state & IPS_DEV_OPEN)
+               return (EBUSY);
+       disk_destroy(&dsc->ipsd_disk);
+       return 0;
+}
+
diff --git a/sys/dev/raid/ips/ips_disk.h b/sys/dev/raid/ips/ips_disk.h
new file mode 100644 (file)
index 0000000..c217cd2
--- /dev/null
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Written by: David Jeffery
+ *
+ * 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: src/sys/dev/ips/ips_disk.h,v 1.2 2003/08/22 06:00:27 imp Exp $
+ * $DragonFly: src/sys/dev/raid/ips/ips_disk.h,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+ */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/disk.h>
+#include <sys/buf.h>
+#include <sys/disk.h>
+
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+
+#define IPS_MAX_IO_SIZE                0x10000
+
+#define IPS_COMP_HEADS         128
+#define IPS_COMP_SECTORS       32
+#define IPS_NORM_HEADS         254
+#define IPS_NORM_SECTORS       63
+
+typedef struct ipsdisk_softc {
+       device_t        dev;
+       int             unit;
+       int             disk_number;
+       u_int32_t       state;
+       struct disk     ipsd_disk;
+       dev_t           ipsd_dev_t;
+       ips_softc_t     *sc;
+} ipsdisk_softc_t;
diff --git a/sys/dev/raid/ips/ips_ioctl.c b/sys/dev/raid/ips/ips_ioctl.c
new file mode 100644 (file)
index 0000000..b6037aa
--- /dev/null
@@ -0,0 +1,177 @@
+/*-
+ * Written by: David Jeffery
+ * Copyright (c) 2002 Adaptec Inc.
+ * 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: src/sys/dev/ips/ips_ioctl.c,v 1.4 2003/08/24 17:49:14 obrien 
+ * $DragonFly: src/sys/dev/raid/ips/ips_ioctl.c,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+ */
+
+#include <sys/cdefs.h>
+
+#include <dev/raid/ips/ips.h>
+#include <dev/raid/ips/ips_ioctl.h>
+
+static void
+ips_ioctl_finish(ips_command_t *command)
+{
+       ips_ioctl_t *ioctl_cmd = command->arg;
+
+       if (ioctl_cmd->readwrite & IPS_IOCTL_READ) {
+               bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
+                   BUS_DMASYNC_POSTREAD);
+       } else if (ioctl_cmd->readwrite & IPS_IOCTL_WRITE) {
+               bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
+                   BUS_DMASYNC_POSTWRITE);
+       }
+       bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
+           BUS_DMASYNC_POSTWRITE);
+       bus_dmamap_unload(ioctl_cmd->dmatag, ioctl_cmd->dmamap);
+       ioctl_cmd->status.value = command->status.value;
+       ips_insert_free_cmd(command->sc, command);
+}
+
+static void
+ips_ioctl_callback(void *cmdptr, bus_dma_segment_t *segments,
+    int segnum, int error)
+{
+       ips_command_t *command;
+       ips_ioctl_t *ioctl_cmd;
+       ips_generic_cmd *command_buffer;
+
+       command = cmdptr;
+       ioctl_cmd = command->arg;
+       command_buffer = command->command_buffer;
+       if (error) {
+               ioctl_cmd->status.value = IPS_ERROR_STATUS;
+               ips_insert_free_cmd(command->sc, command);
+               return;
+       }
+       command_buffer->id = command->id;
+       command_buffer->buffaddr = segments[0].ds_addr;
+       if (ioctl_cmd->readwrite & IPS_IOCTL_WRITE) {
+               bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
+                   BUS_DMASYNC_PREWRITE);
+       } else if (ioctl_cmd->readwrite & IPS_IOCTL_READ) {
+               bus_dmamap_sync(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
+                   BUS_DMASYNC_PREREAD);
+       }
+       bus_dmamap_sync(command->sc->command_dmatag, command->command_dmamap,
+           BUS_DMASYNC_PREWRITE);
+       command->sc->ips_issue_cmd(command);
+}
+
+static int
+ips_ioctl_start(ips_command_t *command)
+{
+       ips_ioctl_t *ioctl_cmd = command->arg;
+
+       memcpy(command->command_buffer, ioctl_cmd->command_buffer,
+           sizeof(ips_generic_cmd));
+       command->callback = ips_ioctl_finish;
+       bus_dmamap_load(ioctl_cmd->dmatag, ioctl_cmd->dmamap,
+           ioctl_cmd->data_buffer, ioctl_cmd->datasize, ips_ioctl_callback,
+           command, 0);
+       return 0;
+}
+
+static int
+ips_ioctl_cmd(ips_softc_t *sc, ips_ioctl_t *ioctl_cmd,
+    ips_user_request *user_request)
+{
+       int error = EINVAL;
+
+       if (bus_dma_tag_create(
+                       /* parent    */ sc->adapter_dmatag,
+                       /* alignment */ 1,
+                       /* boundary  */ 0,
+                       /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
+                       /* highaddr  */ BUS_SPACE_MAXADDR,
+                       /* filter    */ NULL,
+                       /* filterarg */ NULL,
+                       /* maxsize   */ ioctl_cmd->datasize,
+                       /* numsegs   */ 1,
+                       /* maxsegsize*/ ioctl_cmd->datasize,
+                       /* flags     */ 0,
+                       &ioctl_cmd->dmatag) != 0) {
+               return ENOMEM;
+       }
+       if (bus_dmamem_alloc(ioctl_cmd->dmatag, &ioctl_cmd->data_buffer,
+           0, &ioctl_cmd->dmamap)) {
+               error = ENOMEM;
+               goto exit;
+       }
+       if (copyin(user_request->data_buffer, ioctl_cmd->data_buffer,
+           ioctl_cmd->datasize))
+               goto exit;
+       ioctl_cmd->status.value = 0xffffffff;
+       if ((error = ips_get_free_cmd(sc, ips_ioctl_start, ioctl_cmd, 0)) > 0) {
+               error = ENOMEM;
+               goto exit;
+       }
+       while (ioctl_cmd->status.value == 0xffffffff)
+               tsleep(ioctl_cmd, 0, "ips", hz / 10);
+       if (COMMAND_ERROR(&ioctl_cmd->status))
+               error = EIO;
+       else
+               error = 0;
+       if (copyout(ioctl_cmd->data_buffer, user_request->data_buffer,
+           ioctl_cmd->datasize))
+               error = EINVAL;
+exit:
+       bus_dmamem_free(ioctl_cmd->dmatag, ioctl_cmd->data_buffer,
+           ioctl_cmd->dmamap);
+       bus_dma_tag_destroy(ioctl_cmd->dmatag);
+       return error;
+}
+
+int
+ips_ioctl_request(ips_softc_t *sc, u_long ioctl_request, caddr_t addr,
+    int32_t flags)
+{
+       ips_ioctl_t *ioctl_cmd;
+       ips_user_request *user_request;
+       int error = EINVAL;
+
+       switch (ioctl_request) {
+       case IPS_USER_CMD:
+               user_request = (ips_user_request *)addr;
+               ioctl_cmd = malloc(sizeof(ips_ioctl_t), M_DEVBUF, M_WAITOK);
+               ioctl_cmd->command_buffer = malloc(sizeof(ips_generic_cmd),
+                   M_DEVBUF, M_WAITOK);
+               if (copyin(user_request->command_buffer,
+                   ioctl_cmd->command_buffer, sizeof(ips_generic_cmd))) {
+                       free(ioctl_cmd->command_buffer, M_DEVBUF);
+                       free(ioctl_cmd, M_DEVBUF);
+                       break;
+               }
+               ioctl_cmd->readwrite = IPS_IOCTL_READ | IPS_IOCTL_WRITE;
+               ioctl_cmd->datasize = IPS_IOCTL_BUFFER_SIZE;
+               error = ips_ioctl_cmd(sc, ioctl_cmd, user_request);
+               free(ioctl_cmd->command_buffer, M_DEVBUF);
+               free(ioctl_cmd, M_DEVBUF);
+               break;
+       }
+       return error;
+}
diff --git a/sys/dev/raid/ips/ips_ioctl.h b/sys/dev/raid/ips/ips_ioctl.h
new file mode 100644 (file)
index 0000000..155eb26
--- /dev/null
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Written by: David Jeffery
+ *
+ * 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: src/sys/dev/ips/ips_ioctl.h,v 1.1 2003/05/11 06:36:49 scottl Exp $
+ * $DragonFly: src/sys/dev/raid/ips/ips_ioctl.h,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+ */
+
+
+#include <sys/ioccom.h>
+
+#define IPS_USER_CMD _IOWR(0x81, 0x21, ips_user_request)
+
+#define IPS_IOCTL_READ 1
+#define IPS_IOCTL_WRITE 2
+
+#define IPS_REBUILD_STAT_SIZE 116
+#define IPS_SUBSYS_PARAM_SIZE 128
+#define IPS_RW_NVRAM_SIZE 128
+
+#define IPS_IOCTL_BUFFER_SIZE 4096
+
+
+typedef struct ips_ioctl {
+       ips_generic_cmd         *command_buffer;
+       void                    *data_buffer;
+       ips_cmd_status_t        status;
+       int                     datasize;
+       int                     readwrite;
+       bus_dma_tag_t           dmatag;
+       bus_dmamap_t            dmamap;
+} ips_ioctl_t;
+
+typedef struct ips_user_request {
+       void            *command_buffer;
+       void            *data_buffer;
+       u_int32_t       status;
+} ips_user_request;
+
diff --git a/sys/dev/raid/ips/ips_pci.c b/sys/dev/raid/ips/ips_pci.c
new file mode 100644 (file)
index 0000000..9cdde90
--- /dev/null
@@ -0,0 +1,239 @@
+/*-
+ * Copyright (c) 2002 Adaptec Inc.
+ * All rights reserved.
+ *
+ * Written by: David Jeffery
+ *
+ * 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: src/sys/dev/ips/ips_pci.c,v 1.7 2003/09/11 23:30:28 ps 
+ * $DragonFly: src/sys/dev/raid/ips/ips_pci.c,v 1.1 2004/01/15 15:41:23 drhodus Exp $
+ */
+
+#include <sys/cdefs.h>
+
+#include <dev/raid/ips/ips.h>
+
+static int ips_pci_free(ips_softc_t *sc);
+static void ips_intrhook(void *arg);
+
+static int
+ips_pci_probe(device_t dev)
+{
+
+       if ((pci_get_vendor(dev) == IPS_VENDOR_ID) &&
+           (pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID)) {
+               device_set_desc(dev, "IBM ServeRAID Adapter");
+               return 0;
+       } else if ((pci_get_vendor(dev) == IPS_VENDOR_ID) &&
+           (pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID)) {
+               device_set_desc(dev, "IBM ServeRAID Adapter");
+               return (0);
+       }
+       return (ENXIO);
+}
+
+static int
+resource_disabled(const char *name, int unit)
+{
+       int error, value;
+
+       error = resource_int_value(name, unit, "disabled", &value);
+       if (error)
+               return (0);
+       return (value);
+}
+
+static int
+ips_pci_attach(device_t dev)
+{
+       u_int32_t command;
+       ips_softc_t *sc;
+
+       if (resource_disabled(device_get_name(dev), device_get_unit(dev))) {
+               device_printf(dev, "device is disabled\n");
+               /* but return 0 so the !$)$)*!$*) unit isn't reused */
+               return (0);
+       }
+       DEVICE_PRINTF(1, dev, "in attach.\n");
+       sc = (ips_softc_t *)device_get_softc(dev);
+       if (sc == NULL) {
+               printf("how is sc NULL?!\n");
+               return (ENXIO);
+       }
+       bzero(sc, sizeof(ips_softc_t));
+       sc->dev = dev;
+       if (pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID) {
+               sc->ips_adapter_reinit = ips_morpheus_reinit;
+               sc->ips_adapter_intr = ips_morpheus_intr;
+               sc->ips_issue_cmd    = ips_issue_morpheus_cmd;
+       } else if (pci_get_device(dev) == IPS_COPPERHEAD_DEVICE_ID) {
+               sc->ips_adapter_reinit = ips_copperhead_reinit;
+               sc->ips_adapter_intr = ips_copperhead_intr;
+               sc->ips_issue_cmd    = ips_issue_copperhead_cmd;
+       } else
+               goto error;
+       /* make sure busmastering is on */
+       command = pci_read_config(dev, PCIR_COMMAND, 1);
+       command |= PCIM_CMD_BUSMASTEREN;
+       pci_write_config(dev, PCIR_COMMAND, command, 1);
+       /* seting up io space */
+       sc->iores = NULL;
+       if (command & PCIM_CMD_MEMEN) {
+               PRINTF(10, "trying MEMIO\n");
+               if (pci_get_device(dev) == IPS_MORPHEUS_DEVICE_ID)
+                       sc->rid = PCIR_BAR(0);
+               else
+                       sc->rid = PCIR_BAR(1);
+               sc->iotype = SYS_RES_MEMORY;
+               sc->iores = bus_alloc_resource(dev, sc->iotype, &sc->rid, 0,
+                   ~0, 1, RF_ACTIVE);
+       }
+       if (sc->iores == NULL && command & PCIM_CMD_PORTEN) {
+               PRINTF(10, "trying PORTIO\n");
+               sc->rid = PCIR_BAR(0);
+               sc->iotype = SYS_RES_IOPORT;
+               sc->iores = bus_alloc_resource(dev, sc->iotype, &sc->rid, 0,
+                   ~0, 1, RF_ACTIVE);
+       }
+       if (sc->iores == NULL) {
+               device_printf(dev, "resource allocation failed\n");
+               return (ENXIO);
+       }
+       sc->bustag = rman_get_bustag(sc->iores);
+       sc->bushandle = rman_get_bushandle(sc->iores);
+       /*
+        * allocate an interrupt. when does the irq become active?
+        * after leaving attach?
+        */
+       sc->irqrid = 0;
+       if ((sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqrid,
+           0, ~0, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) {
+               device_printf(dev, "irq allocation failed\n");
+               goto error;
+       }
+       if (bus_setup_intr(dev, sc->irqres, INTR_TYPE_BIO,
+           sc->ips_adapter_intr, sc, &sc->irqcookie)) {
+               device_printf(dev, "irq setup failed\n");
+               goto error;
+       }
+       if (bus_dma_tag_create( /* parent    */ NULL,
+                               /* alignemnt */ 1,
+                               /* boundary  */ 0,
+                               /* lowaddr   */ BUS_SPACE_MAXADDR_32BIT,
+                               /* highaddr  */ BUS_SPACE_MAXADDR,
+                               /* filter    */ NULL,
+                               /* filterarg */ NULL,
+                               /* maxsize   */ BUS_SPACE_MAXSIZE_32BIT,
+                               /* numsegs   */ IPS_MAX_SG_ELEMENTS,
+                               /* maxsegsize*/ BUS_SPACE_MAXSIZE_32BIT,
+                               /* flags     */ 0,
+                               &sc->adapter_dmatag) != 0) {
+               printf("IPS can't alloc dma tag\n");
+               goto error;
+       }
+       sc->ips_ich.ich_func = ips_intrhook;
+       sc->ips_ich.ich_arg = sc;
+       if (config_intrhook_establish(&sc->ips_ich) != 0) {
+               printf("IPS can't establish configuration hook\n");
+               goto error;
+       }
+       return 0;
+error:
+       ips_pci_free(sc);
+       return (ENXIO);
+}
+
+static void
+ips_intrhook(void *arg)
+{
+       struct ips_softc *sc = arg;
+
+       config_intrhook_disestablish(&sc->ips_ich);
+       if (ips_adapter_init(sc))
+               ips_pci_free(sc);
+       else
+               sc->configured = 1;
+}
+
+static int
+ips_pci_free(ips_softc_t *sc)
+{
+
+       if (sc->adapter_dmatag)
+               bus_dma_tag_destroy(sc->adapter_dmatag);
+       if (sc->irqcookie)
+               bus_teardown_intr(sc->dev, sc->irqres, sc->irqcookie);
+       if (sc->irqres)
+               bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqrid,
+                   sc->irqres);
+       if (sc->iores)
+               bus_release_resource(sc->dev, sc->iotype, sc->rid, sc->iores);
+       sc->configured = 0;
+       return 0;
+}
+
+static int
+ips_pci_detach(device_t dev)
+{
+       ips_softc_t *sc;
+
+       DEVICE_PRINTF(1, dev, "detaching ServeRaid\n");
+       sc = (ips_softc_t *)device_get_softc(dev);
+       if (sc->configured) {
+               sc->configured = 0;
+               ips_flush_cache(sc);
+               if (ips_adapter_free(sc))
+                       return EBUSY;
+               ips_pci_free(sc);
+               IPS_LOCK_FREE(sc);
+       }
+       return 0;
+}
+
+static int
+ips_pci_shutdown(device_t dev)
+{
+       ips_softc_t *sc;
+
+       sc = (ips_softc_t *)device_get_softc(dev);
+       if (sc->configured)
+               ips_flush_cache(sc);
+       return 0;
+}
+
+static device_method_t ips_driver_methods[] = {
+       DEVMETHOD(device_probe, ips_pci_probe),
+       DEVMETHOD(device_attach, ips_pci_attach),
+       DEVMETHOD(device_detach, ips_pci_detach),
+       DEVMETHOD(device_shutdown, ips_pci_shutdown),
+       { 0, 0 }
+};
+
+static driver_t ips_pci_driver = {
+       "ips",
+       ips_driver_methods,
+       sizeof(ips_softc_t),
+};
+
+static devclass_t ips_devclass;
+DRIVER_MODULE(ips, pci, ips_pci_driver, ips_devclass, 0, 0);