amr(4): Sync with FreeBSD.
authorSascha Wildner <saw@online.de>
Fri, 3 Dec 2010 13:34:47 +0000 (14:34 +0100)
committerSascha Wildner <saw@online.de>
Fri, 3 Dec 2010 13:35:39 +0000 (14:35 +0100)
This adds support for various new devices (full list is in the manual
page), among other things.

Reported-by: Tomaz Borstnar <tomaz.borstnar@over.net>
               Jonas Trollvik <jontro@gmail.com>
Dragonfly-bug: <http://bugs.dragonflybsd.org/issue305>

20 files changed:
share/man/man4/amr.4
sys/conf/files
sys/config/GENERIC
sys/config/GENERIC_SMP
sys/config/LINT
sys/config/X86_64_GENERIC
sys/config/X86_64_GENERIC_SMP
sys/dev/raid/amr/Makefile
sys/dev/raid/amr/amr.c
sys/dev/raid/amr/amr_cam.c
sys/dev/raid/amr/amr_cam/Makefile [new file with mode: 0644]
sys/dev/raid/amr/amr_compat.h [deleted file]
sys/dev/raid/amr/amr_disk.c
sys/dev/raid/amr/amr_linux.c [new file with mode: 0644]
sys/dev/raid/amr/amr_linux/Makefile [new file with mode: 0644]
sys/dev/raid/amr/amr_pci.c
sys/dev/raid/amr/amr_tables.h
sys/dev/raid/amr/amrio.h
sys/dev/raid/amr/amrreg.h
sys/dev/raid/amr/amrvar.h

index a0eb9f3..000be24 100644 (file)
 .\" (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/amr.4,v 1.3.2.10 2003/05/26 12:41:05 hmp Exp $
+.\" $FreeBSD: src/share/man/man4/amr.4,v 1.35 2006/06/18 14:45:28 brueffer Exp $
 .\"
-.Dd November 30, 2010
+.Dd December 3, 2010
 .Dt AMR 4
 .Os
 .Sh NAME
 .Nm amr
-.Nd AMI MegaRAID PCI-SCSI RAID driver
+.Nd MegaRAID SCSI/ATA/SATA RAID driver
 .Sh SYNOPSIS
-.Cd device pci
-.Cd device amr
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device pci"
+.Cd "device scbus"
+.Cd "device amr"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+amr_load="YES"
+.Ed
 .Sh DESCRIPTION
 The
 .Nm
-driver provides support for American Megatrends MegaRAID Express,
-Elite and Enterprise family SCSI RAID controllers,
-including models relabeled and sold by Hewlett-Packard and Dell.
+driver provides support for LSI Logic MegaRAID SCSI, ATA and SATA
+RAID controllers and legacy American Megatrends MegaRAID
+SCSI RAID controllers, including models relabeled
+and sold by Dell and Hewlett-Packard.
 .Pp
 LSI MegaRAID SAS controllers are supported by
 .Xr mfi 4
 and will not work with this driver.
+.Sh HARDWARE
+Controllers supported by the
+.Nm
+driver include:
 .Pp
-Supported controllers include:
-.Bl -bullet
+.Bl -bullet -compact
+.It
+MegaRAID SATA 150-4
+.It
+MegaRAID SATA 150-6
+.It
+MegaRAID SATA 300-4X
+.It
+MegaRAID SATA 300-8X
+.It
+MegaRAID SCSI 320-1E
+.It
+MegaRAID SCSI 320-2E
+.It
+MegaRAID SCSI 320-4E
 .It
-MegaRAID 320-1
+MegaRAID SCSI 320-0X
 .It
-MegaRAID 320-2
+MegaRAID SCSI 320-2X
+.It
+MegaRAID SCSI 320-4X
+.It
+MegaRAID SCSI 320-0
+.It
+MegaRAID SCSI 320-1
+.It
+MegaRAID SCSI 320-2
+.It
+MegaRAID SCSI 320-4
 .It
 MegaRAID Series 418
 .It
+MegaRAID i4 133 RAID
+.It
+MegaRAID Elite 1500 (Series 467)
+.It
+MegaRAID Elite 1600 (Series 493)
+.It
+MegaRAID Elite 1650 (Series 4xx)
+.It
 MegaRAID Enterprise 1200 (Series 428)
 .It
 MegaRAID Enterprise 1300 (Series 434)
@@ -62,10 +111,6 @@ MegaRAID Enterprise 1500 (Series 467)
 .It
 MegaRAID Enterprise 1600 (Series 471)
 .It
-MegaRAID Elite 1500 (Series 467)
-.It
-MegaRAID Elite 1600 (Series 493)
-.It
 MegaRAID Express 100 (Series 466WS)
 .It
 MegaRAID Express 200 (Series 466)
@@ -80,11 +125,35 @@ Dell PERC 2/SC
 .It
 Dell PERC 2/DC
 .It
+Dell PERC 3/DCL
+.It
+Dell PERC 3/QC
+.It
+Dell PERC 4/DC
+.It
+Dell PERC 4/IM
+.It
+Dell PERC 4/SC
+.It
+Dell PERC 4/Di
+.It
+Dell PERC 4e/DC
+.It
+Dell PERC 4e/Di
+.It
+Dell PERC 4e/Si
+.It
+Dell PERC 4ei
+.It
 HP NetRAID-1/Si
 .It
-HP NetRAID-3/Si
+HP NetRAID-3/Si (D4943A)
 .It
 HP Embedded NetRAID
+.It
+Intel RAID Controller SRCS16
+.It
+Intel RAID Controller SRCU42X
 .El
 .Sh DIAGNOSTICS
 .Ss Driver initialisation/shutdown phase
@@ -164,12 +233,9 @@ An I/O error has occurred.
 The
 .Nm
 driver was written by
-.An Mike Smith
-.Aq msmith@FreeBSD.org .
+.An Mike Smith Aq msmith@FreeBSD.org .
 .Pp
 This manual page was written by
-.An Mike Smith
-.Aq msmith@FreeBSD.org
+.An Mike Smith Aq msmith@FreeBSD.org
 and
-.An Jeroen Ruigrok van der Werven
-.Aq asmodai@FreeBSD.org .
+.An Jeroen Ruigrok van der Werven Aq asmodai@FreeBSD.org .
index 7917ed3..19fbb48 100644 (file)
@@ -193,10 +193,11 @@ dev/disk/nata/atapi-cam.c         optional natapicam
 dev/disk/nata/atapi-cd.c               optional natapicd
 dev/disk/nata/atapi-fd.c               optional natapifd
 dev/disk/nata/atapi-tape.c             optional natapist
-dev/raid/amr/amr_cam.c                 optional amr
-dev/raid/amr/amr_disk.c                        optional amr
-dev/raid/amr/amr_pci.c                 optional amr
 dev/raid/amr/amr.c                     optional amr
+dev/raid/amr/amr_cam.c                 optional amrp amr
+dev/raid/amr/amr_disk.c                        optional amr
+#dev/raid/amr/amr_linux.c              optional amr compat_linux
+dev/raid/amr/amr_pci.c                 optional amr pci
 dev/disk/aha/aha.c                     optional aha
 dev/disk/aha/aha_isa.c                 optional aha isa
 dev/disk/ahb/ahb.c                     optional ahb eisa
index ff70cfe..8f281c9 100644 (file)
@@ -167,6 +167,7 @@ device              aac             # Adaptec FSA RAID, Dell PERC2/PERC3
 device         ida             # Compaq Smart RAID
 device         ips             # IBM ServeRAID
 device         amr             # AMI MegaRAID
+device         amrp            # SCSI Passthrough interface (optional, CAM req.)
 device         mlx             # Mylex DAC960 family
 device         mfi             # LSI MegaRAID SAS
 device         mfip            # LSI MegaRAID SAS passthrough, requires CAM
index b30a139..619d777 100644 (file)
@@ -168,6 +168,7 @@ device              aac             # Adaptec FSA RAID, Dell PERC2/PERC3
 device         ida             # Compaq Smart RAID
 device         ips             # IBM ServeRAID
 device         amr             # AMI MegaRAID
+device         amrp            # SCSI Passthrough interface (optional, CAM req.)
 device         mlx             # Mylex DAC960 family
 device         mfi             # LSI MegaRAID SAS
 device         mfip            # LSI MegaRAID SAS passthrough, requires CAM
index 664aa95..fbe6f04 100644 (file)
@@ -1258,6 +1258,7 @@ device            aacp    # SCSI Passthrough interface (optional, CAM required)
 device         ida             # Compaq Smart RAID
 device         mlx             # Mylex DAC960
 device         amr             # AMI MegaRAID
+device         amrp            # SCSI Passthrough interface (optional, CAM req.)
 device         mfi             # LSI MegaRAID SAS
 device         mfip            # LSI MegaRAID SAS passthrough, requires CAM
 options        MFI_DEBUG
index dcf7f50..a66a733 100644 (file)
@@ -153,6 +153,7 @@ device              aac             # Adaptec FSA RAID, Dell PERC2/PERC3
 device         ida             # Compaq Smart RAID
 device         ips             # IBM ServeRAID
 device         amr             # AMI MegaRAID
+device         amrp            # SCSI Passthrough interface (optional, CAM req.)
 device         mlx             # Mylex DAC960 family
 device         mfi             # LSI MegaRAID SAS
 device         mfip            # LSI MegaRAID SAS passthrough, requires CAM
index d78c013..6c3ffb8 100644 (file)
@@ -153,6 +153,7 @@ device              aac             # Adaptec FSA RAID, Dell PERC2/PERC3
 device         ida             # Compaq Smart RAID
 device         ips             # IBM ServeRAID
 device         amr             # AMI MegaRAID
+device         amrp            # SCSI Passthrough interface (optional, CAM req.)
 device         mlx             # Mylex DAC960 family
 device         mfi             # LSI MegaRAID SAS
 device         mfip            # LSI MegaRAID SAS passthrough, requires CAM
index 88c30c9..802cd5e 100644 (file)
@@ -1,16 +1,17 @@
-# $FreeBSD: src/sys/modules/amr/Makefile,v 1.4.2.2 2002/10/28 21:10:06 emoore Exp $
+# $FreeBSD: src/sys/modules/amr/Makefile,v 1.14 2010/08/23 06:13:29 imp Exp $
 
-KMOD           = amr
-SRCS           = amr.c amr_pci.c amr_disk.c device_if.h bus_if.h pci_if.h
+SUBDIR= amr_cam
+#.if ${MACHINE_ARCH} == "i386" || ${MACHINE_ARCH} == "amd64"
+#SUBDIR+= amr_linux
+#.endif
 
-# SCSI passthrough support for non-disk devices
-#CFLAGS                += -DAMR_SCSI_PASSTHROUGH
-SRCS           += amr_cam.c opt_cam.h opt_scsi.h
+KMOD=  amr
+SRCS=  amr.c amr_pci.c amr_disk.c device_if.h bus_if.h pci_if.h
 
 # Enable a questionable optimisation for newer adapters
-#CFLAGS                += -DAMR_QUARTZ_GOFASTER
+#CFLAGS+= -DAMR_QUARTZ_GOFASTER
 
 # Debugging
-#CFLAGS                += -DAMR_DEBUG=3
+#CFLAGS+= -DAMR_DEBUG=3
 
 .include <bsd.kmod.mk>
index d607276..d9ae28a 100644 (file)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 1999,2000 Michael Smith
  * Copyright (c) 2000 BSDi
+ * Copyright (c) 2005 Scott Long
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
+ */
+/*-
  * Copyright (c) 2002 Eric Moore
- * Copyright (c) 2002 LSI Logic Corporation
+ * Copyright (c) 2002, 2004 LSI Logic Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -52,7 +54,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     $FreeBSD: src/sys/dev/amr/amr.c,v 1.7.2.13 2003/01/15 13:41:18 emoore Exp $
+ * $FreeBSD: src/sys/dev/amr/amr.c,v 1.95 2010/01/07 21:01:37 mbr Exp $
  */
 
 /*
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/sysmsg.h>
 
-#include "amr_compat.h"
+#include <sys/bio.h>
 #include <sys/bus.h>
 #include <sys/conf.h>
-#include <sys/devicestat.h>
-#include <sys/disk.h>
 #include <sys/stat.h>
+
+#include <machine/cpu.h>
 #include <sys/rman.h>
 
 #include <bus/pci/pcireg.h>
 #include <bus/pci/pcivar.h>
 
-#include "amrio.h"
-#include "amrreg.h"
-#include "amrvar.h"
+#include <dev/raid/amr/amrio.h>
+#include <dev/raid/amr/amrreg.h>
+#include <dev/raid/amr/amrvar.h>
 #define AMR_DEFINE_TABLES
-#include "amr_tables.h"
+#include <dev/raid/amr/amr_tables.h>
+
+SYSCTL_NODE(_hw, OID_AUTO, amr, CTLFLAG_RD, 0, "AMR driver parameters");
 
 static d_open_t         amr_open;
 static d_close_t        amr_close;
@@ -89,9 +96,10 @@ static struct dev_ops amr_ops = {
        { "amr", 0, 0 },
        .d_open =       amr_open,
        .d_close =      amr_close,
-       .d_ioctl =      amr_ioctl
+       .d_ioctl =      amr_ioctl,
 };
 
+int linux_no_adapter = 0;
 /*
  * Initialisation, bus interface.
  */
@@ -102,7 +110,7 @@ static void amr_startup(void *arg);
  */
 static int     amr_query_controller(struct amr_softc *sc);
 static void    *amr_enquiry(struct amr_softc *sc, size_t bufsize, 
-                            u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual);
+                            u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual, int *status);
 static void    amr_completeio(struct amr_command *ac);
 static int     amr_support_ext_cdb(struct amr_softc *sc);
 
@@ -116,12 +124,15 @@ static void       amr_freecmd_cluster(struct amr_command_cluster *acc);
  * Command processing.
  */
 static int     amr_bio_command(struct amr_softc *sc, struct amr_command **acp);
-static int     amr_wait_command(struct amr_command *ac);
-static int     amr_getslot(struct amr_command *ac);
-static void    amr_mapcmd(struct amr_command *ac);
+static int     amr_wait_command(struct amr_command *ac) __unused;
+static int     amr_mapcmd(struct amr_command *ac);
 static void    amr_unmapcmd(struct amr_command *ac);
 static int     amr_start(struct amr_command *ac);
-static void    amr_complete(void *context, int pending);
+static void    amr_complete(void *context, ac_qhead_t *head);
+static void    amr_setup_sg(void *arg, bus_dma_segment_t *segs, int nsegments, int error);
+static void    amr_setup_data(void *arg, bus_dma_segment_t *segs, int nsegments, int error);
+static void    amr_setup_ccb(void *arg, bus_dma_segment_t *segs, int nsegments, int error);
+static void    amr_abort_load(struct amr_command *ac);
 
 /*
  * Status monitoring
@@ -131,11 +142,12 @@ static void       amr_periodic(void *data);
 /*
  * Interface-specific shims
  */
-static int     amr_quartz_submit_command(struct amr_softc *sc);
+static int     amr_quartz_submit_command(struct amr_command *ac);
 static int     amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
 static int     amr_quartz_poll_command(struct amr_command *ac);
+static int     amr_quartz_poll_command1(struct amr_softc *sc, struct amr_command *ac);
 
-static int     amr_std_submit_command(struct amr_softc *sc);
+static int     amr_std_submit_command(struct amr_command *ac);
 static int     amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave);
 static int     amr_std_poll_command(struct amr_command *ac);
 static void    amr_std_attach_mailbox(struct amr_softc *sc);
@@ -155,7 +167,11 @@ static void        amr_printcommand(struct amr_command *ac);
 #endif
 #endif
 
-DECLARE_DUMMY_MODULE(amr);
+static void    amr_init_sysctl(struct amr_softc *sc);
+static int     amr_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t addr,
+                   int32_t flag, struct sysmsg *sm);
+
+MALLOC_DEFINE(M_AMR, "amr", "AMR memory");
 
 /********************************************************************************
  ********************************************************************************
@@ -175,25 +191,18 @@ DECLARE_DUMMY_MODULE(amr);
 int
 amr_attach(struct amr_softc *sc)
 {
+    device_t child;
 
     debug_called(1);
 
     /*
      * Initialise per-controller queues.
      */
-    TAILQ_INIT(&sc->amr_completed);
-    TAILQ_INIT(&sc->amr_freecmds);
+    amr_init_qhead(&sc->amr_freecmds);
+    amr_init_qhead(&sc->amr_ready);
     TAILQ_INIT(&sc->amr_cmd_clusters);
-    TAILQ_INIT(&sc->amr_ready);
     bioq_init(&sc->amr_bioq);
 
-#if defined(__FreeBSD__) && __FreeBSD_version >= 500005
-    /*
-     * Initialise command-completion task.
-     */
-    TASK_INIT(&sc->amr_task_complete, 0, amr_complete, sc);
-#endif
-
     debug(2, "queue init done");
 
     /*
@@ -203,6 +212,7 @@ amr_attach(struct amr_softc *sc)
        sc->amr_submit_command = amr_quartz_submit_command;
        sc->amr_get_work       = amr_quartz_get_work;
        sc->amr_poll_command   = amr_quartz_poll_command;
+       sc->amr_poll_command1  = amr_quartz_poll_command1;
     } else {
        sc->amr_submit_command = amr_std_submit_command;
        sc->amr_get_work       = amr_std_get_work;
@@ -211,11 +221,16 @@ amr_attach(struct amr_softc *sc)
     }
 
 #ifdef AMR_BOARD_INIT
-    if ((AMR_IS_QUARTZ(sc) ? amr_quartz_init(sc) : amr_std_init(sc))))
+    if ((AMR_IS_QUARTZ(sc) ? amr_quartz_init(sc) : amr_std_init(sc)))
        return(ENXIO);
 #endif
 
     /*
+     * Allocate initial commands.
+     */
+    amr_alloccmd_cluster(sc);
+
+    /*
      * Quiz controller for features and limits.
      */
     if (amr_query_controller(sc))
@@ -224,20 +239,44 @@ amr_attach(struct amr_softc *sc)
     debug(2, "controller query complete");
 
     /*
+     * preallocate the remaining commands.
+     */
+    while (sc->amr_nextslot < sc->amr_maxio)
+       amr_alloccmd_cluster(sc);
+
+    /*
+     * Setup sysctls.
+     */
+    sysctl_ctx_init(&sc->amr_sysctl_ctx);
+    sc->amr_sysctl_tree = SYSCTL_ADD_NODE(&sc->amr_sysctl_ctx,
+       SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
+       device_get_nameunit(sc->amr_dev), CTLFLAG_RD, 0, "");
+    if (sc->amr_sysctl_tree == NULL) {
+       device_printf(sc->amr_dev, "can't add sysctl node\n");
+       return (EINVAL);
+    }
+    amr_init_sysctl(sc);
+
+    /*
      * Attach our 'real' SCSI channels to CAM.
      */
-    if (amr_cam_attach(sc))
-       return(ENXIO);
-    debug(2, "CAM attach done");
+    child = device_add_child(sc->amr_dev, "amrp", -1);
+    sc->amr_pass = child;
+    if (child != NULL) {
+       device_set_softc(child, sc);
+       device_set_desc(child, "SCSI Passthrough Bus");
+       bus_generic_attach(sc->amr_dev);
+    }
 
     /*
      * Create the control device.
      */
-    sc->amr_dev_t = make_dev(&amr_ops, device_get_unit(sc->amr_dev), 
-                           UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR,
-                           "amr%d", device_get_unit(sc->amr_dev));
+    sc->amr_dev_t = make_dev(&amr_ops, device_get_unit(sc->amr_dev), UID_ROOT, GID_OPERATOR,
+                            S_IRUSR | S_IWUSR, "amr%d", device_get_unit(sc->amr_dev));
     sc->amr_dev_t->si_drv1 = sc;
-    reference_dev(sc->amr_dev_t);
+    linux_no_adapter++;
+    if (device_get_unit(sc->amr_dev) == 0)
+       make_dev_alias(sc->amr_dev_t, "megadev0");
 
     /*
      * Schedule ourselves to bring the controller up once interrupts are
@@ -246,7 +285,6 @@ amr_attach(struct amr_softc *sc)
     bzero(&sc->amr_ich, sizeof(struct intr_config_hook));
     sc->amr_ich.ich_func = amr_startup;
     sc->amr_ich.ich_arg = sc;
-    sc->amr_ich.ich_desc = "amr";
     if (config_intrhook_establish(&sc->amr_ich) != 0) {
        device_printf(sc->amr_dev, "can't establish configuration hook\n");
        return(ENOMEM);
@@ -275,7 +313,9 @@ amr_startup(void *arg)
     callout_init(&sc->amr_timeout);
 
     /* pull ourselves off the intrhook chain */
-    config_intrhook_disestablish(&sc->amr_ich);
+    if (sc->amr_ich.ich_func)
+       config_intrhook_disestablish(&sc->amr_ich);
+    sc->amr_ich.ich_func = NULL;
 
     /* get up-to-date drive information */
     if (amr_query_controller(sc)) {
@@ -316,11 +356,34 @@ amr_startup(void *arg)
     /*
      * Start the timeout routine.
      */
-/*    callout_reset(&sc->amr_timeout, hz, amr_periodic, sc); */
+/*    sc->amr_timeout = timeout(amr_periodic, sc, hz);*/
 
     return;
 }
 
+static void
+amr_init_sysctl(struct amr_softc *sc)
+{
+
+    SYSCTL_ADD_INT(&sc->amr_sysctl_ctx,
+       SYSCTL_CHILDREN(sc->amr_sysctl_tree),
+       OID_AUTO, "allow_volume_configure", CTLFLAG_RW, &sc->amr_allow_vol_config, 0,
+       "");
+    SYSCTL_ADD_INT(&sc->amr_sysctl_ctx,
+       SYSCTL_CHILDREN(sc->amr_sysctl_tree),
+       OID_AUTO, "nextslot", CTLFLAG_RD, &sc->amr_nextslot, 0,
+       "");
+    SYSCTL_ADD_INT(&sc->amr_sysctl_ctx,
+       SYSCTL_CHILDREN(sc->amr_sysctl_tree),
+       OID_AUTO, "busyslots", CTLFLAG_RD, &sc->amr_busyslots, 0,
+       "");
+    SYSCTL_ADD_INT(&sc->amr_sysctl_ctx,
+       SYSCTL_CHILDREN(sc->amr_sysctl_tree),
+       OID_AUTO, "maxio", CTLFLAG_RD, &sc->amr_maxio, 0,
+       "");
+}
+
+
 /*******************************************************************************
  * Free resources associated with a controller instance
  */
@@ -330,7 +393,8 @@ amr_free(struct amr_softc *sc)
     struct amr_command_cluster *acc;
 
     /* detach from CAM */
-    amr_cam_detach(sc);
+    if (sc->amr_pass != NULL)
+       device_delete_child(sc->amr_dev, sc->amr_pass);
 
     /* cancel status timeout */
     callout_stop(&sc->amr_timeout);
@@ -342,9 +406,23 @@ amr_free(struct amr_softc *sc)
     }
 
     /* destroy control device */
-    if( sc->amr_dev_t != (cdev_t)NULL)
+    if(sc->amr_dev_t != (struct cdev *)NULL)
            destroy_dev(sc->amr_dev_t);
     dev_ops_remove_minor(&amr_ops, device_get_unit(sc->amr_dev));
+
+#if 0 /* XXX swildner */
+    if (mtx_initialized(&sc->amr_hw_lock))
+       mtx_destroy(&sc->amr_hw_lock);
+
+    if (mtx_initialized(&sc->amr_list_lock))
+       mtx_destroy(&sc->amr_list_lock);
+#endif
+
+    if (sc->amr_sysctl_tree != NULL)
+           sysctl_ctx_free(&sc->amr_sysctl_ctx);
+
+    lockuninit(&sc->amr_hw_lock);
+    lockuninit(&sc->amr_list_lock);
 }
 
 /*******************************************************************************
@@ -356,8 +434,10 @@ amr_submit_bio(struct amr_softc *sc, struct bio *bio)
 {
     debug_called(2);
 
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
     amr_enqueue_bio(sc, bio);
     amr_startio(sc);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
     return(0);
 }
 
@@ -367,7 +447,7 @@ amr_submit_bio(struct amr_softc *sc, struct bio *bio)
 static int
 amr_open(struct dev_open_args *ap)
 {
-    cdev_t dev = ap->a_head.a_dev;
+    cdev_t             dev = ap->a_head.a_dev;
     int                        unit = minor(dev);
     struct amr_softc   *sc = devclass_get_softc(devclass_find("amr"), unit);
 
@@ -377,13 +457,54 @@ amr_open(struct dev_open_args *ap)
     return(0);
 }
 
+#ifdef LSI
+static int
+amr_del_ld(struct amr_softc *sc, int drv_no, int status)
+{
+
+    debug_called(1);
+
+    sc->amr_state &= ~AMR_STATE_QUEUE_FRZN;
+    sc->amr_state &= ~AMR_STATE_LD_DELETE;
+    sc->amr_state |= AMR_STATE_REMAP_LD;
+    debug(1, "State Set");
+
+    if (!status) {
+       debug(1, "disk begin destroyed %d",drv_no);
+       if (--amr_disks_registered == 0)
+           cdevsw_remove(&amrddisk_cdevsw);
+       debug(1, "disk begin destroyed success");
+    }
+    return 0;
+}
+
+static int
+amr_prepare_ld_delete(struct amr_softc *sc)
+{
+
+    debug_called(1);
+    if (sc->ld_del_supported == 0)
+       return(ENOIOCTL);
+
+    sc->amr_state |= AMR_STATE_QUEUE_FRZN;
+    sc->amr_state |= AMR_STATE_LD_DELETE;
+
+    /* 5 minutes for the all the commands to be flushed.*/
+    tsleep((void *)&sc->ld_del_supported, PCATCH,"delete_logical_drv",hz * 60 * 1);
+    if ( sc->amr_busyslots )
+       return(ENOIOCTL);
+
+    return 0;
+}
+#endif
+
 /********************************************************************************
  * Accept the last close on the control device.
  */
 static int
 amr_close(struct dev_close_args *ap)
 {
-    cdev_t dev = ap->a_head.a_dev;
+    cdev_t             dev = ap->a_head.a_dev;
     int                        unit = minor(dev);
     struct amr_softc   *sc = devclass_get_softc(devclass_find("amr"), unit);
 
@@ -396,124 +517,445 @@ amr_close(struct dev_close_args *ap)
 /********************************************************************************
  * Handle controller-specific control operations.
  */
+static void
+amr_rescan_drives(struct cdev *dev)
+{
+    struct amr_softc   *sc = (struct amr_softc *)dev->si_drv1;
+    int                        i, error = 0;
+
+    sc->amr_state |= AMR_STATE_REMAP_LD;
+    while (sc->amr_busyslots) {
+       device_printf(sc->amr_dev, "idle controller\n");
+       amr_done(sc);
+    }
+
+    /* mark ourselves as in-shutdown */
+    sc->amr_state |= AMR_STATE_SHUTDOWN;
+
+    /* flush controller */
+    device_printf(sc->amr_dev, "flushing cache...");
+    kprintf("%s\n", amr_flush(sc) ? "failed" : "done");
+
+    /* delete all our child devices */
+    for(i = 0 ; i < AMR_MAXLD; i++) {
+       if(sc->amr_drive[i].al_disk != 0) {
+           if((error = device_delete_child(sc->amr_dev,
+               sc->amr_drive[i].al_disk)) != 0)
+               goto shutdown_out;
+
+            sc->amr_drive[i].al_disk = 0;
+       }
+    }
+
+shutdown_out:
+    amr_startup(sc);
+}
+
+int
+amr_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag,
+    struct sysmsg *sm)
+{
+    struct amr_softc           *sc = (struct amr_softc *)dev->si_drv1;
+    struct amr_command         *ac;
+    struct amr_mailbox         *mb;
+    struct amr_linux_ioctl     ali;
+    void                       *dp, *temp;
+    int                                error;
+    int                                adapter, len, ac_flags = 0;
+    int                                logical_drives_changed = 0;
+    u_int32_t                  linux_version = 0x02100000;
+    u_int8_t                   status;
+    struct amr_passthrough     *ap;    /* 60 bytes */
+
+    error = 0;
+    dp = NULL;
+    ac = NULL;
+    ap = NULL;
+
+    if ((error = copyin(addr, &ali, sizeof(ali))) != 0)
+       return (error);
+    switch (ali.ui.fcs.opcode) {
+    case 0x82:
+       switch(ali.ui.fcs.subopcode) {
+       case 'e':
+           copyout(&linux_version, (void *)(uintptr_t)ali.data,
+               sizeof(linux_version));
+           error = 0;
+           break;
+
+       case 'm':
+           copyout(&linux_no_adapter, (void *)(uintptr_t)ali.data,
+               sizeof(linux_no_adapter));
+           sm->sm_result.iresult = linux_no_adapter;
+           error = 0;
+           break;
+
+       default:
+           kprintf("Unknown subopcode\n");
+           error = ENOIOCTL;
+           break;
+       }
+    break;
+
+    case 0x80:
+    case 0x81:
+       if (ali.ui.fcs.opcode == 0x80)
+           len = max(ali.outlen, ali.inlen);
+       else
+           len = ali.ui.fcs.length;
+
+       adapter = (ali.ui.fcs.adapno) ^ 'm' << 8;
+
+       mb = (void *)&ali.mbox[0];
+
+       if ((ali.mbox[0] == FC_DEL_LOGDRV  && ali.mbox[2] == OP_DEL_LOGDRV) ||  /* delete */
+           (ali.mbox[0] == AMR_CMD_CONFIG && ali.mbox[2] == 0x0d)) {           /* create */
+           if (sc->amr_allow_vol_config == 0) {
+               error = EPERM;
+               break;
+           }
+           logical_drives_changed = 1;
+       }
+
+       if (ali.mbox[0] == AMR_CMD_PASS) {
+           lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+           while ((ac = amr_alloccmd(sc)) == NULL)
+               lksleep(sc, &sc->amr_list_lock, 0, "amrioc", hz);
+           lockmgr(&sc->amr_list_lock, LK_RELEASE);
+           ap = &ac->ac_ccb->ccb_pthru;
+
+           error = copyin((void *)(uintptr_t)mb->mb_physaddr, ap,
+               sizeof(struct amr_passthrough));
+           if (error)
+               break;
+
+           if (ap->ap_data_transfer_length)
+               dp = kmalloc(ap->ap_data_transfer_length, M_AMR,
+                   M_WAITOK | M_ZERO);
+
+           if (ali.inlen) {
+               error = copyin((void *)(uintptr_t)ap->ap_data_transfer_address,
+                   dp, ap->ap_data_transfer_length);
+               if (error)
+                   break;
+           }
+
+           ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT|AMR_CMD_CCB;
+           bzero(&ac->ac_mailbox, sizeof(ac->ac_mailbox));
+           ac->ac_mailbox.mb_command = AMR_CMD_PASS;
+           ac->ac_flags = ac_flags;
+
+           ac->ac_data = dp;
+           ac->ac_length = ap->ap_data_transfer_length;
+           temp = (void *)(uintptr_t)ap->ap_data_transfer_address;
+
+           lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+           error = amr_wait_command(ac);
+           lockmgr(&sc->amr_list_lock, LK_RELEASE);
+           if (error)
+               break;
+
+           status = ac->ac_status;
+           error = copyout(&status, &((struct amr_passthrough *)(uintptr_t)mb->mb_physaddr)->ap_scsi_status, sizeof(status));
+           if (error)
+               break;
+
+           if (ali.outlen) {
+               error = copyout(dp, temp, ap->ap_data_transfer_length);
+               if (error)
+                   break;
+           }
+           error = copyout(ap->ap_request_sense_area, ((struct amr_passthrough *)(uintptr_t)mb->mb_physaddr)->ap_request_sense_area, ap->ap_request_sense_length);
+           if (error)
+               break;
+
+           error = 0;
+           break;
+       } else if (ali.mbox[0] == AMR_CMD_PASS_64) {
+           kprintf("No AMR_CMD_PASS_64\n");
+           error = ENOIOCTL;
+           break;
+       } else if (ali.mbox[0] == AMR_CMD_EXTPASS) {
+           kprintf("No AMR_CMD_EXTPASS\n");
+           error = ENOIOCTL;
+           break;
+       } else {
+           /*
+            * Bug-for-bug compatibility with Linux!
+            * Some apps will send commands with inlen and outlen set to 0,
+            * even though they expect data to be transfered to them from the
+            * card.  Linux accidentally allows this by allocating a 4KB
+            * buffer for the transfer anyways, but it then throws it away
+            * without copying it back to the app.
+            */
+           if (!len)
+               len = 4096;
+
+           dp = kmalloc(len, M_AMR, M_WAITOK | M_ZERO);
+
+           if (ali.inlen) {
+               error = copyin((void *)(uintptr_t)mb->mb_physaddr, dp, len);
+               if (error)
+                   break;
+           }
+
+           lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+           while ((ac = amr_alloccmd(sc)) == NULL)
+               lksleep(sc, &sc->amr_list_lock, 0, "amrioc", hz);
+
+           ac_flags = AMR_CMD_DATAIN|AMR_CMD_DATAOUT;
+           bzero(&ac->ac_mailbox, sizeof(ac->ac_mailbox));
+           bcopy(&ali.mbox[0], &ac->ac_mailbox, sizeof(ali.mbox));
+
+           ac->ac_length = len;
+           ac->ac_data = dp;
+           ac->ac_flags = ac_flags;
+
+           error = amr_wait_command(ac);
+           lockmgr(&sc->amr_list_lock, LK_RELEASE);
+           if (error)
+               break;
+
+           status = ac->ac_status;
+           error = copyout(&status, &((struct amr_mailbox *)&((struct amr_linux_ioctl *)addr)->mbox[0])->mb_status, sizeof(status));
+           if (ali.outlen) {
+               error = copyout(dp, (void *)(uintptr_t)mb->mb_physaddr, len);
+               if (error)
+                   break;
+           }
+
+           error = 0;
+           if (logical_drives_changed)
+               amr_rescan_drives(dev);
+           break;
+       }
+       break;
+
+    default:
+       debug(1, "unknown linux ioctl 0x%lx", cmd);
+       kprintf("unknown linux ioctl 0x%lx\n", cmd);
+       error = ENOIOCTL;
+       break;
+    }
+
+    /*
+     * At this point, we know that there is a lock held and that these
+     * objects have been allocated.
+     */
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+    if (ac != NULL)
+       amr_releasecmd(ac);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
+    if (dp != NULL)
+       kfree(dp, M_AMR);
+    return(error);
+}
+
 static int
 amr_ioctl(struct dev_ioctl_args *ap)
 {
-    cdev_t dev = ap->a_head.a_dev;
+    cdev_t                     dev = ap->a_head.a_dev;
+    caddr_t                    addr = ap->a_data;
+    u_long                     cmd = ap->a_cmd;
     struct amr_softc           *sc = (struct amr_softc *)dev->si_drv1;
-    int                                *arg = (int *)ap->a_data;
-    struct amr_user_ioctl      *au = (struct amr_user_ioctl *)ap->a_data;
+    union {
+       void                    *_p;
+       struct amr_user_ioctl   *au;
+#ifdef AMR_IO_COMMAND32
+       struct amr_user_ioctl32 *au32;
+#endif
+       int                     *result;
+    } arg;
     struct amr_command         *ac;
     struct amr_mailbox_ioctl   *mbi;
-    struct amr_passthrough     *apt;
-    void                       *dp;
+    void                       *dp, *au_buffer;
+    unsigned long              au_length;
+    unsigned char              *au_cmd;
+    int                                *au_statusp, au_direction;
     int                                error;
+    struct amr_passthrough     *_ap;   /* 60 bytes */
+    int                                logical_drives_changed = 0;
 
     debug_called(1);
 
+    arg._p = (void *)addr;
+
     error = 0;
     dp = NULL;
-    apt = NULL;
     ac = NULL;
-    switch(ap->a_cmd) {
+    _ap = NULL;
+
+    switch(cmd) {
 
     case AMR_IO_VERSION:
        debug(1, "AMR_IO_VERSION");
-       *arg = AMR_IO_VERSION_NUMBER;
+       *arg.result = AMR_IO_VERSION_NUMBER;
+       return(0);
+
+#ifdef AMR_IO_COMMAND32
+    /*
+     * Accept ioctl-s from 32-bit binaries on non-32-bit
+     * platforms, such as AMD. LSI's MEGAMGR utility is
+     * the only example known today... -mi
+     */
+    case AMR_IO_COMMAND32:
+       debug(1, "AMR_IO_COMMAND32 0x%x", arg.au32->au_cmd[0]);
+       au_cmd = arg.au32->au_cmd;
+       au_buffer = (void *)(u_int64_t)arg.au32->au_buffer;
+       au_length = arg.au32->au_length;
+       au_direction = arg.au32->au_direction;
+       au_statusp = &arg.au32->au_status;
        break;
+#endif
 
     case AMR_IO_COMMAND:
-       debug(1, "AMR_IO_COMMAND  0x%x", au->au_cmd[0]);
-       /* handle inbound data buffer */
-       if (au->au_length != 0) {
-           dp = kmalloc(au->au_length, M_DEVBUF, M_WAITOK);
-           if ((error = copyin(au->au_buffer, dp, au->au_length)) != 0)
-               break;
-           debug(2, "copyin %ld bytes from %p -> %p", au->au_length, au->au_buffer, dp);
+       debug(1, "AMR_IO_COMMAND  0x%x", arg.au->au_cmd[0]);
+       au_cmd = arg.au->au_cmd;
+       au_buffer = (void *)arg.au->au_buffer;
+       au_length = arg.au->au_length;
+       au_direction = arg.au->au_direction;
+       au_statusp = &arg.au->au_status;
+       break;
+
+    case 0xc0046d00:
+    case 0xc06e6d00:   /* Linux emulation */
+       {
+           devclass_t                  devclass;
+           struct amr_linux_ioctl      ali;
+           int                         adapter, error;
+
+           devclass = devclass_find("amr");
+           if (devclass == NULL)
+               return (ENOENT);
+
+           error = copyin(addr, &ali, sizeof(ali));
+           if (error)
+               return (error);
+           if (ali.ui.fcs.opcode == 0x82)
+               adapter = 0;
+           else
+               adapter = (ali.ui.fcs.adapno) ^ 'm' << 8;
+
+           sc = devclass_get_softc(devclass, adapter);
+           if (sc == NULL)
+               return (ENOENT);
+
+           return (amr_linux_ioctl_int(sc->amr_dev_t, cmd, addr, 0, ap->a_sysmsg));
        }
+    default:
+       debug(1, "unknown ioctl 0x%lx", cmd);
+       return(ENOIOCTL);
+    }
+
+    if ((au_cmd[0] == FC_DEL_LOGDRV && au_cmd[1] == OP_DEL_LOGDRV) ||  /* delete */
+       (au_cmd[0] == AMR_CMD_CONFIG && au_cmd[1] == 0x0d)) {           /* create */
+       if (sc->amr_allow_vol_config == 0) {
+           error = EPERM;
+           goto out;
+       }
+       logical_drives_changed = 1;
+#ifdef LSI
+       if ((error = amr_prepare_ld_delete(sc)) != 0)
+           return (error);
+#endif
+    }
 
-       if ((ac = amr_alloccmd(sc)) == NULL) {
+    /* handle inbound data buffer */
+    if (au_length != 0 && au_cmd[0] != 0x06) {
+       if ((dp = kmalloc(au_length, M_AMR, M_WAITOK|M_ZERO)) == NULL) {
            error = ENOMEM;
-           break;
+           goto out;
+       }
+       if ((error = copyin(au_buffer, dp, au_length)) != 0) {
+           kfree(dp, M_AMR);
+           return (error);
        }
+       debug(2, "copyin %ld bytes from %p -> %p", au_length, au_buffer, dp);
+    }
 
-       /* handle SCSI passthrough command */
-       if (au->au_cmd[0] == AMR_CMD_PASS) {
-           apt = kmalloc(sizeof(*apt), M_DEVBUF, M_WAITOK | M_ZERO);
-
-           /* copy cdb */
-           apt->ap_cdb_length = au->au_cmd[2];
-           bcopy(&au->au_cmd[3], &apt->ap_cdb[0], apt->ap_cdb_length);
-
-           /* build passthrough */
-           apt->ap_timeout             = au->au_cmd[apt->ap_cdb_length + 3] & 0x07;
-           apt->ap_ars                 = (au->au_cmd[apt->ap_cdb_length + 3] & 0x08) ? 1 : 0;
-           apt->ap_islogical           = (au->au_cmd[apt->ap_cdb_length + 3] & 0x80) ? 1 : 0;
-           apt->ap_logical_drive_no    = au->au_cmd[apt->ap_cdb_length + 4];
-           apt->ap_channel             = au->au_cmd[apt->ap_cdb_length + 5];
-           apt->ap_scsi_id             = au->au_cmd[apt->ap_cdb_length + 6];
-           apt->ap_request_sense_length        = 14;
-           apt->ap_data_transfer_length = au->au_length;
-           /* XXX what about the request-sense area? does the caller want it? */
-
-           /* build command */
-           ac->ac_data = apt;
-           ac->ac_length = sizeof(*apt);
-           ac->ac_flags |= AMR_CMD_DATAOUT;
-           ac->ac_ccb_data = dp;
-           ac->ac_ccb_length = au->au_length;
-           if (au->au_direction & AMR_IO_READ)
-               ac->ac_flags |= AMR_CMD_CCB_DATAIN;
-           if (au->au_direction & AMR_IO_WRITE)
-               ac->ac_flags |= AMR_CMD_CCB_DATAOUT;
+    /* Allocate this now before the mutex gets held */
 
-           ac->ac_mailbox.mb_command = AMR_CMD_PASS;
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+    while ((ac = amr_alloccmd(sc)) == NULL)
+       lksleep(sc, &sc->amr_list_lock, 0, "amrioc", hz);
 
-       } else {
-           /* direct command to controller */
-           mbi = (struct amr_mailbox_ioctl *)&ac->ac_mailbox;
+    /* handle SCSI passthrough command */
+    if (au_cmd[0] == AMR_CMD_PASS) {
+        int len;
 
-           /* copy pertinent mailbox items */
-           mbi->mb_command = au->au_cmd[0];
-           mbi->mb_channel = au->au_cmd[1];
-           mbi->mb_param = au->au_cmd[2];
-           mbi->mb_pad[0] = au->au_cmd[3];
-           mbi->mb_drive = au->au_cmd[4];
+       _ap = &ac->ac_ccb->ccb_pthru;
+       bzero(_ap, sizeof(struct amr_passthrough));
 
-           /* build the command */
-           ac->ac_data = dp;
-           ac->ac_length = au->au_length;
-           if (au->au_direction & AMR_IO_READ)
-               ac->ac_flags |= AMR_CMD_DATAIN;
-           if (au->au_direction & AMR_IO_WRITE)
-               ac->ac_flags |= AMR_CMD_DATAOUT;
-       }
+       /* copy cdb */
+        len = au_cmd[2];
+       _ap->ap_cdb_length = len;
+       bcopy(au_cmd + 3, _ap->ap_cdb, len);
 
-       /* run the command */
-       if ((error = amr_wait_command(ac)) != 0)
-           break;
+       /* build passthrough */
+       _ap->ap_timeout         = au_cmd[len + 3] & 0x07;
+       _ap->ap_ars             = (au_cmd[len + 3] & 0x08) ? 1 : 0;
+       _ap->ap_islogical       = (au_cmd[len + 3] & 0x80) ? 1 : 0;
+       _ap->ap_logical_drive_no = au_cmd[len + 4];
+       _ap->ap_channel         = au_cmd[len + 5];
+       _ap->ap_scsi_id         = au_cmd[len + 6];
+       _ap->ap_request_sense_length    = 14;
+       _ap->ap_data_transfer_length    = au_length;
+       /* XXX what about the request-sense area? does the caller want it? */
 
-       /* copy out data and set status */
-       if (au->au_length != 0)
-           error = copyout(dp, au->au_buffer, au->au_length);
-       debug(2, "copyout %ld bytes from %p -> %p", au->au_length, dp, au->au_buffer);
-       if (dp != NULL)
-           debug(2, "%16d", (int)dp);
-       au->au_status = ac->ac_status;
-       break;
+       /* build command */
+       ac->ac_mailbox.mb_command = AMR_CMD_PASS;
+       ac->ac_flags = AMR_CMD_CCB;
 
-    default:
-       debug(1, "unknown ioctl 0x%lx", cmd);
-       error = ENOIOCTL;
-       break;
+    } else {
+       /* direct command to controller */
+       mbi = (struct amr_mailbox_ioctl *)&ac->ac_mailbox;
+
+       /* copy pertinent mailbox items */
+       mbi->mb_command = au_cmd[0];
+       mbi->mb_channel = au_cmd[1];
+       mbi->mb_param = au_cmd[2];
+       mbi->mb_pad[0] = au_cmd[3];
+       mbi->mb_drive = au_cmd[4];
+       ac->ac_flags = 0;
     }
 
+    /* build the command */
+    ac->ac_data = dp;
+    ac->ac_length = au_length;
+    ac->ac_flags |= AMR_CMD_DATAIN|AMR_CMD_DATAOUT;
+
+    /* run the command */
+    error = amr_wait_command(ac);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
+    if (error)
+       goto out;
+
+    /* copy out data and set status */
+    if (au_length != 0) {
+       error = copyout(dp, au_buffer, au_length);
+    }
+    debug(2, "copyout %ld bytes from %p -> %p", au_length, dp, au_buffer);
     if (dp != NULL)
-       kfree(dp, M_DEVBUF);
-    if (apt != NULL)
-       kfree(apt, M_DEVBUF);
+       debug(2, "%p status 0x%x", dp, ac->ac_status);
+    *au_statusp = ac->ac_status;
+
+out:
+    /*
+     * At this point, we know that there is a lock held and that these
+     * objects have been allocated.
+     */
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
     if (ac != NULL)
        amr_releasecmd(ac);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
+    if (dp != NULL)
+       kfree(dp, M_AMR);
+
+#ifndef LSI
+    if (logical_drives_changed)
+       amr_rescan_drives(dev);
+#endif
+
     return(error);
 }
 
@@ -558,13 +1000,7 @@ amr_query_controller(struct amr_softc *sc)
     struct amr_prodinfo        *ap;
     struct amr_enquiry *ae;
     int                        ldrv;
-
-    /* 
-     * If we haven't found the real limit yet, let us have a couple of commands in
-     * order to be able to probe.
-     */
-    if (sc->amr_maxio == 0)
-       sc->amr_maxio = 2;
+    int                        status;
 
     /*
      * Greater than 10 byte cdb support
@@ -579,7 +1015,7 @@ amr_query_controller(struct amr_softc *sc)
      * Try to issue an ENQUIRY3 command 
      */
     if ((aex = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_ENQ3, 
-                          AMR_CONFIG_ENQ3_SOLICITED_FULL)) != NULL) {
+                          AMR_CONFIG_ENQ3_SOLICITED_FULL, &status)) != NULL) {
 
        /*
         * Fetch current state of logical drives.
@@ -591,12 +1027,12 @@ amr_query_controller(struct amr_softc *sc)
            debug(2, "  drive %d: %d state %x properties %x\n", ldrv, sc->amr_drive[ldrv].al_size,
                  sc->amr_drive[ldrv].al_state, sc->amr_drive[ldrv].al_properties);
        }
-       kfree(aex, M_DEVBUF);
+       kfree(aex, M_AMR);
 
        /*
         * Get product info for channel count.
         */
-       if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) == NULL) {
+       if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0, &status)) == NULL) {
            device_printf(sc->amr_dev, "can't obtain product data from controller\n");
            return(1);
        }
@@ -604,13 +1040,20 @@ amr_query_controller(struct amr_softc *sc)
        sc->amr_maxchan = ap->ap_nschan;
        sc->amr_maxio = ap->ap_maxio;
        sc->amr_type |= AMR_TYPE_40LD;
-       kfree(ap, M_DEVBUF);
-
+       kfree(ap, M_AMR);
+
+       ap = amr_enquiry(sc, 0, FC_DEL_LOGDRV, OP_SUP_DEL_LOGDRV, 0, &status);
+       if (ap != NULL)
+           kfree(ap, M_AMR);
+       if (!status) {
+           sc->amr_ld_del_supported = 1;
+           device_printf(sc->amr_dev, "delete logical drives supported by controller\n");
+       }
     } else {
 
        /* failed, try the 8LD ENQUIRY commands */
-       if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) == NULL) {
-           if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) == NULL) {
+       if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0, &status)) == NULL) {
+           if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0, &status)) == NULL) {
                device_printf(sc->amr_dev, "can't obtain configuration data from controller\n");
                return(1);
            }
@@ -631,7 +1074,7 @@ amr_query_controller(struct amr_softc *sc)
        sc->amr_maxdrives = 8;
        sc->amr_maxchan = ae->ae_adapter.aa_channels;
        sc->amr_maxio = ae->ae_adapter.aa_maxio;
-       kfree(ae, M_DEVBUF);
+       kfree(ae, M_AMR);
     }
 
     /*
@@ -653,7 +1096,7 @@ amr_query_controller(struct amr_softc *sc)
  * Run a generic enquiry-style command.
  */
 static void *
-amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual)
+amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub, u_int8_t cmdqual, int *status)
 {
     struct amr_command *ac;
     void               *result;
@@ -666,12 +1109,17 @@ amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub,
     result = NULL;
     
     /* get ourselves a command buffer */
-    if ((ac = amr_alloccmd(sc)) == NULL)
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+    ac = amr_alloccmd(sc);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
+    if (ac == NULL)
        goto out;
     /* allocate the response structure */
-    result = kmalloc(bufsize, M_DEVBUF, M_INTWAIT);
+    if ((result = kmalloc(bufsize, M_AMR, M_ZERO|M_NOWAIT)) == NULL)
+       goto out;
     /* set command flags */
-    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
+
+    ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAIN;
     
     /* point the command at our data */
     ac->ac_data = result;
@@ -682,17 +1130,21 @@ amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub,
     mbox[0] = cmd;
     mbox[2] = cmdsub;
     mbox[3] = cmdqual;
+    *status = 0;
 
     /* can't assume that interrupts are going to work here, so play it safe */
     if (sc->amr_poll_command(ac))
        goto out;
     error = ac->ac_status;
+    *status = ac->ac_status;
     
  out:
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
     if (ac != NULL)
        amr_releasecmd(ac);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
     if ((error != 0) && (result != NULL)) {
-       kfree(result, M_DEVBUF);
+       kfree(result, M_AMR);
        result = NULL;
     }
     return(result);
@@ -709,7 +1161,10 @@ amr_flush(struct amr_softc *sc)
 
     /* get ourselves a command buffer */
     error = 1;
-    if ((ac = amr_alloccmd(sc)) == NULL)
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+    ac = amr_alloccmd(sc);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
+    if (ac == NULL)
        goto out;
     /* set command flags */
     ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
@@ -723,8 +1178,10 @@ amr_flush(struct amr_softc *sc)
     error = ac->ac_status;
     
  out:
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
     if (ac != NULL)
        amr_releasecmd(ac);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
     return(error);
 }
 
@@ -742,7 +1199,10 @@ amr_support_ext_cdb(struct amr_softc *sc)
 
     /* get ourselves a command buffer */
     error = 0;
-    if ((ac = amr_alloccmd(sc)) == NULL)
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+    ac = amr_alloccmd(sc);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
+    if (ac == NULL)
        goto out;
     /* set command flags */
     ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
@@ -761,8 +1221,10 @@ amr_support_ext_cdb(struct amr_softc *sc)
     }
 
 out:
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
     if (ac != NULL)
        amr_releasecmd(ac);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
     return(error);
 }
 
@@ -781,6 +1243,10 @@ amr_startio(struct amr_softc *sc)
     /* spin until something prevents us from doing any work */
     for (;;) {
 
+       /* Don't bother to queue commands no bounce buffers are available. */
+       if (sc->amr_state & AMR_STATE_QUEUE_FRZN)
+           break;
+
        /* try to get a ready command */
        ac = amr_dequeue_ready(sc);
 
@@ -789,8 +1255,8 @@ amr_startio(struct amr_softc *sc)
            (void)amr_bio_command(sc, &ac);
 
        /* if that failed, build a command from a ccb */
-       if (ac == NULL)
-           (void)amr_cam_command(sc, &ac);
+       if ((ac == NULL) && (sc->amr_cam_command != NULL))
+           sc->amr_cam_command(sc, &ac);
 
        /* if we don't have anything to do, give up */
        if (ac == NULL)
@@ -811,17 +1277,23 @@ amr_startio(struct amr_softc *sc)
 static void
 amr_completeio(struct amr_command *ac)
 {
-    struct amr_softc   *sc = ac->ac_sc;
-    
+    struct amr_softc           *sc = ac->ac_sc;
+    static struct timeval      lastfail;
+    static int                 curfail;
+    struct buf                 *bp = ac->ac_bio->bio_buf;
+
     if (ac->ac_status != AMR_STATUS_SUCCESS) { /* could be more verbose here? */
-       ac->ac_bio->bio_buf->b_error = EIO;
-       ac->ac_bio->bio_buf->b_flags |= B_ERROR;
+       bp->b_error = EIO;
+       bp->b_flags |= B_ERROR;
 
-       device_printf(sc->amr_dev, "I/O error - 0x%x\n", ac->ac_status);
+       if (ppsratecheck(&lastfail, &curfail, 1))
+           device_printf(sc->amr_dev, "I/O error - 0x%x\n", ac->ac_status);
 /*     amr_printcommand(ac);*/
     }
     amrd_intr(ac->ac_bio);
+    lockmgr(&ac->ac_sc->amr_list_lock, LK_EXCLUSIVE);
     amr_releasecmd(ac);
+    lockmgr(&ac->ac_sc->amr_list_lock, LK_RELEASE);
 }
 
 /********************************************************************************
@@ -839,73 +1311,78 @@ amr_bio_command(struct amr_softc *sc, struct amr_command **acp)
     struct amr_command *ac;
     struct amrd_softc  *amrd;
     struct bio         *bio;
+    struct buf         *bp;
     int                        error;
     int                        blkcount;
     int                        driveno;
     int                        cmd;
-    u_int64_t          lba;
 
     ac = NULL;
     error = 0;
 
+    /* get a command */
+    if ((ac = amr_alloccmd(sc)) == NULL)
+       return (ENOMEM);
+
     /* get a bio to work on */
-    if ((bio = amr_dequeue_bio(sc)) == NULL)
-       goto out;
+    if ((bio = amr_dequeue_bio(sc)) == NULL) {
+       amr_releasecmd(ac);
+       return (0);
+    }
 
-    /* get a command */
-    if ((ac = amr_alloccmd(sc)) == NULL) {
-       error = ENOMEM;
-       goto out;
-    }  
-       
     /* connect the bio to the command */
+    bp = bio->bio_buf;
     ac->ac_complete = amr_completeio;
     ac->ac_bio = bio;
-    ac->ac_data = bio->bio_buf->b_data;
-    ac->ac_length = bio->bio_buf->b_bcount;
-
-    switch (bio->bio_buf->b_cmd) {
+    ac->ac_data = bp->b_data;
+    ac->ac_length = bp->b_bcount;
+    cmd = 0;
+    switch (bp->b_cmd) {
     case BUF_CMD_READ:
        ac->ac_flags |= AMR_CMD_DATAIN;
-       cmd = AMR_CMD_LREAD;
+       if (AMR_IS_SG64(sc)) {
+           cmd = AMR_CMD_LREAD64;
+           ac->ac_flags |= AMR_CMD_SG64;
+       } else
+           cmd = AMR_CMD_LREAD;
        break;
     case BUF_CMD_WRITE:
        ac->ac_flags |= AMR_CMD_DATAOUT;
-       cmd = AMR_CMD_LWRITE;
+       if (AMR_IS_SG64(sc)) {
+           cmd = AMR_CMD_LWRITE64;
+           ac->ac_flags |= AMR_CMD_SG64;
+       } else
+           cmd = AMR_CMD_LWRITE;
        break;
     case BUF_CMD_FLUSH:
        ac->ac_flags |= AMR_CMD_PRIORITY | AMR_CMD_DATAOUT;
        cmd = AMR_CMD_FLUSH;
        break;
     default:
-       cmd = 0;
-       break;
+       panic("Invalid bio command");
     }
     amrd = (struct amrd_softc *)bio->bio_driver_info;
     driveno = amrd->amrd_drive - sc->amr_drive;
-    blkcount = (bio->bio_buf->b_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE;
-
-    lba = bio->bio_offset / AMR_BLKSIZE;
-    KKASSERT(lba < 0x100000000ULL);
+    blkcount = (bp->b_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE;
 
     ac->ac_mailbox.mb_command = cmd;
-    ac->ac_mailbox.mb_blkcount = blkcount;
-    ac->ac_mailbox.mb_lba = lba;
+    if (bp->b_cmd & (BUF_CMD_READ|BUF_CMD_WRITE)) {
+       ac->ac_mailbox.mb_blkcount = blkcount;
+       ac->ac_mailbox.mb_lba = bio->bio_offset / AMR_BLKSIZE;
+       if (((bio->bio_offset / AMR_BLKSIZE) + blkcount) > sc->amr_drive[driveno].al_size) {
+           device_printf(sc->amr_dev,
+                         "I/O beyond end of unit (%lld,%d > %lu)\n",
+                         (long long)(bio->bio_offset / AMR_BLKSIZE), blkcount,
+                         (u_long)sc->amr_drive[driveno].al_size);
+       }
+    }
     ac->ac_mailbox.mb_drive = driveno;
+    if (sc->amr_state & AMR_STATE_REMAP_LD)
+       ac->ac_mailbox.mb_drive |= 0x80;
+
     /* we fill in the s/g related data when the command is mapped */
 
-    if ((lba + blkcount) > sc->amr_drive[driveno].al_size)
-       device_printf(sc->amr_dev, "I/O beyond end of unit (%ju,%d > %lu)\n",
-                     (uintmax_t)lba, blkcount,
-                     (u_long)sc->amr_drive[driveno].al_size);
 
-out:
-    if (error != 0) {
-       if (ac != NULL)
-           amr_releasecmd(ac);
-       if (bio != NULL)                        /* this breaks ordering... */
-           amr_enqueue_bio(sc, bio);
-    }
     *acp = ac;
     return(error);
 }
@@ -917,21 +1394,22 @@ out:
 static int
 amr_wait_command(struct amr_command *ac)
 {
-    int                        error, count;
-    
+    int                        error = 0;
+    struct amr_softc   *sc = ac->ac_sc;
+
     debug_called(1);
 
     ac->ac_complete = NULL;
     ac->ac_flags |= AMR_CMD_SLEEP;
-    if ((error = amr_start(ac)) != 0)
+    if ((error = amr_start(ac)) != 0) {
        return(error);
+    }
     
-    count = 0;
-    /* XXX better timeout? */
-    while ((ac->ac_flags & AMR_CMD_BUSY) && (count < 30)) {
-       tsleep(ac, PCATCH, "amrwcmd", hz);
+    while ((ac->ac_flags & AMR_CMD_BUSY) && (error != EWOULDBLOCK)) {
+       error = lksleep(ac,&sc->amr_list_lock, 0, "amrwcmd", 0);
     }
-    return(0);
+
+    return(error);
 }
 
 /********************************************************************************
@@ -969,6 +1447,38 @@ amr_std_poll_command(struct amr_command *ac)
     return(error);
 }
 
+static void
+amr_setup_polled_dmamap(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
+{
+    struct amr_command *ac = arg;
+    struct amr_softc *sc = ac->ac_sc;
+    int mb_channel;
+
+    if (err) {
+       device_printf(sc->amr_dev, "error %d in %s", err, __FUNCTION__);
+       ac->ac_status = AMR_STATUS_ABORTED;
+       return;
+    }
+
+    amr_setup_sg(arg, segs, nsegs, err);
+
+    /* for AMR_CMD_CONFIG Read/Write the s/g count goes elsewhere */
+    mb_channel = ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel;
+    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG &&
+        ((mb_channel == AMR_CONFIG_READ_NVRAM_CONFIG) ||
+        (mb_channel == AMR_CONFIG_WRITE_NVRAM_CONFIG)))
+       ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param = ac->ac_nsegments;
+
+    ac->ac_mailbox.mb_nsgelem = ac->ac_nsegments;
+    ac->ac_mailbox.mb_physaddr = ac->ac_mb_physaddr;
+    if (AC_IS_SG64(ac)) {
+       ac->ac_sg64_hi = 0;
+       ac->ac_sg64_lo = ac->ac_sgbusaddr;
+    }
+
+    sc->amr_poll_command1(sc, ac);
+}
+
 /********************************************************************************
  * Take a command, submit it to the controller and busy-wait for it to return.
  * Returns nonzero on error.  Can be safely called with interrupts enabled.
@@ -977,31 +1487,57 @@ static int
 amr_quartz_poll_command(struct amr_command *ac)
 {
     struct amr_softc   *sc = ac->ac_sc;
-    int                        error,count;
+    int                        error;
 
     debug_called(2);
 
+    error = 0;
+
+    if (AC_IS_SG64(ac)) {
+       ac->ac_tag = sc->amr_buffer64_dmat;
+       ac->ac_datamap = ac->ac_dma64map;
+    } else {
+       ac->ac_tag = sc->amr_buffer_dmat;
+       ac->ac_datamap = ac->ac_dmamap;
+    }
+
     /* now we have a slot, we can map the command (unmapped in amr_complete) */
-    amr_mapcmd(ac);
+    if (ac->ac_data != NULL && ac->ac_length != 0) {
+       if (bus_dmamap_load(ac->ac_tag, ac->ac_datamap, ac->ac_data,
+           ac->ac_length, amr_setup_polled_dmamap, ac, BUS_DMA_NOWAIT) != 0) {
+           error = 1;
+       }
+    } else {
+       error = amr_quartz_poll_command1(sc, ac);
+    }
 
-    crit_enter();
+    return (error);
+}
 
-    if (sc->amr_state & AMR_STATE_INTEN) {
-           count=0;
-           while (sc->amr_busyslots) {
-                   tsleep(sc, PCATCH, "amrpoll", hz);
-                   if(count++>10) {
-                           break;
-                   }
+static int
+amr_quartz_poll_command1(struct amr_softc *sc, struct amr_command *ac)
+{
+    int count, error;
+
+    lockmgr(&sc->amr_hw_lock, LK_EXCLUSIVE);
+    if ((sc->amr_state & AMR_STATE_INTEN) == 0) {
+       count=0;
+       while (sc->amr_busyslots) {
+           lksleep(sc, &sc->amr_hw_lock, PCATCH, "amrpoll", hz);
+           if(count++>10) {
+               break;
            }
-           
-           if(sc->amr_busyslots) {
-                   device_printf(sc->amr_dev, "adapter is busy\n");
-                   crit_exit();
-                   amr_unmapcmd(ac);
-                   ac->ac_status=0;
-                   return(1);
+       }
+
+       if(sc->amr_busyslots) {
+           device_printf(sc->amr_dev, "adapter is busy\n");
+           lockmgr(&sc->amr_hw_lock, LK_RELEASE);
+           if (ac->ac_data != NULL) {
+               bus_dmamap_unload(ac->ac_tag, ac->ac_datamap);
            }
+           ac->ac_status=0;
+           return(1);
+       }
     }
 
     bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
@@ -1016,65 +1552,51 @@ amr_quartz_poll_command(struct amr_command *ac)
 
     AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
 
-    while(sc->amr_mailbox->mb_nstatus == 0xFF);
-    while(sc->amr_mailbox->mb_status == 0xFF);
+    while(sc->amr_mailbox->mb_nstatus == 0xFF)
+       DELAY(1);
+    while(sc->amr_mailbox->mb_status == 0xFF)
+       DELAY(1);
     ac->ac_status=sc->amr_mailbox->mb_status;
     error = (ac->ac_status !=AMR_STATUS_SUCCESS) ? 1:0;
-    while(sc->amr_mailbox->mb_poll != 0x77);
+    while(sc->amr_mailbox->mb_poll != 0x77)
+       DELAY(1);
     sc->amr_mailbox->mb_poll = 0;
     sc->amr_mailbox->mb_ack = 0x77;
 
     /* acknowledge that we have the commands */
     AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
-    while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK);
-
-    crit_exit();
+    while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
+       DELAY(1);
+    lockmgr(&sc->amr_hw_lock, LK_RELEASE);
 
     /* unmap the command's data buffer */
-    amr_unmapcmd(ac);
+    if (ac->ac_flags & AMR_CMD_DATAIN) {
+       bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, BUS_DMASYNC_POSTREAD);
+    }
+    if (ac->ac_flags & AMR_CMD_DATAOUT) {
+       bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, BUS_DMASYNC_POSTWRITE);
+    }
+    bus_dmamap_unload(ac->ac_tag, ac->ac_datamap);
 
     return(error);
 }
 
-/********************************************************************************
- * Get a free command slot for a command if it doesn't already have one.
- *
- * May be safely called multiple times for a given command.
- */
-static int
-amr_getslot(struct amr_command *ac)
+static __inline int
+amr_freeslot(struct amr_command *ac)
 {
-    struct amr_softc   *sc = ac->ac_sc;
-    int                        slot, limit, error;
+    struct amr_softc *sc = ac->ac_sc;
+    int                        slot;
 
     debug_called(3);
 
-    /* if the command already has a slot, don't try to give it another one */
-    if (ac->ac_slot != 0)
-       return(0);
+    slot = ac->ac_slot;
+    if (sc->amr_busycmd[slot] == NULL)
+       panic("amr: slot %d not busy?\n", slot);
 
-    /* enforce slot usage limit */
-    limit = (ac->ac_flags & AMR_CMD_PRIORITY) ? sc->amr_maxio : sc->amr_maxio - 4;
-    if (sc->amr_busyslots > limit)
-       return(EBUSY);
-    
-    /*
-     * Allocate a slot.  XXX linear scan is slow
-     */
-    error = EBUSY;
-    crit_enter();
-    for (slot = 0; slot < sc->amr_maxio; slot++) {
-       if (sc->amr_busycmd[slot] == NULL) {
-           sc->amr_busycmd[slot] = ac;
-           sc->amr_busyslots++;
-           ac->ac_slot = slot;
-           error = 0;
-           break;
-       }
-    }
-    crit_exit();
+    sc->amr_busycmd[slot] = NULL;
+    atomic_subtract_int(&sc->amr_busyslots, 1);
 
-    return(error);
+    return (0);
 }
 
 /********************************************************************************
@@ -1083,132 +1605,166 @@ amr_getslot(struct amr_command *ac)
  * These functions may be safely called multiple times on a given command.
  */
 static void
-amr_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
+amr_setup_sg(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
 {
     struct amr_command *ac = (struct amr_command *)arg;
-    struct amr_softc   *sc = ac->ac_sc;
     struct amr_sgentry *sg;
-    int                        i;
-    u_int8_t           *sgc;
+    struct amr_sg64entry *sg64;
+    int flags, i;
 
     debug_called(3);
 
     /* get base address of s/g table */
-    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
+    sg = ac->ac_sg.sg32;
+    sg64 = ac->ac_sg.sg64;
+
+    if (AC_IS_SG64(ac)) {
+       ac->ac_nsegments = nsegments;
+       ac->ac_mb_physaddr = 0xffffffff;
+       for (i = 0; i < nsegments; i++, sg64++) {
+           sg64->sg_addr = segs[i].ds_addr;
+           sg64->sg_count = segs[i].ds_len;
+       }
+    } else {
+       /* decide whether we need to populate the s/g table */
+       if (nsegments < 2) {
+           ac->ac_nsegments = 0;
+           ac->ac_mb_physaddr = segs[0].ds_addr;
+       } else {
+            ac->ac_nsegments = nsegments;
+           ac->ac_mb_physaddr = ac->ac_sgbusaddr;
+           for (i = 0; i < nsegments; i++, sg++) {
+               sg->sg_addr = segs[i].ds_addr;
+               sg->sg_count = segs[i].ds_len;
+           }
+       }
+    }
 
-    /* save data physical address */
-    ac->ac_dataphys = segs[0].ds_addr;
+    flags = 0;
+    if (ac->ac_flags & AMR_CMD_DATAIN)
+       flags |= BUS_DMASYNC_PREREAD;
+    if (ac->ac_flags & AMR_CMD_DATAOUT)
+       flags |= BUS_DMASYNC_PREWRITE;
+    bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, flags);
+    ac->ac_flags |= AMR_CMD_MAPPED;
+}
 
-    /* for AMR_CMD_CONFIG the s/g count goes elsewhere */
-    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG) {
-       sgc = &(((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param);
-    } else {
-       sgc = &ac->ac_mailbox.mb_nsgelem;
+static void
+amr_setup_data(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
+{
+    struct amr_command *ac = arg;
+    struct amr_softc *sc = ac->ac_sc;
+    int mb_channel;
+
+    if (err) {
+       device_printf(sc->amr_dev, "error %d in %s", err, __FUNCTION__);
+       amr_abort_load(ac);
+       return;
     }
 
-    /* decide whether we need to populate the s/g table */
-    if (nsegments < 2) {
-       *sgc = 0;
-       ac->ac_mailbox.mb_nsgelem = 0;
-       ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
-    } else {
-        ac->ac_mailbox.mb_nsgelem = nsegments;
-       *sgc = nsegments;
-       ac->ac_mailbox.mb_physaddr = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
-       for (i = 0; i < nsegments; i++, sg++) {
-           sg->sg_addr = segs[i].ds_addr;
-           sg->sg_count = segs[i].ds_len;
-       }
+    amr_setup_sg(arg, segs, nsegs, err);
+
+    /* for AMR_CMD_CONFIG Read/Write the s/g count goes elsewhere */
+    mb_channel = ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_channel;
+    if (ac->ac_mailbox.mb_command == AMR_CMD_CONFIG &&
+        ((mb_channel == AMR_CONFIG_READ_NVRAM_CONFIG) ||
+        (mb_channel == AMR_CONFIG_WRITE_NVRAM_CONFIG)))
+       ((struct amr_mailbox_ioctl *)&ac->ac_mailbox)->mb_param = ac->ac_nsegments;
+
+    ac->ac_mailbox.mb_nsgelem = ac->ac_nsegments;
+    ac->ac_mailbox.mb_physaddr = ac->ac_mb_physaddr;
+    if (AC_IS_SG64(ac)) {
+       ac->ac_sg64_hi = 0;
+       ac->ac_sg64_lo = ac->ac_sgbusaddr;
+    }
+
+    if (sc->amr_submit_command(ac) == EBUSY) {
+       amr_freeslot(ac);
+       amr_requeue_ready(ac);
     }
 }
 
 static void
-amr_setup_ccbmap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
+amr_setup_ccb(void *arg, bus_dma_segment_t *segs, int nsegs, int err)
 {
-    struct amr_command          *ac = (struct amr_command *)arg;
-    struct amr_softc            *sc = ac->ac_sc;
-    struct amr_sgentry          *sg;
-    struct amr_passthrough      *ap = (struct amr_passthrough *)ac->ac_data;
-    struct amr_ext_passthrough *aep = (struct amr_ext_passthrough *)ac->ac_data;
-    int                         i;
+    struct amr_command *ac = arg;
+    struct amr_softc *sc = ac->ac_sc;
+    struct amr_passthrough *ap = &ac->ac_ccb->ccb_pthru;
+    struct amr_ext_passthrough *aep = &ac->ac_ccb->ccb_epthru;
+
+    if (err) {
+       device_printf(sc->amr_dev, "error %d in %s", err, __FUNCTION__);
+       amr_abort_load(ac);
+       return;
+    }
 
-    /* get base address of s/g table */
-    sg = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
+    /* Set up the mailbox portion of the command to point at the ccb */
+    ac->ac_mailbox.mb_nsgelem = 0;
+    ac->ac_mailbox.mb_physaddr = ac->ac_ccb_busaddr;
 
-    /* decide whether we need to populate the s/g table */
-    if( ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS ) {
-       if (nsegments < 2) {
-           aep->ap_no_sg_elements = 0;
-           aep->ap_data_transfer_address =  segs[0].ds_addr;
-       } else {
-           /* save s/g table information in passthrough */
-           aep->ap_no_sg_elements = nsegments;
-           aep->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
-           /* populate s/g table (overwrites previous call which mapped the passthrough) */
-           for (i = 0; i < nsegments; i++, sg++) {
-               sg->sg_addr = segs[i].ds_addr;
-               sg->sg_count = segs[i].ds_len;
-               debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count);
-           }
-       }
-       debug(3, "slot %d  %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot,
-           aep->ap_no_sg_elements, aep->ap_data_transfer_address, ac->ac_dataphys);
-    } else {
-       if (nsegments < 2) {
-           ap->ap_no_sg_elements = 0;
-           ap->ap_data_transfer_address =  segs[0].ds_addr;
-       } else {
-           /* save s/g table information in passthrough */
-           ap->ap_no_sg_elements = nsegments;
-           ap->ap_data_transfer_address = sc->amr_sgbusaddr + (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
-           /* populate s/g table (overwrites previous call which mapped the passthrough) */
-           for (i = 0; i < nsegments; i++, sg++) {
-               sg->sg_addr = segs[i].ds_addr;
-               sg->sg_count = segs[i].ds_len;
-               debug(3, " %d: 0x%x/%d", i, sg->sg_addr, sg->sg_count);
-           }
-       }
-       debug(3, "slot %d  %d segments at 0x%x, passthrough at 0x%x", ac->ac_slot,
-           ap->ap_no_sg_elements, ap->ap_data_transfer_address, ac->ac_dataphys);
+    amr_setup_sg(arg, segs, nsegs, err);
+
+    switch (ac->ac_mailbox.mb_command) {
+    case AMR_CMD_EXTPASS:
+       aep->ap_no_sg_elements = ac->ac_nsegments;
+       aep->ap_data_transfer_address = ac->ac_mb_physaddr;
+        break;
+    case AMR_CMD_PASS:
+       ap->ap_no_sg_elements = ac->ac_nsegments;
+       ap->ap_data_transfer_address = ac->ac_mb_physaddr;
+       break;
+    default:
+       panic("Unknown ccb command");
+    }
+
+    if (sc->amr_submit_command(ac) == EBUSY) {
+       amr_freeslot(ac);
+       amr_requeue_ready(ac);
     }
 }
 
-static void
+static int
 amr_mapcmd(struct amr_command *ac)
 {
+    bus_dmamap_callback_t *cb;
     struct amr_softc   *sc = ac->ac_sc;
 
     debug_called(3);
 
-    /* if the command involves data at all, and hasn't been mapped */
-    if (!(ac->ac_flags & AMR_CMD_MAPPED)) {
+    if (AC_IS_SG64(ac)) {
+       ac->ac_tag = sc->amr_buffer64_dmat;
+       ac->ac_datamap = ac->ac_dma64map;
+    } else {
+       ac->ac_tag = sc->amr_buffer_dmat;
+       ac->ac_datamap = ac->ac_dmamap;
+    }
 
-       if (ac->ac_data != NULL) {
-           /* map the data buffers into bus space and build the s/g list */
-           bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_dmamap, ac->ac_data, ac->ac_length,
-                           amr_setup_dmamap, ac, 0);
-           if (ac->ac_flags & AMR_CMD_DATAIN)
-               bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREREAD);
-           if (ac->ac_flags & AMR_CMD_DATAOUT)
-               bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_PREWRITE);
-       }
+    if (ac->ac_flags & AMR_CMD_CCB)
+       cb = amr_setup_ccb;
+    else
+       cb = amr_setup_data;
 
-       if (ac->ac_ccb_data != NULL) {
-           bus_dmamap_load(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, ac->ac_ccb_data, ac->ac_ccb_length,
-                           amr_setup_ccbmap, ac, 0);
-           if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
-               bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREREAD);
-           if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
-               bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_PREWRITE);
+    /* if the command involves data at all, and hasn't been mapped */
+    if ((ac->ac_flags & AMR_CMD_MAPPED) == 0 && (ac->ac_data != NULL)) {
+       /* map the data buffers into bus space and build the s/g list */
+       if (bus_dmamap_load(ac->ac_tag, ac->ac_datamap, ac->ac_data,
+            ac->ac_length, cb, ac, 0) == EINPROGRESS) {
+           sc->amr_state |= AMR_STATE_QUEUE_FRZN;
        }
-       ac->ac_flags |= AMR_CMD_MAPPED;
-    }
+   } else {
+       if (sc->amr_submit_command(ac) == EBUSY) {
+           amr_freeslot(ac);
+           amr_requeue_ready(ac);
+       }
+   }
+
+    return (0);
 }
 
 static void
 amr_unmapcmd(struct amr_command *ac)
 {
-    struct amr_softc   *sc = ac->ac_sc;
+    int                        flag;
 
     debug_called(3);
 
@@ -1216,24 +1772,38 @@ amr_unmapcmd(struct amr_command *ac)
     if (ac->ac_flags & AMR_CMD_MAPPED) {
 
        if (ac->ac_data != NULL) {
+
+           flag = 0;
            if (ac->ac_flags & AMR_CMD_DATAIN)
-               bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTREAD);
+               flag |= BUS_DMASYNC_POSTREAD;
            if (ac->ac_flags & AMR_CMD_DATAOUT)
-               bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_dmamap, BUS_DMASYNC_POSTWRITE);
-           bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_dmamap);
-       }
+               flag |= BUS_DMASYNC_POSTWRITE;
 
-       if (ac->ac_ccb_data != NULL) {
-           if (ac->ac_flags & AMR_CMD_CCB_DATAIN)
-               bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTREAD);
-           if (ac->ac_flags & AMR_CMD_CCB_DATAOUT)
-               bus_dmamap_sync(sc->amr_buffer_dmat, ac->ac_ccb_dmamap, BUS_DMASYNC_POSTWRITE);
-           bus_dmamap_unload(sc->amr_buffer_dmat, ac->ac_ccb_dmamap);
+           bus_dmamap_sync(ac->ac_tag, ac->ac_datamap, flag);
+           bus_dmamap_unload(ac->ac_tag, ac->ac_datamap);
        }
+
        ac->ac_flags &= ~AMR_CMD_MAPPED;
     }
 }
 
+static void
+amr_abort_load(struct amr_command *ac)
+{
+    ac_qhead_t head;
+    struct amr_softc *sc = ac->ac_sc;
+
+    KKASSERT(lockstatus(&sc->amr_list_lock, curthread) != 0);
+
+    ac->ac_status = AMR_STATUS_ABORTED;
+    amr_init_qhead(&head);
+    amr_enqueue_completed(ac, &head);
+
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
+    amr_complete(sc, &head);
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+}
+
 /********************************************************************************
  * Take a command and give it to the controller, returns 0 if successful, or
  * EBUSY if the command should be retried later.
@@ -1241,79 +1811,33 @@ amr_unmapcmd(struct amr_command *ac)
 static int
 amr_start(struct amr_command *ac)
 {
-    struct amr_softc   *sc = ac->ac_sc;
-    int                        done, i;
+    struct amr_softc *sc;
+    int error = 0;
+    int slot;
 
     debug_called(3);
 
     /* mark command as busy so that polling consumer can tell */
+    sc = ac->ac_sc;
     ac->ac_flags |= AMR_CMD_BUSY;
 
     /* get a command slot (freed in amr_done) */
-    if (amr_getslot(ac))
-       return(EBUSY);
-
-    /* now we have a slot, we can map the command (unmapped in amr_complete) */
-    amr_mapcmd(ac);
-
-    /* mark the new mailbox we are going to copy in as busy */
-    ac->ac_mailbox.mb_busy = 1;
-
-    /* clear the poll/ack fields in the mailbox */
-    sc->amr_mailbox->mb_poll = 0;
-    sc->amr_mailbox->mb_ack = 0;
-
-    /* 
-     * Save the slot number so that we can locate this command when complete.
-     * Note that ident = 0 seems to be special, so we don't use it.
-     */
-    ac->ac_mailbox.mb_ident = ac->ac_slot + 1;
-
-    /* 
-     * Spin waiting for the mailbox, give up after ~1 second.  We expect the
-     * controller to be able to handle our I/O.
-     *
-     * XXX perhaps we should wait for less time, and count on the deferred command
-     * handling to deal with retries?
-     */
-    debug(4, "wait for mailbox");
-    for (i = 10000, done = 0; (i > 0) && !done; i--) {
-       crit_enter();
-       
-       /* is the mailbox free? */
-       if (sc->amr_mailbox->mb_busy == 0) {
-           debug(4, "got mailbox");
-           sc->amr_mailbox64->mb64_segment = 0;
-           bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, AMR_MBOX_CMDSIZE);
-           done = 1;
-
-           /* not free, spin waiting */
-       } else {
-           debug(4, "busy flag %x\n", sc->amr_mailbox->mb_busy);
-           /* this is somewhat ugly */
-           DELAY(100);
-       }
-       crit_exit();
+    slot = ac->ac_slot;
+    if (sc->amr_busycmd[slot] != NULL)
+       panic("amr: slot %d busy?\n", slot);
+    sc->amr_busycmd[slot] = ac;
+    atomic_add_int(&sc->amr_busyslots, 1);
+
+    /* Now we have a slot, we can map the command (unmapped in amr_complete). */
+    if ((error = amr_mapcmd(ac)) == ENOMEM) {
+       /*
+        * Memroy resources are short, so free the slot and let this be tried
+        * later.
+        */
+       amr_freeslot(ac);
     }
 
-    /*
-     * Now give the command to the controller
-     */
-    if (done) {
-       if (sc->amr_submit_command(sc)) {
-           /* the controller wasn't ready to take the command, forget that we tried to post it */
-           sc->amr_mailbox->mb_busy = 0;
-           return(EBUSY);
-       }
-       debug(3, "posted command");
-       return(0);
-    }
-    
-    /*
-     * The controller wouldn't take the command.  Return the command as busy
-     * so that it is retried later.
-     */
-    return(EBUSY);
+    return (error);
 }
 
 /********************************************************************************
@@ -1321,9 +1845,11 @@ amr_start(struct amr_command *ac)
  *
  * Returns nonzero if any commands on the work queue were marked as completed.
  */
+
 int
 amr_done(struct amr_softc *sc)
 {
+    ac_qhead_t         head;
     struct amr_command *ac;
     struct amr_mailbox mbox;
     int                        i, idx, result;
@@ -1332,6 +1858,7 @@ amr_done(struct amr_softc *sc)
 
     /* See if there's anything for us to do */
     result = 0;
+    amr_init_qhead(&head);
 
     /* loop collecting completed commands */
     for (;;) {
@@ -1349,42 +1876,32 @@ amr_done(struct amr_softc *sc)
                if (ac != NULL) {
 
                    /* pull the command from the busy index */
-                   sc->amr_busycmd[idx] = NULL;
-                   sc->amr_busyslots--;
+                   amr_freeslot(ac);
                
                    /* save status for later use */
                    ac->ac_status = mbox.mb_status;
-                   amr_enqueue_completed(ac);
+                   amr_enqueue_completed(ac, &head);
                    debug(3, "completed command with status %x", mbox.mb_status);
                } else {
                    device_printf(sc->amr_dev, "bad slot %d completed\n", idx);
                }
            }
-       } else {
+       } else
            break;      /* no work */
-       }
     }
-    
-    /* if we've completed any commands, try posting some more */
-    if (result)
-       amr_startio(sc);
-    
+
     /* handle completion and timeouts */
-#if defined(__FreeBSD__) && __FreeBSD_version >= 500005
-    if (sc->amr_state & AMR_STATE_INTEN) 
-       taskqueue_enqueue(taskqueue_swi, &sc->amr_task_complete);
-    else
-#endif
-       amr_complete(sc, 0);
-    
+    amr_complete(sc, &head);
+
     return(result);
 }
 
 /********************************************************************************
  * Do completion processing on done commands on (sc)
  */
+
 static void
-amr_complete(void *context, int pending)
+amr_complete(void *context, ac_qhead_t *head)
 {
     struct amr_softc   *sc = (struct amr_softc *)context;
     struct amr_command *ac;
@@ -1393,33 +1910,43 @@ amr_complete(void *context, int pending)
 
     /* pull completed commands off the queue */
     for (;;) {
-       ac = amr_dequeue_completed(sc);
+       ac = amr_dequeue_completed(sc, head);
        if (ac == NULL)
            break;
 
        /* unmap the command's data buffer */
        amr_unmapcmd(ac);
 
-       /* unbusy the command */
-       ac->ac_flags &= ~AMR_CMD_BUSY;
-           
        /* 
         * Is there a completion handler? 
         */
        if (ac->ac_complete != NULL) {
+           /* unbusy the command */
+           ac->ac_flags &= ~AMR_CMD_BUSY;
            ac->ac_complete(ac);
            
            /* 
             * Is someone sleeping on this one?
             */
-       } else if (ac->ac_flags & AMR_CMD_SLEEP) {
-           wakeup(ac);
+       } else {
+           lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+           ac->ac_flags &= ~AMR_CMD_BUSY;
+           if (ac->ac_flags & AMR_CMD_SLEEP) {
+               /* unbusy the command */
+               wakeup(ac);
+           }
+           lockmgr(&sc->amr_list_lock, LK_RELEASE);
        }
 
        if(!sc->amr_busyslots) {
            wakeup(sc);
        }
     }
+
+    lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+    sc->amr_state &= ~AMR_STATE_QUEUE_FRZN;
+    amr_startio(sc);
+    lockmgr(&sc->amr_list_lock, LK_RELEASE);
 }
 
 /********************************************************************************
@@ -1444,21 +1971,20 @@ amr_alloccmd(struct amr_softc *sc)
 
     ac = amr_dequeue_free(sc);
     if (ac == NULL) {
-       amr_alloccmd_cluster(sc);
-       ac = amr_dequeue_free(sc);
-    }
-    if (ac == NULL)
+       sc->amr_state |= AMR_STATE_QUEUE_FRZN;
        return(NULL);
+    }
 
     /* clear out significant fields */
-    ac->ac_slot = 0;
     ac->ac_status = 0;
     bzero(&ac->ac_mailbox, sizeof(struct amr_mailbox));
     ac->ac_flags = 0;
     ac->ac_bio = NULL;
     ac->ac_data = NULL;
-    ac->ac_ccb_data = NULL;
     ac->ac_complete = NULL;
+    ac->ac_retries = 0;
+    ac->ac_tag = NULL;
+    ac->ac_datamap = NULL;
     return(ac);
 }
 
@@ -1481,19 +2007,58 @@ amr_alloccmd_cluster(struct amr_softc *sc)
 {
     struct amr_command_cluster *acc;
     struct amr_command         *ac;
-    int                                i;
+    int                                i, nextslot;
 
-    acc = kmalloc(AMR_CMD_CLUSTERSIZE, M_DEVBUF, M_INTWAIT);
-    crit_enter();
-    TAILQ_INSERT_TAIL(&sc->amr_cmd_clusters, acc, acc_link);
-    crit_exit();
-    for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
-       ac = &acc->acc_command[i];
-       bzero(ac, sizeof(*ac));
-       ac->ac_sc = sc;
-       if (!bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap) &&
-           !bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_ccb_dmamap))
+    /*
+     * If we haven't found the real limit yet, let us have a couple of
+     * commands in order to be able to probe.
+     */
+    if (sc->amr_maxio == 0)
+       sc->amr_maxio = 2;
+
+    if (sc->amr_nextslot > sc->amr_maxio)
+       return;
+    acc = kmalloc(AMR_CMD_CLUSTERSIZE, M_AMR, M_NOWAIT | M_ZERO);
+    if (acc != NULL) {
+       nextslot = sc->amr_nextslot;
+       lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+       TAILQ_INSERT_TAIL(&sc->amr_cmd_clusters, acc, acc_link);
+       lockmgr(&sc->amr_list_lock, LK_RELEASE);
+       for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
+           ac = &acc->acc_command[i];
+           ac->ac_sc = sc;
+           ac->ac_slot = nextslot;
+
+           /*
+            * The SG table for each slot is a fixed size and is assumed to
+            * to hold 64-bit s/g objects when the driver is configured to do
+            * 64-bit DMA.  32-bit DMA commands still use the same table, but
+            * cast down to 32-bit objects.
+            */
+           if (AMR_IS_SG64(sc)) {
+               ac->ac_sgbusaddr = sc->amr_sgbusaddr +
+                   (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sg64entry));
+               ac->ac_sg.sg64 = sc->amr_sg64table + (ac->ac_slot * AMR_NSEG);
+           } else {
+               ac->ac_sgbusaddr = sc->amr_sgbusaddr +
+                   (ac->ac_slot * AMR_NSEG * sizeof(struct amr_sgentry));
+               ac->ac_sg.sg32 = sc->amr_sgtable + (ac->ac_slot * AMR_NSEG);
+           }
+
+           ac->ac_ccb = sc->amr_ccb + ac->ac_slot;
+           ac->ac_ccb_busaddr = sc->amr_ccb_busaddr +
+               (ac->ac_slot * sizeof(union amr_ccb));
+
+           if (bus_dmamap_create(sc->amr_buffer_dmat, 0, &ac->ac_dmamap))
+               break;
+           if (AMR_IS_SG64(sc) &&
+               (bus_dmamap_create(sc->amr_buffer64_dmat, 0,&ac->ac_dma64map)))
+               break;
            amr_releasecmd(ac);
+           if (++nextslot > sc->amr_maxio)
+               break;
+       }
+       sc->amr_nextslot = nextslot;
     }
 }
 
@@ -1506,9 +2071,14 @@ amr_freecmd_cluster(struct amr_command_cluster *acc)
     struct amr_softc   *sc = acc->acc_command[0].ac_sc;
     int                        i;
 
-    for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++)
+    for (i = 0; i < AMR_CMD_CLUSTERCOUNT; i++) {
+       if (acc->acc_command[i].ac_sc == NULL)
+           break;
        bus_dmamap_destroy(sc->amr_buffer_dmat, acc->acc_command[i].ac_dmamap);
-    kfree(acc, M_DEVBUF);
+       if (AMR_IS_SG64(sc))
+               bus_dmamap_destroy(sc->amr_buffer64_dmat, acc->acc_command[i].ac_dma64map);
+    }
+    kfree(acc, M_AMR);
 }
 
 /********************************************************************************
@@ -1521,24 +2091,78 @@ amr_freecmd_cluster(struct amr_command_cluster *acc)
  * Tell the controller that the mailbox contains a valid command
  */
 static int
-amr_quartz_submit_command(struct amr_softc *sc)
+amr_quartz_submit_command(struct amr_command *ac)
 {
-    debug_called(3);
+    struct amr_softc   *sc = ac->ac_sc;
+    static struct timeval lastfail;
+    static int         curfail;
+    int                        i = 0;
+
+    lockmgr(&sc->amr_hw_lock, LK_EXCLUSIVE);
+    while (sc->amr_mailbox->mb_busy && (i++ < 10)) {
+        DELAY(1);
+       /* This is a no-op read that flushes pending mailbox updates */
+       AMR_QGET_ODB(sc);
+    }
+    if (sc->amr_mailbox->mb_busy) {
+       lockmgr(&sc->amr_hw_lock, LK_RELEASE);
+       if (ac->ac_retries++ > 1000) {
+           if (ppsratecheck(&lastfail, &curfail, 1))
+               device_printf(sc->amr_dev, "Too many retries on command %p.  "
+                             "Controller is likely dead\n", ac);
+           ac->ac_retries = 0;
+       }
+       return (EBUSY);
+    }
+
+    /*
+     * Save the slot number so that we can locate this command when complete.
+     * Note that ident = 0 seems to be special, so we don't use it.
+     */
+    ac->ac_mailbox.mb_ident = ac->ac_slot + 1; /* will be coppied into mbox */
+    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, 14);
+    sc->amr_mailbox->mb_busy = 1;
+    sc->amr_mailbox->mb_poll = 0;
+    sc->amr_mailbox->mb_ack  = 0;
+    sc->amr_mailbox64->sg64_hi = ac->ac_sg64_hi;
+    sc->amr_mailbox64->sg64_lo = ac->ac_sg64_lo;
 
-    if (AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT)
-       return(EBUSY);
     AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
+    lockmgr(&sc->amr_hw_lock, LK_RELEASE);
     return(0);
 }
 
 static int
-amr_std_submit_command(struct amr_softc *sc)
+amr_std_submit_command(struct amr_command *ac)
 {
-    debug_called(3);
+    struct amr_softc   *sc = ac->ac_sc;
+    static struct timeval lastfail;
+    static int         curfail;
+
+    lockmgr(&sc->amr_hw_lock, LK_EXCLUSIVE);
+    if (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG) {
+       lockmgr(&sc->amr_hw_lock, LK_RELEASE);
+       if (ac->ac_retries++ > 1000) {
+           if (ppsratecheck(&lastfail, &curfail, 1))
+               device_printf(sc->amr_dev, "Too many retries on command %p.  "
+                             "Controller is likely dead\n", ac);
+           ac->ac_retries = 0;
+       }
+       return (EBUSY);
+    }
+
+    /*
+     * Save the slot number so that we can locate this command when complete.
+     * Note that ident = 0 seems to be special, so we don't use it.
+     */
+    ac->ac_mailbox.mb_ident = ac->ac_slot + 1; /* will be coppied into mbox */
+    bcopy(&ac->ac_mailbox, (void *)(uintptr_t)(volatile void *)sc->amr_mailbox, 14);
+    sc->amr_mailbox->mb_busy = 1;
+    sc->amr_mailbox->mb_poll = 0;
+    sc->amr_mailbox->mb_ack  = 0;
 
-    if (AMR_SGET_MBSTAT(sc) & AMR_SMBOX_BUSYFLAG)
-       return(EBUSY);
     AMR_SPOST_COMMAND(sc);
+    lockmgr(&sc->amr_hw_lock, LK_RELEASE);
     return(0);
 }
 
@@ -1549,26 +2173,44 @@ amr_std_submit_command(struct amr_softc *sc)
 static int
 amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
 {
-    int                worked;
+    int                worked, i;
     u_int32_t  outd;
+    u_int8_t   nstatus;
+    u_int8_t   completed[46];
 
     debug_called(3);
 
     worked = 0;
-    crit_enter();
 
     /* work waiting for us? */
     if ((outd = AMR_QGET_ODB(sc)) == AMR_QODB_READY) {
 
-       /* save mailbox, which contains a list of completed commands */
-       bcopy((void *)(uintptr_t)(volatile void *)sc->amr_mailbox, mbsave, sizeof(*mbsave));
-
        /* acknowledge interrupt */
        AMR_QPUT_ODB(sc, AMR_QODB_READY);
 
+       while ((nstatus = sc->amr_mailbox->mb_nstatus) == 0xff)
+           DELAY(1);
+       sc->amr_mailbox->mb_nstatus = 0xff;
+
+       /* wait until fw wrote out all completions */
+       for (i = 0; i < nstatus; i++) {
+           while ((completed[i] = sc->amr_mailbox->mb_completed[i]) == 0xff)
+               DELAY(1);
+           sc->amr_mailbox->mb_completed[i] = 0xff;
+       }
+
+       /* Save information for later processing */
+       mbsave->mb_nstatus = nstatus;
+       mbsave->mb_status = sc->amr_mailbox->mb_status;
+       sc->amr_mailbox->mb_status = 0xff;
+
+       for (i = 0; i < nstatus; i++)
+           mbsave->mb_completed[i] = completed[i];
+
        /* acknowledge that we have the commands */
-       AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_ACK);
+       AMR_QPUT_IDB(sc, AMR_QIDB_ACK);
 
+#if 0
 #ifndef AMR_QUARTZ_GOFASTER
        /*
         * This waits for the controller to notice that we've taken the
@@ -1582,11 +2224,11 @@ amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
        while(AMR_QGET_IDB(sc) & AMR_QIDB_ACK)
            ;                           /* XXX aiee! what if it dies? */
 #endif
+#endif
 
        worked = 1;                     /* got some work */
     }
 
-    crit_exit();
     return(worked);
 }
 
@@ -1599,7 +2241,6 @@ amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
     debug_called(3);
 
     worked = 0;
-    crit_enter();
 
     /* check for valid interrupt status */
     istat = AMR_SGET_ISTAT(sc);
@@ -1613,7 +2254,6 @@ amr_std_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
        worked = 1;
     }
 
-    crit_exit();
     return(worked);
 }
 
@@ -1710,26 +2350,27 @@ amr_describe_controller(struct amr_softc *sc)
     struct amr_prodinfo        *ap;
     struct amr_enquiry *ae;
     char               *prod;
+    int                        status;
 
     /*
      * Try to get 40LD product info, which tells us what the card is labelled as.
      */
-    if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0)) != NULL) {
+    if ((ap = amr_enquiry(sc, 2048, AMR_CMD_CONFIG, AMR_CONFIG_PRODUCT_INFO, 0, &status)) != NULL) {
        device_printf(sc->amr_dev, "<LSILogic %.80s> Firmware %.16s, BIOS %.16s, %dMB RAM\n",
                      ap->ap_product, ap->ap_firmware, ap->ap_bios,
                      ap->ap_memsize);
 
-       kfree(ap, M_DEVBUF);
+       kfree(ap, M_AMR);
        return;
     }
 
     /*
      * Try 8LD extended ENQUIRY to get controller signature, and use lookup table.
      */
-    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0)) != NULL) {
+    if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_EXT_ENQUIRY2, 0, 0, &status)) != NULL) {
        prod = amr_describe_code(amr_table_adaptertype, ae->ae_signature);
 
-    } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0)) != NULL) {
+    } else if ((ae = (struct amr_enquiry *)amr_enquiry(sc, 2048, AMR_CMD_ENQUIRY, 0, 0, &status)) != NULL) {
 
        /*
         * Try to work it out based on the PCI signatures.
@@ -1786,19 +2427,18 @@ amr_describe_controller(struct amr_softc *sc)
                      prod, ae->ae_adapter.aa_firmware, ae->ae_adapter.aa_bios,
                      ae->ae_adapter.aa_memorysize);
     }          
-    kfree(ae, M_DEVBUF);
+    kfree(ae, M_AMR);
 }
 
 int
-amr_dump_blocks(struct amr_softc *sc, int unit, u_int64_t lba, void *data, int blks)
+amr_dump_blocks(struct amr_softc *sc, int unit, u_int32_t lba, void *data, int blks)
 {
-
     struct amr_command *ac;
-    int                        error = 1;
+    int                        error = EIO;
 
     debug_called(1);
 
-    sc->amr_state &= ~AMR_STATE_INTEN;
+    sc->amr_state |= AMR_STATE_INTEN;
 
     /* get ourselves a command buffer */
     if ((ac = amr_alloccmd(sc)) == NULL)
@@ -1815,7 +2455,7 @@ amr_dump_blocks(struct amr_softc *sc, int unit, u_int64_t lba, void *data, int b
     ac->ac_mailbox.mb_blkcount = blks;
     ac->ac_mailbox.mb_lba      = lba;
     ac->ac_mailbox.mb_drive    = unit;
-       
+
     /* can't assume that interrupts are going to work here, so play it safe */
     if (sc->amr_poll_command(ac))
        goto out;
@@ -1825,12 +2465,12 @@ amr_dump_blocks(struct amr_softc *sc, int unit, u_int64_t lba, void *data, int b
     if (ac != NULL)
        amr_releasecmd(ac);
 
-    sc->amr_state |= AMR_STATE_INTEN;
-
-    return (error);    
+    sc->amr_state &= ~AMR_STATE_INTEN;
+    return (error);
 }
 
 
+
 #ifdef AMR_DEBUG
 /********************************************************************************
  * Print the command (ac) in human-readable format
index 6297988..ed5da50 100644 (file)
@@ -7,10 +7,10 @@
  * 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.
+ *     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.
+ *     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
@@ -23,7 +23,8 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
+ */
+/*-
  * Copyright (c) 2002 Eric Moore
  * Copyright (c) 2002 LSI Logic Corporation
  * All rights reserved.
  * 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.
+ *     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.
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
  * 3. The party using or redistributing the source code and binary forms
- *    agrees to the disclaimer below and the terms and conditions set forth
- *    herein.
+ *     agrees to the disclaimer below and the terms and conditions set forth
+ *     herein.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     $FreeBSD: src/sys/dev/amr/amr_cam.c,v 1.1.2.3 2002/11/11 13:19:10 emoore Exp $
- *     $DragonFly: src/sys/dev/raid/amr/amr_cam.c,v 1.11 2008/05/18 20:30:23 pavalos Exp $
+ * $FreeBSD: src/sys/dev/amr/amr_cam.c,v 1.28 2008/11/03 00:53:54 scottl Exp $
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <sys/module.h>
 
-#include "amr_compat.h"
+#include <sys/bio.h>
 #include <sys/bus.h>
 #include <sys/conf.h>
-#include <sys/devicestat.h>
-#include <sys/disk.h>
 #include <sys/stat.h>
 
 #include <bus/cam/cam.h>
 #include <bus/cam/scsi/scsi_all.h>
 #include <bus/cam/scsi/scsi_message.h>
 
-#include "amrreg.h"
-#include "amrvar.h"
+#include <dev/raid/amr/amrreg.h>
+#include <dev/raid/amr/amrvar.h>
 
-static void            amr_cam_action(struct cam_sim *sim, union ccb *ccb);
-static void            amr_cam_poll(struct cam_sim *sim);
-static void            amr_cam_complete(struct amr_command *ac);
-static void            amr_cam_complete_extcdb(struct amr_command *ac);
+static int     amr_cam_probe(device_t dev);
+static int     amr_cam_attach(device_t dev);
+static int     amr_cam_detach(device_t dev);
+static void    amr_cam_action(struct cam_sim *sim, union ccb *ccb);
+static void    amr_cam_poll(struct cam_sim *sim);
+static void    amr_cam_complete(struct amr_command *ac);
+static int     amr_cam_command(struct amr_softc *sc, struct amr_command **acp);
 
+static devclass_t      amr_pass_devclass;
 
-/********************************************************************************
+static device_method_t amr_pass_methods[] = {
+       DEVMETHOD(device_probe,         amr_cam_probe),
+       DEVMETHOD(device_attach,        amr_cam_attach),
+       DEVMETHOD(device_detach,        amr_cam_detach),
+       { 0, 0 }
+};
+
+static driver_t        amr_pass_driver = {
+       "amrp",
+       amr_pass_methods,
+       0
+};
+
+DRIVER_MODULE(amrp, amr, amr_pass_driver, amr_pass_devclass, 0, 0);
+MODULE_DEPEND(amrp, cam, 1, 1, 1);
+
+MALLOC_DEFINE(M_AMRCAM, "amrcam", "AMR CAM memory");
+
+/***********************************************************************
  * Enqueue/dequeue functions
  */
 static __inline void
 amr_enqueue_ccb(struct amr_softc *sc, union ccb *ccb)
 {
-    crit_enter();
-    TAILQ_INSERT_TAIL(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
-    crit_exit();
+
+       TAILQ_INSERT_TAIL(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
 }
 
 static __inline void
 amr_requeue_ccb(struct amr_softc *sc, union ccb *ccb)
 {
-    crit_enter();
-    TAILQ_INSERT_HEAD(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
-    crit_exit();
+
+       TAILQ_INSERT_HEAD(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
 }
 
 static __inline union ccb *
 amr_dequeue_ccb(struct amr_softc *sc)
 {
-    union ccb  *ccb;
+       union ccb       *ccb;
 
-    crit_enter();
-    if ((ccb = (union ccb *)TAILQ_FIRST(&sc->amr_cam_ccbq)) != NULL)
-       TAILQ_REMOVE(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
-    crit_exit();
-    return(ccb);
+       if ((ccb = (union ccb *)TAILQ_FIRST(&sc->amr_cam_ccbq)) != NULL)
+               TAILQ_REMOVE(&sc->amr_cam_ccbq, &ccb->ccb_h, sim_links.tqe);
+       return(ccb);
+}
+
+static int
+amr_cam_probe(device_t dev)
+{
+       return (0);
 }
 
 /********************************************************************************
  * Attach our 'real' SCSI channels to CAM
  */
-int
-amr_cam_attach(struct amr_softc *sc)
+static int
+amr_cam_attach(device_t dev)
 {
-    struct cam_devq    *devq;
-    int                        chn;
-
-    /* initialise the ccb queue */
-    TAILQ_INIT(&sc->amr_cam_ccbq);
-
-    /*
-     * Allocate a devq for all our channels combined.  This should
-     * allow for the maximum number of SCSI commands we will accept
-     * at one time.
-     */
-    if ((devq = cam_simq_alloc(AMR_MAX_SCSI_CMDS)) == NULL)
-       return(ENOMEM);
-
-    /*
-     * Iterate over our channels, registering them with CAM
-     */
-    for (chn = 0; chn < sc->amr_maxchan; chn++) {
-
-       /* allocate a sim */
-       if ((sc->amr_cam_sim[chn] = cam_sim_alloc(amr_cam_action,
-                                                 amr_cam_poll,
-                                                 "amr",
-                                                 sc,
-                                                 device_get_unit(sc->amr_dev),
-                                                 &sim_mplock,
-                                                 1,
-                                                 AMR_MAX_SCSI_CMDS,
-                                                 devq)) == NULL) {
-           device_printf(sc->amr_dev, "CAM SIM attach failed\n");
-           return(ENOMEM);
-       }
+       struct amr_softc *sc;
+       struct cam_devq *devq;
+       int chn, error;
 
-       /* register the bus ID so we can get it later */
-       if (xpt_bus_register(sc->amr_cam_sim[chn], chn)) {
-           device_printf(sc->amr_dev, "CAM XPT bus registration failed\n");
-           return(ENXIO);
+       sc = device_get_softc(dev);
+
+       /* initialise the ccb queue */
+       TAILQ_INIT(&sc->amr_cam_ccbq);
+
+       /*
+        * Allocate a devq for all our channels combined.  This should
+        * allow for the maximum number of SCSI commands we will accept
+        * at one time. Save the pointer in the softc so we can find it later
+        * during detach.
+        */
+       if ((devq = cam_simq_alloc(AMR_MAX_SCSI_CMDS)) == NULL)
+               return(ENOMEM);
+       sc->amr_cam_devq = devq;
+
+       /*
+        * Iterate over our channels, registering them with CAM
+        */
+       for (chn = 0; chn < sc->amr_maxchan; chn++) {
+
+               /* allocate a sim */
+               if ((sc->amr_cam_sim[chn] = cam_sim_alloc(amr_cam_action,
+                   amr_cam_poll, "amr", sc, device_get_unit(sc->amr_dev),
+                   &sc->amr_list_lock, 1, AMR_MAX_SCSI_CMDS, devq)) == NULL) {
+                       device_printf(sc->amr_dev, "CAM SIM attach failed\n");
+                       return(ENOMEM);
+               }
+
+               /* register the bus ID so we can get it later */
+               lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+               error = xpt_bus_register(sc->amr_cam_sim[chn], chn);
+               lockmgr(&sc->amr_list_lock, LK_RELEASE);
+               if (error) {
+                       device_printf(sc->amr_dev,
+                           "CAM XPT bus registration failed\n");
+                       return(ENXIO);
+               }
        }
-    }
-    cam_simq_release(devq);
-    /*
-     * XXX we should scan the config and work out which devices are actually
-     * protected.
-     */
-    return(0);
+       /*
+        * XXX we should scan the config and work out which devices are
+        * actually protected.
+        */
+       sc->amr_cam_command = amr_cam_command;
+       return(0);
 }
 
 /********************************************************************************
  * Disconnect ourselves from CAM
  */
-void
-amr_cam_detach(struct amr_softc *sc)
+static int
+amr_cam_detach(device_t dev)
 {
-    int                chn;
-
-    /*
-     * If a sim was allocated for a channel, free it
-     */
-    for (chn = 0; chn < sc->amr_maxchan; chn++) {
-       if (sc->amr_cam_sim[chn] != NULL) {
-           xpt_bus_deregister(cam_sim_path(sc->amr_cam_sim[chn]));
-           cam_sim_free(sc->amr_cam_sim[chn]);
+       struct amr_softc *sc;
+       int             chn;
+
+       sc = device_get_softc(dev);
+       lockmgr(&sc->amr_list_lock, LK_EXCLUSIVE);
+       for (chn = 0; chn < sc->amr_maxchan; chn++) {
+               /*
+                * If a sim was allocated for this channel, free it
+                */
+               if (sc->amr_cam_sim[chn] != NULL) {
+                       xpt_bus_deregister(cam_sim_path(sc->amr_cam_sim[chn]));
+                       cam_sim_free(sc->amr_cam_sim[chn]);
+               }
        }
-    }
+       lockmgr(&sc->amr_list_lock, LK_RELEASE);
+
+       return (0);
 }
 
-/********************************************************************************
- ********************************************************************************
-                                                        CAM passthrough interface
- ********************************************************************************
- ********************************************************************************/
+/***********************************************************************
+ ***********************************************************************
+                       CAM passthrough interface
+ ***********************************************************************
+ ***********************************************************************/
 
-/********************************************************************************
+/***********************************************************************
  * Handle a request for action from CAM
  */
 static void
 amr_cam_action(struct cam_sim *sim, union ccb *ccb)
 {
-    struct amr_softc   *sc = cam_sim_softc(sim);
-
-    switch(ccb->ccb_h.func_code) {
-
-    /*
-     * Perform SCSI I/O to a physical device.
-     */
-    case XPT_SCSI_IO:
-    {
-       struct ccb_hdr          *ccbh = &ccb->ccb_h;
-       struct ccb_scsiio       *csio = &ccb->csio;
-
-       /* Validate the CCB */
-       ccbh->status = CAM_REQ_INPROG;
-
-       /* check the CDB length */
-       if (csio->cdb_len > AMR_MAX_EXTCDB_LEN)
-           ccbh->status = CAM_REQ_CMP_ERR;
-
-       if ((csio->cdb_len > AMR_MAX_CDB_LEN) && (sc->support_ext_cdb == 0 ))
-           ccbh->status = CAM_REQ_CMP_ERR;
-
-       /* check that the CDB pointer is not to a physical address */
-       if ((ccbh->flags & CAM_CDB_POINTER) && (ccbh->flags & CAM_CDB_PHYS))
-           ccbh->status = CAM_REQ_CMP_ERR;
-
-       /* if there is data transfer, it must be to/from a virtual address */
-       if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
-           if (ccbh->flags & CAM_DATA_PHYS)            /* we can't map it */
-               ccbh->status = CAM_REQ_CMP_ERR;         
-           if (ccbh->flags & CAM_SCATTER_VALID)        /* we want to do the s/g setup */
-               ccbh->status = CAM_REQ_CMP_ERR;
+       struct amr_softc        *sc = cam_sim_softc(sim);
+
+       switch(ccb->ccb_h.func_code) {
+
+       /*
+        * Perform SCSI I/O to a physical device.
+        */
+       case XPT_SCSI_IO:
+       {
+               struct ccb_hdr          *ccbh = &ccb->ccb_h;
+               struct ccb_scsiio       *csio = &ccb->csio;
+
+               /* Validate the CCB */
+               ccbh->status = CAM_REQ_INPROG;
+
+               /* check the CDB length */
+               if (csio->cdb_len > AMR_MAX_EXTCDB_LEN)
+                       ccbh->status = CAM_REQ_INVALID;
+
+               if ((csio->cdb_len > AMR_MAX_CDB_LEN) &&
+                   (sc->support_ext_cdb == 0))
+                       ccbh->status = CAM_REQ_INVALID;
+
+               /* check that the CDB pointer is not to a physical address */
+               if ((ccbh->flags & CAM_CDB_POINTER) &&
+                   (ccbh->flags & CAM_CDB_PHYS))
+                       ccbh->status = CAM_REQ_INVALID;
+               /*
+                * if there is data transfer, it must be to/from a virtual
+                * address
+                */
+               if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
+                       if (ccbh->flags & CAM_DATA_PHYS)
+                               /* we can't map it */
+                               ccbh->status = CAM_REQ_INVALID;
+                       if (ccbh->flags & CAM_SCATTER_VALID)
+                               /* we want to do the s/g setup */
+                               ccbh->status = CAM_REQ_INVALID;
+               }
+
+               /*
+                * If the command is to a LUN other than 0, fail it.
+                * This is probably incorrect, but during testing the
+                * firmware did not seem to respect the LUN field, and thus
+                * devices appear echoed.
+                */
+               if (csio->ccb_h.target_lun != 0)
+                       ccbh->status = CAM_DEV_NOT_THERE;
+
+               /* if we're happy with the request, queue it for attention */
+               if (ccbh->status == CAM_REQ_INPROG) {
+
+                       /* save the channel number in the ccb */
+                       csio->ccb_h.sim_priv.entries[0].field= cam_sim_bus(sim);
+
+                       amr_enqueue_ccb(sc, ccb);
+                       amr_startio(sc);
+                       return;
+               }
+               break;
+       }
+
+       case XPT_CALC_GEOMETRY:
+       {
+               cam_calc_geometry(&ccb->ccg, /*extended*/1);
+               break;
        }
 
        /*
-        * If the command is to a LUN other than 0, fail it.
-        * This is probably incorrect, but during testing the firmware did not
-        * seem to respect the LUN field, and thus devices appear echoed.
+        * Return path stats.  Some of these should probably be amended.
         */
-       if (csio->ccb_h.target_lun != 0)
-           ccbh->status = CAM_REQ_CMP_ERR;
+       case XPT_PATH_INQ:
+       {
+               struct ccb_pathinq        *cpi = & ccb->cpi;
+
+               debug(3, "XPT_PATH_INQ");
+               cpi->version_num = 1;              /* XXX??? */
+               cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
+               cpi->target_sprt = 0;
+               cpi->hba_misc = PIM_NOBUSRESET|PIM_SEQSCAN;
+               cpi->hba_eng_cnt = 0;
+               cpi->max_target = AMR_MAX_TARGETS;
+               cpi->max_lun = 0 /* AMR_MAX_LUNS*/;
+               cpi->initiator_id = 7;            /* XXX variable? */
+               strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+               strncpy(cpi->hba_vid, "LSI", HBA_IDLEN);
+               strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+               cpi->unit_number = cam_sim_unit(sim);
+               cpi->bus_id = cam_sim_bus(sim);
+               cpi->base_transfer_speed = 132 * 1024;  /* XXX */
+               cpi->transport = XPORT_SPI;
+               cpi->transport_version = 2;
+               cpi->protocol = PROTO_SCSI;
+               cpi->protocol_version = SCSI_REV_2;
+               cpi->ccb_h.status = CAM_REQ_CMP;
 
-       /* if we're happy with the request, queue it for attention */
-       if (ccbh->status == CAM_REQ_INPROG) {
+               break;
+       }
 
-           /* save the channel number in the ccb */
-           csio->ccb_h.sim_priv.entries[0].field = cam_sim_bus(sim);
+       case XPT_RESET_BUS:
+       {
+               struct ccb_pathinq      *cpi = & ccb->cpi;
 
-           amr_enqueue_ccb(sc, ccb);
-           amr_startio(sc);
-           return;
+               debug(1, "XPT_RESET_BUS");
+               cpi->ccb_h.status = CAM_REQ_CMP;
+               break;
        }
-       break;
-    }
 
-    case XPT_CALC_GEOMETRY:
-    {
-       struct    ccb_calc_geometry *ccg = &ccb->ccg;
-       u_int32_t size_in_mb;
-       u_int32_t secs_per_cylinder;
+       case XPT_RESET_DEV:
+       {
+               debug(1, "XPT_RESET_DEV");
+               ccb->ccb_h.status = CAM_REQ_CMP;
+               break;
+       }
 
-       size_in_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size);
+       case XPT_GET_TRAN_SETTINGS:
+       {
+               struct ccb_trans_settings       *cts = &(ccb->cts);
 
-       if (size_in_mb > 1024) {
-           ccg->heads = 255;
-           ccg->secs_per_track = 63;
-       } else {
-           ccg->heads = 64;
-           ccg->secs_per_track = 32;
+               debug(3, "XPT_GET_TRAN_SETTINGS");
+
+               struct ccb_trans_settings_scsi *scsi;
+               struct ccb_trans_settings_spi *spi;
+
+               scsi = &cts->proto_specific.scsi;
+               spi = &cts->xport_specific.spi;
+
+               cts->protocol = PROTO_SCSI;
+               cts->protocol_version = SCSI_REV_2;
+               cts->transport = XPORT_SPI;
+               cts->transport_version = 2;
+
+               if (cts->type == CTS_TYPE_USER_SETTINGS) {
+                       ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
+                       break;
+               }
+
+               spi->flags = CTS_SPI_FLAGS_DISC_ENB;
+               spi->bus_width = MSG_EXT_WDTR_BUS_32_BIT;
+               spi->sync_period = 6;   /* 40MHz how wide is this bus? */
+               spi->sync_offset = 31;  /* How to extract this from board? */
+
+               spi->valid = CTS_SPI_VALID_SYNC_RATE
+                       | CTS_SPI_VALID_SYNC_OFFSET
+                       | CTS_SPI_VALID_BUS_WIDTH
+                       | CTS_SPI_VALID_DISC;
+               scsi->valid = CTS_SCSI_VALID_TQ;
+               ccb->ccb_h.status = CAM_REQ_CMP;
+               break;
        }
-       secs_per_cylinder = ccg->heads * ccg->secs_per_track;
-       ccg->cylinders = ccg->volume_size / secs_per_cylinder;
-       ccb->ccb_h.status = CAM_REQ_CMP;
-       break;
-    }
-
-    /*
-     * Return path stats.  Some of these should probably be
-     * amended.
-     */
-    case XPT_PATH_INQ:
-    {
-       struct ccb_pathinq      *cpi = & ccb->cpi;
-
-       debug(3, "XPT_PATH_INQ");
-       cpi->version_num = 1;           /* XXX??? */
-       cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
-       cpi->target_sprt = 0;
-       cpi->hba_misc = PIM_NOBUSRESET;
-       cpi->hba_eng_cnt = 0;
-       cpi->max_target = AMR_MAX_TARGETS;
-       cpi->max_lun = 0 /* AMR_MAX_LUNS*/;
-       cpi->initiator_id = 7;          /* XXX variable? */
-       strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
-       strncpy(cpi->hba_vid, "LSI", HBA_IDLEN);
-       strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
-       cpi->unit_number = cam_sim_unit(sim);
-       cpi->bus_id = cam_sim_bus(sim);
-       cpi->base_transfer_speed = 132 * 1024;  /* XXX get from controller? */
-       cpi->transport = XPORT_SPI;
-       cpi->transport_version = 2;
-       cpi->protocol = PROTO_SCSI;
-       cpi->protocol_version = SCSI_REV_2;
-       cpi->ccb_h.status = CAM_REQ_CMP;
-
-       break;
-    }
-
-    case XPT_RESET_BUS:
-    {
-       struct ccb_pathinq      *cpi = & ccb->cpi;
-
-       debug(1, "XPT_RESET_BUS");
-       cpi->ccb_h.status = CAM_REQ_CMP;
-       break;
-    }
-
-    case XPT_RESET_DEV:
-    {
-       debug(1, "XPT_RESET_DEV");
-       ccb->ccb_h.status = CAM_REQ_CMP;
-       break;
-    }
-
-    case XPT_GET_TRAN_SETTINGS:
-    {
-       struct ccb_trans_settings       *cts = &(ccb->cts);
-
-       debug(3, "XPT_GET_TRAN_SETTINGS");
-
-       struct ccb_trans_settings_scsi *scsi = &cts->proto_specific.scsi;
-       struct ccb_trans_settings_spi *spi = &cts->xport_specific.spi;
-
-       cts->protocol = PROTO_SCSI;
-       cts->protocol_version = SCSI_REV_2;
-       cts->transport = XPORT_SPI;
-       cts->transport_version = 2;
-
-       if (cts->type == CTS_TYPE_USER_SETTINGS) {
+
+       case XPT_SET_TRAN_SETTINGS:
+               debug(3, "XPT_SET_TRAN_SETTINGS");
                ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
                break;
+
+
+       /*
+        * Reject anything else as unsupported.
+        */
+       default:
+               /* we can't do this */
+               ccb->ccb_h.status = CAM_REQ_INVALID;
+               break;
        }
 
-       spi->flags = CTS_SPI_FLAGS_DISC_ENB;
-       spi->bus_width = MSG_EXT_WDTR_BUS_32_BIT;
-       spi->sync_period = 6;   /* 40MHz how wide is this bus? */
-       spi->sync_offset = 31;  /* How to extract this from board? */
-
-       spi->valid = CTS_SPI_VALID_SYNC_RATE
-           | CTS_SPI_VALID_SYNC_OFFSET
-           | CTS_SPI_VALID_BUS_WIDTH
-           | CTS_SPI_VALID_DISC;
-       scsi->valid = CTS_SCSI_VALID_TQ;
-       ccb->ccb_h.status = CAM_REQ_CMP;
-       break;
-    }
-
-    case XPT_SET_TRAN_SETTINGS:
-       debug(3, "XPT_SET_TRAN_SETTINGS");
-       ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
-       break;
-
-
-    /*
-     * Reject anything else as unsupported.
-     */
-    default:
-       /* we can't do this */
-       ccb->ccb_h.status = CAM_REQ_INVALID;
-       break;
-    }
-    xpt_done(ccb);
+       KKASSERT(lockstatus(&sc->amr_list_lock, curthread) != 0);
+       xpt_done(ccb);
 }
 
-/********************************************************************************
- * Convert a CAM CCB off the top of the CCB queue to a passthrough SCSI command.
+/***********************************************************************
+ * Convert a CAM CCB off the top of the CCB queue to a passthrough SCSI
+ * command.
  */
-int
+static int
 amr_cam_command(struct amr_softc *sc, struct amr_command **acp)
 {
-    struct amr_command         *ac;
-    struct amr_passthrough             *ap;
-    struct amr_ext_passthrough *aep;
-    struct ccb_scsiio                  *csio;
-    int                                bus, target, error;
-
-    error = 0;
-    ac = NULL;
-    ap = NULL;
-    aep = NULL;
-
-    /* check to see if there is a ccb for us to work with */
-    if ((csio = (struct ccb_scsiio *)amr_dequeue_ccb(sc)) == NULL)
-       goto out;
-
-    /* get bus/target, XXX validate against protected devices? */
-    bus = csio->ccb_h.sim_priv.entries[0].field;
-    target = csio->ccb_h.target_id;
-
-    /*
-     * Build a passthrough command.
-     */
-
-    /* construct passthrough */
-    if (sc->support_ext_cdb ) {
-           aep = kmalloc(sizeof(*aep), M_DEVBUF, M_INTWAIT | M_ZERO);
-           aep->ap_timeout = 2;
-           aep->ap_ars = 1;
-           aep->ap_request_sense_length = 14;
-           aep->ap_islogical = 0;
-           aep->ap_channel = bus;
-           aep->ap_scsi_id = target;
-           aep->ap_logical_drive_no = csio->ccb_h.target_lun;
-           aep->ap_cdb_length = csio->cdb_len;
-           aep->ap_data_transfer_length = csio->dxfer_len;
-           if (csio->ccb_h.flags & CAM_CDB_POINTER) {
-               bcopy(csio->cdb_io.cdb_ptr, aep->ap_cdb, csio->cdb_len);
-           } else {
-               bcopy(csio->cdb_io.cdb_bytes, aep->ap_cdb, csio->cdb_len);
-           }
-           /* we leave the data s/g list and s/g count to the map routine later */
-
-           debug(2, " COMMAND %x/%d+%d to %d:%d:%d", aep->ap_cdb[0], aep->ap_cdb_length, csio->dxfer_len,
-                 aep->ap_channel, aep->ap_scsi_id, aep->ap_logical_drive_no);
-
-    } else {
-           ap = kmalloc(sizeof(*ap), M_DEVBUF, M_INTWAIT | M_ZERO);
-           ap->ap_timeout = 0;
-           ap->ap_ars = 1;
-           ap->ap_request_sense_length = 14;
-           ap->ap_islogical = 0;
-           ap->ap_channel = bus;
-           ap->ap_scsi_id = target;
-           ap->ap_logical_drive_no = csio->ccb_h.target_lun;
-           ap->ap_cdb_length = csio->cdb_len;
-           ap->ap_data_transfer_length = csio->dxfer_len;
-           if (csio->ccb_h.flags & CAM_CDB_POINTER) {
-               bcopy(csio->cdb_io.cdb_ptr, ap->ap_cdb, csio->cdb_len);
-           } else {
-               bcopy(csio->cdb_io.cdb_bytes, ap->ap_cdb, csio->cdb_len);
-           }
-           /* we leave the data s/g list and s/g count to the map routine later */
-
-           debug(2, " COMMAND %x/%d+%d to %d:%d:%d", ap->ap_cdb[0], ap->ap_cdb_length, csio->dxfer_len,
-                 ap->ap_channel, ap->ap_scsi_id, ap->ap_logical_drive_no);
-    }
-
-    /* construct command */
-    if ((ac = amr_alloccmd(sc)) == NULL) {
-       error = ENOMEM;
+       struct amr_command              *ac;
+       struct amr_passthrough          *ap;
+       struct amr_ext_passthrough      *aep;
+       struct ccb_scsiio               *csio;
+       int                             bus, target, error;
+
+       error = 0;
+       ac = NULL;
+       ap = NULL;
+       aep = NULL;
+
+       /* check to see if there is a ccb for us to work with */
+       if ((csio = (struct ccb_scsiio *)amr_dequeue_ccb(sc)) == NULL)
        goto out;
-    }
-
-    ac->ac_flags |= AMR_CMD_DATAOUT;
-
-    ac->ac_ccb_data = csio->data_ptr;
-    ac->ac_ccb_length = csio->dxfer_len;
-    if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
-       ac->ac_flags |= AMR_CMD_CCB_DATAIN;
-    if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
-       ac->ac_flags |= AMR_CMD_CCB_DATAOUT;
-
-    ac->ac_private = csio;
-    if ( sc->support_ext_cdb ) {
-           ac->ac_data = aep;
-           ac->ac_length = sizeof(*aep);
-           ac->ac_complete = amr_cam_complete_extcdb;
-           ac->ac_mailbox.mb_command = AMR_CMD_EXTPASS;
-    } else {
-           ac->ac_data = ap;
-           ac->ac_length = sizeof(*ap);
-           ac->ac_complete = amr_cam_complete;
-           ac->ac_mailbox.mb_command = AMR_CMD_PASS;
-    }
 
-out:
-    if (error != 0) {
-       if (ac != NULL)
-           amr_releasecmd(ac);
-       if (ap != NULL)
-           kfree(ap, M_DEVBUF);
-       if (aep != NULL)
-           kfree(aep, M_DEVBUF);
-       if (csio != NULL)                       /* put it back and try again later */
-           amr_requeue_ccb(sc, (union ccb *)csio);
-    }
-    *acp = ac;
-    return(error);
-}
-
-/********************************************************************************
- * Check for interrupt status
- */
-static void
-amr_cam_poll(struct cam_sim *sim)
-{
-    amr_done(cam_sim_softc(sim));
-}
+       /* get bus/target, XXX validate against protected devices? */
+       bus = csio->ccb_h.sim_priv.entries[0].field;
+       target = csio->ccb_h.target_id;
 
- /********************************************************************************
- * Handle completion of a command submitted via CAM.
- */
-static void
-amr_cam_complete(struct amr_command *ac)
-{
-    struct amr_passthrough      *ap = (struct amr_passthrough *)ac->ac_data;
-    struct ccb_scsiio           *csio = (struct ccb_scsiio *)ac->ac_private;
-    struct scsi_inquiry_data    *inq = (struct scsi_inquiry_data *)csio->data_ptr;
-
-    /* XXX note that we're ignoring ac->ac_status - good idea? */
+       /*
+        * Build a passthrough command.
+        */
 
-    debug(1, "status 0x%x  scsi_status 0x%x", ac->ac_status, ap->ap_scsi_status);
+       /* construct command */
+       if ((ac = amr_alloccmd(sc)) == NULL) {
+               error = ENOMEM;
+               goto out;
+       }
 
-    /*
-     * Hide disks from CAM so that they're not picked up and treated as 'normal' disks.
-     *
-     * If the configuration provides a mechanism to mark a disk a "not managed", we
-     * could add handling for that to allow disks to be selectively visible.
-     */
+       /* construct passthrough */
+       if (sc->support_ext_cdb ) {
+               aep = &ac->ac_ccb->ccb_epthru;
+               aep->ap_timeout = 2;
+               aep->ap_ars = 1;
+               aep->ap_request_sense_length = 14;
+               aep->ap_islogical = 0;
+               aep->ap_channel = bus;
+               aep->ap_scsi_id = target;
+               aep->ap_logical_drive_no = csio->ccb_h.target_lun;
+               aep->ap_cdb_length = csio->cdb_len;
+               aep->ap_data_transfer_length = csio->dxfer_len;
+               if (csio->ccb_h.flags & CAM_CDB_POINTER) {
+                       bcopy(csio->cdb_io.cdb_ptr, aep->ap_cdb, csio->cdb_len);
+               } else {
+                       bcopy(csio->cdb_io.cdb_bytes, aep->ap_cdb,
+                           csio->cdb_len);
+               }
+               /*
+                * we leave the data s/g list and s/g count to the map routine
+                * later
+                */
+
+               debug(2, " COMMAND %x/%d+%d to %d:%d:%d", aep->ap_cdb[0],
+                   aep->ap_cdb_length, csio->dxfer_len, aep->ap_channel,
+                   aep->ap_scsi_id, aep->ap_logical_drive_no);
 
-    if ((ap->ap_cdb[0] == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) {
-       bzero(csio->data_ptr, csio->dxfer_len);
-       if (ap->ap_scsi_status == 0xf0) {
-           csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
        } else {
-           csio->ccb_h.status = CAM_DEV_NOT_THERE;
+               ap = &ac->ac_ccb->ccb_pthru;
+               ap->ap_timeout = 0;
+               ap->ap_ars = 1;
+               ap->ap_request_sense_length = 14;
+               ap->ap_islogical = 0;
+               ap->ap_channel = bus;
+               ap->ap_scsi_id = target;
+               ap->ap_logical_drive_no = csio->ccb_h.target_lun;
+               ap->ap_cdb_length = csio->cdb_len;
+               ap->ap_data_transfer_length = csio->dxfer_len;
+               if (csio->ccb_h.flags & CAM_CDB_POINTER) {
+                       bcopy(csio->cdb_io.cdb_ptr, ap->ap_cdb, csio->cdb_len);
+               } else {
+                       bcopy(csio->cdb_io.cdb_bytes, ap->ap_cdb,
+                           csio->cdb_len);
+               }
+               /*
+                * we leave the data s/g list and s/g count to the map routine
+                * later
+                */
+
+               debug(2, " COMMAND %x/%d+%d to %d:%d:%d", ap->ap_cdb[0],
+                   ap->ap_cdb_length, csio->dxfer_len, ap->ap_channel,
+                   ap->ap_scsi_id, ap->ap_logical_drive_no);
        }
-    } else {
 
-       /* handle passthrough SCSI status */
-       switch(ap->ap_scsi_status) {
-       case 0:                         /* completed OK */
-           csio->ccb_h.status = CAM_REQ_CMP;
-           break;
+       ac->ac_flags |= AMR_CMD_CCB;
 
-       case 0x02:
-           csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
-           csio->scsi_status = SCSI_STATUS_CHECK_COND;
-           bcopy(ap->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN);
-           csio->sense_len = AMR_MAX_REQ_SENSE_LEN;
-           csio->ccb_h.status |= CAM_AUTOSNS_VALID;
-           break;
+       ac->ac_data = csio->data_ptr;
+       ac->ac_length = csio->dxfer_len;
+       if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
+               ac->ac_flags |= AMR_CMD_DATAIN;
+       if ((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
+               ac->ac_flags |= AMR_CMD_DATAOUT;
 
-       case 0x08:
-           csio->ccb_h.status = CAM_SCSI_BUSY;
-           break;
+       ac->ac_private = csio;
+       ac->ac_complete = amr_cam_complete;
+       if ( sc->support_ext_cdb ) {
+               ac->ac_mailbox.mb_command = AMR_CMD_EXTPASS;
+       } else {
+               ac->ac_mailbox.mb_command = AMR_CMD_PASS;
+       }
 
-       case 0xf0:
-       case 0xf4:
-       default:
-           csio->ccb_h.status = CAM_REQ_CMP_ERR;
-           break;
+out:
+       if (error != 0) {
+               if (ac != NULL)
+                       amr_releasecmd(ac);
+               if (csio != NULL)
+                       /* put it back and try again later */
+                       amr_requeue_ccb(sc, (union ccb *)csio);
        }
-    }
-    kfree(ap, M_DEVBUF);
-    if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
-       debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " ");
-    xpt_done((union ccb *)csio);
-    amr_releasecmd(ac);
+       *acp = ac;
+       return(error);
 }
 
-/********************************************************************************
- * Handle completion of a command submitted via CAM.
- * Completion for extended cdb
+/***********************************************************************
+ * Check for interrupt status
  */
 static void
-amr_cam_complete_extcdb(struct amr_command *ac)
+amr_cam_poll(struct cam_sim *sim)
 {
-    struct amr_ext_passthrough      *aep = (struct amr_ext_passthrough *)ac->ac_data;
-    struct ccb_scsiio           *csio = (struct ccb_scsiio *)ac->ac_private;
-    struct scsi_inquiry_data    *inq = (struct scsi_inquiry_data *)csio->data_ptr;
-
-    /* XXX note that we're ignoring ac->ac_status - good idea? */
-
-    debug(1, "status 0x%x  scsi_status 0x%x", ac->ac_status, aep->ap_scsi_status);
 
-    /*
-     * Hide disks from CAM so that they're not picked up and treated as 'normal' disks.
-     *
-     * If the configuration provides a mechanism to mark a disk a "not managed", we
-     * could add handling for that to allow disks to be selectively visible.
-     */
+       amr_done(cam_sim_softc(sim));
+}
 
-    if ((aep->ap_cdb[0] == INQUIRY) && (SID_TYPE(inq) == T_DIRECT)) {
-       bzero(csio->data_ptr, csio->dxfer_len);
-       if (aep->ap_scsi_status == 0xf0) {
-           csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
-       } else {
-           csio->ccb_h.status = CAM_DEV_NOT_THERE;
+ /**********************************************************************
+ * Handle completion of a command submitted via CAM.
+ */
+static void
+amr_cam_complete(struct amr_command *ac)
+{
+       struct amr_passthrough          *ap;
+       struct amr_ext_passthrough      *aep;
+       struct ccb_scsiio               *csio;
+       struct scsi_inquiry_data        *inq;
+       int                             scsi_status, cdb0;
+
+       ap = &ac->ac_ccb->ccb_pthru;
+       aep = &ac->ac_ccb->ccb_epthru;
+       csio = (struct ccb_scsiio *)ac->ac_private;
+       inq = (struct scsi_inquiry_data *)csio->data_ptr;
+
+       if (ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS)
+               scsi_status = aep->ap_scsi_status;
+       else
+               scsi_status = ap->ap_scsi_status;
+       debug(1, "status 0x%x  AP scsi_status 0x%x", ac->ac_status,
+           scsi_status);
+
+       /* Make sure the status is sane */
+       if ((ac->ac_status != AMR_STATUS_SUCCESS) && (scsi_status == 0)) {
+               csio->ccb_h.status = CAM_REQ_CMP_ERR;
+               goto out;
        }
-    } else {
+
+       /*
+        * Hide disks from CAM so that they're not picked up and treated as
+        * 'normal' disks.
+        *
+        * If the configuration provides a mechanism to mark a disk a "not
+        * managed", we could add handling for that to allow disks to be
+        * selectively visible.
+        */
 
        /* handle passthrough SCSI status */
-       switch(aep->ap_scsi_status) {
-       case 0:                         /* completed OK */
-           csio->ccb_h.status = CAM_REQ_CMP;
-           break;
+       switch(scsi_status) {
+       case 0: /* completed OK */
+               if (ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS)
+                       cdb0 = aep->ap_cdb[0];
+               else
+                       cdb0 = ap->ap_cdb[0];
+               if ((cdb0 == INQUIRY) && (SID_TYPE(inq) == T_DIRECT))
+                       inq->device = (inq->device & 0xe0) | T_NODEVICE;
+               csio->ccb_h.status = CAM_REQ_CMP;
+               break;
 
        case 0x02:
-           csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
-           csio->scsi_status = SCSI_STATUS_CHECK_COND;
-           bcopy(aep->ap_request_sense_area, &csio->sense_data, AMR_MAX_REQ_SENSE_LEN);
-           csio->sense_len = AMR_MAX_REQ_SENSE_LEN;
-           csio->ccb_h.status |= CAM_AUTOSNS_VALID;
-           break;
+               csio->ccb_h.status = CAM_SCSI_STATUS_ERROR;
+               csio->scsi_status = SCSI_STATUS_CHECK_COND;
+               if (ac->ac_mailbox.mb_command == AMR_CMD_EXTPASS)
+                       bcopy(aep->ap_request_sense_area, &csio->sense_data,
+                           AMR_MAX_REQ_SENSE_LEN);
+               else
+                       bcopy(ap->ap_request_sense_area, &csio->sense_data,
+                           AMR_MAX_REQ_SENSE_LEN);
+               csio->sense_len = AMR_MAX_REQ_SENSE_LEN;
+               csio->ccb_h.status |= CAM_AUTOSNS_VALID;
+               break;
 
        case 0x08:
-           csio->ccb_h.status = CAM_SCSI_BUSY;
-           break;
+               csio->ccb_h.status = CAM_SCSI_BUSY;
+               break;
 
        case 0xf0:
        case 0xf4:
        default:
-           csio->ccb_h.status = CAM_REQ_CMP_ERR;
-           break;
+               /*
+                * Non-zero LUNs are already filtered, so there's no need
+                * to return CAM_DEV_NOT_THERE.
+                */
+               csio->ccb_h.status = CAM_SEL_TIMEOUT;
+               break;
        }
-    }
-    kfree(aep, M_DEVBUF);
-    if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
-       debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr, " ");
-    xpt_done((union ccb *)csio);
-    amr_releasecmd(ac);
+
+out:
+       if ((csio->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
+               debug(2, "%*D\n", imin(csio->dxfer_len, 16), csio->data_ptr,
+                   " ");
+
+       lockmgr(&ac->ac_sc->amr_list_lock, LK_EXCLUSIVE);
+       xpt_done((union ccb *)csio);
+       amr_releasecmd(ac);
+       lockmgr(&ac->ac_sc->amr_list_lock, LK_RELEASE);
 }
diff --git a/sys/dev/raid/amr/amr_cam/Makefile b/sys/dev/raid/amr/amr_cam/Makefile
new file mode 100644 (file)
index 0000000..cdd3396
--- /dev/null
@@ -0,0 +1,9 @@
+# $FreeBSD: src/sys/modules/amr/amr_cam/Makefile,v 1.1 2008/11/03 04:13:27 scottl Exp $
+
+.PATH: ${.CURDIR}/..
+
+KMOD=  amr_cam
+SRCS=  amr_cam.c device_if.h bus_if.h
+SRCS+= opt_cam.h opt_scsi.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/dev/raid/amr/amr_compat.h b/sys/dev/raid/amr/amr_compat.h
deleted file mode 100644 (file)
index 88e3f86..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*-
- * Copyright (c) 2000 Michael Smith
- * Copyright (c) 2000 BSDi
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * Copyright (c) 2002 Eric Moore
- * Copyright (c) 2002 LSI Logic Corporation
- * 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.
- * 3. The party using or redistributing the source code and binary forms
- *    agrees to the disclaimer below and the terms and conditions set forth
- *    herein.
- *
- * 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/amr/amr_compat.h,v 1.2.2.4 2002/11/11 13:19:10 emoore Exp $
- * $DragonFly: src/sys/dev/raid/amr/amr_compat.h,v 1.7 2006/02/17 19:18:05 dillon Exp $
- */
-
-#include <sys/proc.h>
-#include <sys/buf.h>
-#include <machine/clock.h>
-#include <sys/buf2.h>
-
-#ifndef __packed
-#define __packed __attribute__ ((packed))
-#endif
index 999c0ea..a1df430 100644 (file)
@@ -24,7 +24,8 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
+ */
+/*-
  * Copyright (c) 2002 Eric Moore
  * Copyright (c) 2002 LSI Logic Corporation
  * All rights reserved.
@@ -53,7 +54,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/dev/amr/amr_disk.c,v 1.5.2.5 2002/12/20 15:12:04 emoore Exp $
+ * $FreeBSD: src/sys/dev/amr/amr_disk.c,v 1.39 2006/10/31 21:19:25 pjd Exp $
  */
 
 /*
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/module.h>
 
-#include "amr_compat.h"
+#include <sys/bio.h>
 #include <sys/bus.h>
 #include <sys/conf.h>
-#include <sys/devicestat.h>
-#include <sys/disk.h>
 #include <sys/dtype.h>
-#include <sys/rman.h>
 
-#include <vm/vm.h>
-#include <vm/pmap.h>
-
-#include <machine/md_var.h>
+#include <sys/rman.h>
 
-#include "amrio.h"
-#include "amrreg.h"
-#include "amrvar.h"
-#include "amr_tables.h"
+#include <dev/raid/amr/amrio.h>
+#include <dev/raid/amr/amrreg.h>
+#include <dev/raid/amr/amrvar.h>
+#include <dev/raid/amr/amr_tables.h>
 
 /* prototypes */
 static int amrd_probe(device_t dev);
@@ -88,23 +84,20 @@ static int amrd_attach(device_t dev);
 static int amrd_detach(device_t dev);
 
 static d_open_t        amrd_open;
-static d_close_t       amrd_close;
 static d_strategy_t    amrd_strategy;
-static d_ioctl_t       amrd_ioctl;
 static d_dump_t        amrd_dump;
 
 static struct dev_ops amrd_ops = {
        { "amrd", 0, D_DISK },
        .d_open = amrd_open,
-       .d_close = amrd_close,
-       .d_read = physread,
-       .d_write = physwrite,
-       .d_ioctl = amrd_ioctl,
        .d_strategy = amrd_strategy,
-       .d_dump = amrd_dump
+       .d_dump = amrd_dump,
 };
 
 static devclass_t      amrd_devclass;
+#ifdef FREEBSD_4
+int                    amr_disks_registered = 0;
+#endif
 
 static device_method_t amrd_methods[] = {
     DEVMETHOD(device_probe,    amrd_probe),
@@ -124,8 +117,7 @@ DRIVER_MODULE(amrd, amr, amrd_driver, amrd_devclass, 0, 0);
 static int
 amrd_open(struct dev_open_args *ap)
 {
-    cdev_t dev = ap->a_head.a_dev;
-    struct amrd_softc  *sc = (struct amrd_softc *)dev->si_drv1;
+    struct amrd_softc  *sc = ap->a_head.a_dev->si_drv1;
 
     debug_called(1);
 
@@ -135,73 +127,38 @@ amrd_open(struct dev_open_args *ap)
     /* controller not active? */
     if (sc->amrd_controller->amr_state & AMR_STATE_SHUTDOWN)
        return(ENXIO);
-#if 0
-    bzero(&info, sizeof(info));
-    info.d_media_blksize = AMR_BLKSIZE;                        /* optional */
-    info.d_media_blocks        = sc->amrd_drive->al_size;
 
-    info.d_type       = DTYPE_SCSI;                    /* mandatory */
-    info.d_secpertrack = sc->amrd_drive->al_sectors;
-    info.d_nheads      = sc->amrd_drive->al_heads;
-    info.d_ncylinders = sc->amrd_drive->al_cylinders;
-    info.d_secpercyl  = sc->amrd_drive->al_sectors * sc->amrd_drive->al_heads;
-
-    disk_setdiskinfo(&sc->amrd_disk, &info);
-#endif
-    sc->amrd_flags |= AMRD_OPEN;
     return (0);
 }
-
-static int
-amrd_close(struct dev_close_args *ap)
-{
-    cdev_t dev = ap->a_head.a_dev;
-    struct amrd_softc  *sc = (struct amrd_softc *)dev->si_drv1;
-
-    debug_called(1);
-
-    if (sc == NULL)
-       return (ENXIO);
-    sc->amrd_flags &= ~AMRD_OPEN;
-    return (0);
-}
-
-static int
-amrd_ioctl(struct dev_ioctl_args *ap)
-{
-    return (ENOTTY);
-}
-
-
 /********************************************************************************
  * System crashdump support
  */
-int
+
+static int
 amrd_dump(struct dev_dump_args *ap)
 {
     cdev_t dev = ap->a_head.a_dev;
-    struct amrd_softc  *amrd_sc = (struct amrd_softc *)dev->si_drv1;
+    off_t offset = ap->a_offset;
+    void *virtual = ap->a_virtual;
+    size_t length = ap->a_length;
+    struct amrd_softc  *amrd_sc;
     struct amr_softc   *amr_sc;
-    int                        error = 0;
-    int                        driveno;
+    int                        error;
 
-    debug_called(1);
-
-    amr_sc  = (struct amr_softc *)amrd_sc->amrd_controller;
-
-    if (!amrd_sc || !amr_sc)
+    amrd_sc = (struct amrd_softc *)dev->si_drv1;
+    if (amrd_sc == NULL)
        return(ENXIO);
+    amr_sc  = (struct amr_softc *)amrd_sc->amrd_controller;
 
-    driveno = amrd_sc->amrd_drive - amr_sc->amr_drive;
-
-    if (ap->a_length > 0) {
-       if ((error = amr_dump_blocks(amr_sc,driveno,ap->a_offset / AMR_BLKSIZE,
-           (void *)ap->a_virtual,(int) ap->a_length / AMR_BLKSIZE  )) != 0)
+    if (length > 0) {
+       int     driveno = amrd_sc->amrd_drive - amr_sc->amr_drive;
+       if ((error = amr_dump_blocks(amr_sc,driveno,offset / AMR_BLKSIZE ,(void *)virtual,(int) length / AMR_BLKSIZE  )) != 0)
                return(error);
+
     }
     return(0);
-
 }
+
 /*
  * Read/write routine for a buffer.  Finds the proper unit, range checks
  * arguments, and schedules the transfer.  Does not wait for the transfer
@@ -225,7 +182,7 @@ amrd_strategy(struct dev_strategy_args *ap)
 
     devstat_start_transaction(&sc->amrd_stats);
     amr_submit_bio(sc->amrd_controller, bio);
-    return(0);
+    return (0);
 
  bad:
     bp->b_flags |= B_ERROR;
@@ -235,12 +192,13 @@ amrd_strategy(struct dev_strategy_args *ap)
      */
     bp->b_resid = bp->b_bcount;
     biodone(bio);
-    return(0);
+    return (0);
 }
 
 void
-amrd_intr(struct bio *bio)
+amrd_intr(void *data)
 {
+    struct bio *bio = (struct bio *)data;
     struct buf *bp = bio->bio_buf;
 
     debug_called(2);
@@ -251,6 +209,7 @@ amrd_intr(struct bio *bio)
     } else {
        bp->b_resid = 0;
     }
+
     biodone(bio);
 }
 
@@ -267,10 +226,10 @@ amrd_probe(device_t dev)
 static int
 amrd_attach(device_t dev)
 {
-       struct disk_info info;
     struct amrd_softc  *sc = (struct amrd_softc *)device_get_softc(dev);
     device_t           parent;
-    
+    struct disk_info info;
+
     debug_called(1);
 
     parent = device_get_parent(dev);
@@ -295,19 +254,19 @@ amrd_attach(device_t dev)
     /* set maximum I/O size to match the maximum s/g size */
     sc->amrd_dev_t->si_iosize_max = (AMR_NSEG - 1) * PAGE_SIZE;
 
-       /*
-        * Set disk info, as it appears that all needed data is available already.
-        * Setting the disk info will also cause the probing to start.
-        */
-       bzero(&info, sizeof(info));
+    /*
+     * Set disk info, as it appears that all needed data is available already.
+     * Setting the disk info will also cause the probing to start.
+     */
+    bzero(&info, sizeof(info));
     info.d_media_blksize = AMR_BLKSIZE;                        /* optional */
-    info.d_media_blocks        = sc->amrd_drive->al_size;
+    info.d_media_blocks = sc->amrd_drive->al_size;
 
-    info.d_type       = DTYPE_SCSI;                    /* mandatory */
+    info.d_type = DTYPE_SCSI;                          /* mandatory */
     info.d_secpertrack = sc->amrd_drive->al_sectors;
-    info.d_nheads      = sc->amrd_drive->al_heads;
+    info.d_nheads = sc->amrd_drive->al_heads;
     info.d_ncylinders = sc->amrd_drive->al_cylinders;
-    info.d_secpercyl  = sc->amrd_drive->al_sectors * sc->amrd_drive->al_heads;
+    info.d_secpercyl = sc->amrd_drive->al_sectors * sc->amrd_drive->al_heads;
 
     disk_setdiskinfo(&sc->amrd_disk, &info);
 
@@ -321,11 +280,18 @@ amrd_detach(device_t dev)
 
     debug_called(1);
 
-    if (sc->amrd_flags & AMRD_OPEN)
+#if 0 /* XXX swildner */
+    if (sc->amrd_disk->d_flags & DISKFLAG_OPEN)
        return(EBUSY);
+#endif
 
     devstat_remove_entry(&sc->amrd_stats);
+#ifdef FREEBSD_4
+    if (--amr_disks_registered == 0)
+       cdevsw_remove(&amrddisk_cdevsw);
+#else
     disk_destroy(&sc->amrd_disk);
+#endif
     return(0);
 }
 
diff --git a/sys/dev/raid/amr/amr_linux.c b/sys/dev/raid/amr/amr_linux.c
new file mode 100644 (file)
index 0000000..362a166
--- /dev/null
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 2005 Paul Saab
+ * 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/amr/amr_linux.c,v 1.5 2009/05/20 17:29:21 imp Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+
+#if defined(__amd64__) /* Assume amd64 wants 32 bit Linux */
+#include <machine/../linux32/linux.h>
+#include <machine/../linux32/linux32_proto.h>
+#else
+#include <emulation/linux/i386/linux.h>
+#include <emulation/linux/i386/linux_proto.h>
+#endif
+#include <emulation/linux/linux_ioctl.h>
+
+/* There are multiple ioctl number ranges that need to be handled */
+#define AMR_LINUX_IOCTL_MIN  0x6d00
+#define AMR_LINUX_IOCTL_MAX  0x6d01
+
+static linux_ioctl_function_t amr_linux_ioctl;
+static struct linux_ioctl_handler amr_linux_handler = {amr_linux_ioctl,
+                                                      AMR_LINUX_IOCTL_MIN,
+                                                      AMR_LINUX_IOCTL_MAX};
+
+SYSINIT  (amr_register,   SI_SUB_KLD, SI_ORDER_MIDDLE,
+         linux_ioctl_register_handler, &amr_linux_handler);
+SYSUNINIT(amr_unregister, SI_SUB_KLD, SI_ORDER_MIDDLE,
+         linux_ioctl_unregister_handler, &amr_linux_handler);
+
+static int
+amr_linux_modevent(module_t mod, int cmd, void *data)
+{
+       return (0);
+}
+
+DEV_MODULE(amr_linux, amr_linux_modevent, NULL);
+MODULE_DEPEND(amr, linux, 1, 1, 1);
+
+static int
+amr_linux_ioctl(struct thread *p, struct linux_ioctl_args *args)
+{
+       struct file *fp;
+       int error;
+
+       if ((error = fget(p, args->fd, &fp)) != 0)
+               return (error);
+       error = fo_ioctl(fp, args->cmd, (caddr_t)args->arg, p->td_ucred, p);
+       fdrop(fp, p);
+       return (error);
+}
diff --git a/sys/dev/raid/amr/amr_linux/Makefile b/sys/dev/raid/amr/amr_linux/Makefile
new file mode 100644 (file)
index 0000000..ec14676
--- /dev/null
@@ -0,0 +1,8 @@
+# $FreeBSD: src/sys/modules/amr/amr_linux/Makefile,v 1.1 2006/01/24 21:13:49 ambrisko Exp $
+
+.PATH: ${.CURDIR}/..
+
+KMOD=  amr_linux
+SRCS=  amr_linux.c device_if.h bus_if.h
+
+.include <bsd.kmod.mk>
index 76c060d..0ee0937 100644 (file)
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
+ */
+/*-
  * Copyright (c) 2002 Eric Moore
- * Copyright (c) 2002 LSI Logic Corporation
+ * Copyright (c) 2002, 2004 LSI Logic Corporation
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     $FreeBSD: src/sys/dev/amr/amr_pci.c,v 1.1.2.9 2002/12/20 15:12:04 emoore Exp $
- *     $DragonFly: src/sys/dev/raid/amr/amr_pci.c,v 1.9 2006/12/22 23:26:23 swildner Exp $
+ * $FreeBSD: src/sys/dev/amr/amr_pci.c,v 1.40 2007/12/12 05:55:03 scottl Exp $
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
 
-#include "amr_compat.h"
+#include <sys/bio.h>
 #include <sys/bus.h>
 #include <sys/conf.h>
-#include <sys/devicestat.h>
-#include <sys/disk.h>
+
 #include <sys/rman.h>
 
 #include <bus/pci/pcireg.h>
 #include <bus/pci/pcivar.h>
 
-#include "amrio.h"
-#include "amrreg.h"
-#include "amrvar.h"
+#include <dev/raid/amr/amrio.h>
+#include <dev/raid/amr/amrreg.h>
+#include <dev/raid/amr/amrvar.h>
 
 static int             amr_pci_probe(device_t dev);
 static int             amr_pci_attach(device_t dev);
@@ -82,10 +83,16 @@ static int              amr_pci_suspend(device_t dev);
 static int              amr_pci_resume(device_t dev);
 static void            amr_pci_intr(void *arg);
 static void            amr_pci_free(struct amr_softc *sc);
-static void            amr_sglist_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
+static void            amr_sglist_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
 static int             amr_sglist_map(struct amr_softc *sc);
-static void            amr_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
 static int             amr_setup_mbox(struct amr_softc *sc);
+static int             amr_ccb_map(struct amr_softc *sc);
+
+static u_int amr_force_sg32 = 0;
+TUNABLE_INT("hw.amr.force_sg32", &amr_force_sg32);
+SYSCTL_DECL(_hw_amr);
+SYSCTL_UINT(_hw_amr, OID_AUTO, force_sg32, CTLFLAG_RD, &amr_force_sg32, 0,
+    "Force the AMR driver to use 32bit scatter gather");
 
 static device_method_t amr_methods[] = {
     /* Device interface */
@@ -109,46 +116,64 @@ static driver_t amr_pci_driver = {
 
 static devclass_t      amr_devclass;
 DRIVER_MODULE(amr, pci, amr_pci_driver, amr_devclass, 0, 0);
+MODULE_DEPEND(amr, pci, 1, 1, 1);
+MODULE_DEPEND(amr, cam, 1, 1, 1);
 
-static struct
+static struct amr_ident
 {
     int                vendor;
     int                device;
-    int                flag;
-#define PROBE_SIGNATURE        (1<<0)
+    int                flags;
+#define AMR_ID_PROBE_SIG       (1<<0)  /* generic i960RD, check signature */
+#define AMR_ID_DO_SG64         (1<<1)
+#define AMR_ID_QUARTZ          (1<<2)
 } amr_device_ids[] = {
     {0x101e, 0x9010, 0},
     {0x101e, 0x9060, 0},
-    {0x8086, 0x1960, PROBE_SIGNATURE},/* generic i960RD, check for signature */
-    {0x101e, 0x1960, 0},
-    {0x1000, 0x1960, PROBE_SIGNATURE},
-    {0x1000, 0x0407, 0},
-    {0x1028, 0x000e, PROBE_SIGNATURE}, /* perc4/di i960 */
-    {0x1028, 0x000f, 0}, /* perc4/di Verde*/
+    {0x8086, 0x1960, AMR_ID_QUARTZ | AMR_ID_PROBE_SIG},
+    {0x101e, 0x1960, AMR_ID_QUARTZ},
+    {0x1000, 0x1960, AMR_ID_QUARTZ | AMR_ID_DO_SG64 | AMR_ID_PROBE_SIG},
+    {0x1000, 0x0407, AMR_ID_QUARTZ | AMR_ID_DO_SG64},
+    {0x1000, 0x0408, AMR_ID_QUARTZ | AMR_ID_DO_SG64},
+    {0x1000, 0x0409, AMR_ID_QUARTZ | AMR_ID_DO_SG64},
+    {0x1028, 0x000e, AMR_ID_QUARTZ | AMR_ID_DO_SG64 | AMR_ID_PROBE_SIG}, /* perc4/di i960 */
+    {0x1028, 0x000f, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, /* perc4/di Verde*/
+    {0x1028, 0x0013, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, /* perc4/di */
     {0, 0, 0}
 };
 
-static int
-amr_pci_probe(device_t dev)
+static struct amr_ident *
+amr_find_ident(device_t dev)
 {
-    int                i, sig;
-
-    debug_called(1);
+    struct amr_ident *id;
+    int sig;
 
-    for (i = 0; amr_device_ids[i].vendor != 0; i++) {
-       if ((pci_get_vendor(dev) == amr_device_ids[i].vendor) &&
-           (pci_get_device(dev) == amr_device_ids[i].device)) {
+    for (id = amr_device_ids; id->vendor != 0; id++) {
+       if ((pci_get_vendor(dev) == id->vendor) &&
+           (pci_get_device(dev) == id->device)) {
 
            /* do we need to test for a signature? */
-           if (amr_device_ids[i].flag & PROBE_SIGNATURE) {
+           if (id->flags & AMR_ID_PROBE_SIG) {
                sig = pci_read_config(dev, AMR_CFG_SIG, 2);
                if ((sig != AMR_SIGNATURE_1) && (sig != AMR_SIGNATURE_2))
                    continue;
            }
-           device_set_desc(dev, "LSILogic MegaRAID");
-           return(-10);        /* allow room to be overridden */
+           return (id);
        }
     }
+    return (NULL);
+}
+
+static int
+amr_pci_probe(device_t dev)
+{
+
+    debug_called(1);
+
+    if (amr_find_ident(dev) != NULL) {
+       device_set_desc(dev, LSI_DESC_PCI);
+       return(BUS_PROBE_DEFAULT);
+    }
     return(ENXIO);
 }
 
@@ -156,6 +181,7 @@ static int
 amr_pci_attach(device_t dev)
 {
     struct amr_softc   *sc;
+    struct amr_ident   *id;
     int                        rid, rtype, error;
     u_int32_t          command;
 
@@ -174,28 +200,35 @@ amr_pci_attach(device_t dev)
     /*
      * Determine board type.
      */
+    if ((id = amr_find_ident(dev)) == NULL)
+       return (ENXIO);
+
     command = pci_read_config(dev, PCIR_COMMAND, 1);
-    if ((pci_get_device(dev) == 0x1960) || (pci_get_device(dev) == 0x0407) ||
-       (pci_get_device(dev) == 0x000e) || (pci_get_device(dev) == 0x000f)) {
+    if (id->flags & AMR_ID_QUARTZ) {
        /*
         * Make sure we are going to be able to talk to this board.
         */
        if ((command & PCIM_CMD_MEMEN) == 0) {
            device_printf(dev, "memory window not available\n");
-           goto out;
+           return (ENXIO);
        }
        sc->amr_type |= AMR_TYPE_QUARTZ;
-
     } else {
        /*
         * Make sure we are going to be able to talk to this board.
         */
        if ((command & PCIM_CMD_PORTEN) == 0) {
            device_printf(dev, "I/O window not available\n");
-           goto out;
+           return (ENXIO);
        }
     }
 
+    if ((amr_force_sg32 == 0) && (id->flags & AMR_ID_DO_SG64) &&
+       (sizeof(vm_paddr_t) > 4)) {
+       device_printf(dev, "Using 64-bit DMA\n");
+       sc->amr_type |= AMR_TYPE_SG64;
+    }
+
     /* force the busmaster enable bit on */
     if (!(command & PCIM_CMD_BUSMASTEREN)) {
        device_printf(dev, "busmaster bit not set, enabling\n");
@@ -206,9 +239,9 @@ amr_pci_attach(device_t dev)
     /*
      * Allocate the PCI register window.
      */
-    rid = PCIR_MAPS;
+    rid = PCIR_BAR(0);
     rtype = AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT;
-    sc->amr_reg = bus_alloc_resource(dev, rtype, &rid, 0, ~0, 1, RF_ACTIVE);
+    sc->amr_reg = bus_alloc_resource_any(dev, rtype, &rid, RF_ACTIVE);
     if (sc->amr_reg == NULL) {
        device_printf(sc->amr_dev, "can't allocate register window\n");
        goto out;
@@ -220,15 +253,15 @@ amr_pci_attach(device_t dev)
      * Allocate and connect our interrupt.
      */
     rid = 0;
-    sc->amr_irq = bus_alloc_resource(sc->amr_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
+    sc->amr_irq = bus_alloc_resource_any(sc->amr_dev, SYS_RES_IRQ, &rid,
+        RF_SHAREABLE | RF_ACTIVE);
     if (sc->amr_irq == NULL) {
         device_printf(sc->amr_dev, "can't allocate interrupt\n");
        goto out;
     }
-    error = bus_setup_intr(sc->amr_dev, sc->amr_irq, 
-                          0, amr_pci_intr, sc,
-                          &sc->amr_intr, NULL);
-    if (error) {
+    if (bus_setup_intr(sc->amr_dev, sc->amr_irq,
+       0, amr_pci_intr,
+       sc, &sc->amr_intr, NULL)) {
         device_printf(sc->amr_dev, "can't set up interrupt\n");
        goto out;
     }
@@ -242,13 +275,15 @@ amr_pci_attach(device_t dev)
      * Allocate the parent bus DMA tag appropriate for PCI.
      */
     if (bus_dma_tag_create(NULL,                       /* parent */
-                          1, 0,                        /* alignment, boundary */
+                          1, 0,                        /* alignment,boundary */
+                          AMR_IS_SG64(sc) ?
+                          BUS_SPACE_MAXADDR :
                           BUS_SPACE_MAXADDR_32BIT,     /* lowaddr */
                           BUS_SPACE_MAXADDR,           /* highaddr */
                           NULL, NULL,                  /* filter, filterarg */
                           MAXBSIZE, AMR_NSEG,          /* maxsize, nsegments */
                           BUS_SPACE_MAXSIZE_32BIT,     /* maxsegsize */
-                          BUS_DMA_ALLOCNOW,            /* flags */
+                          0,                           /* flags */
                           &sc->amr_parent_dmat)) {
        device_printf(dev, "can't allocate parent DMA tag\n");
        goto out;
@@ -258,23 +293,38 @@ amr_pci_attach(device_t dev)
      * Create DMA tag for mapping buffers into controller-addressable space.
      */
     if (bus_dma_tag_create(sc->amr_parent_dmat,                /* parent */
-                          1, 0,                        /* alignment, boundary */
-                          BUS_SPACE_MAXADDR,           /* lowaddr */
+                          1, 0,                        /* alignment,boundary */
+                          BUS_SPACE_MAXADDR_32BIT,     /* lowaddr */
                           BUS_SPACE_MAXADDR,           /* highaddr */
                           NULL, NULL,                  /* filter, filterarg */
                           MAXBSIZE, AMR_NSEG,          /* maxsize, nsegments */
-                          BUS_SPACE_MAXSIZE_32BIT,     /* maxsegsize */
-                          0,                           /* flags */
+                          MAXBSIZE,                    /* maxsegsize */
+                          0,           /* flags */
                           &sc->amr_buffer_dmat)) {
         device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n");
        goto out;
     }
 
+    if (bus_dma_tag_create(sc->amr_parent_dmat,                /* parent */
+                          1, 0,                        /* alignment,boundary */
+                          BUS_SPACE_MAXADDR,           /* lowaddr */
+                          BUS_SPACE_MAXADDR,           /* highaddr */
+                          NULL, NULL,                  /* filter, filterarg */
+                          MAXBSIZE, AMR_NSEG,          /* maxsize, nsegments */
+                          MAXBSIZE,                    /* maxsegsize */
+                          0,           /* flags */
+                          &sc->amr_buffer64_dmat)) {
+        device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n");
+       goto out;
+    }
+
     debug(2, "dma tag done");
 
     /*
      * Allocate and set up mailbox in a bus-visible fashion.
      */
+    lockinit(&sc->amr_list_lock, "AMR List Lock", 0, LK_CANRECURSE);
+    lockinit(&sc->amr_hw_lock, "AMR HW Lock", 0, LK_CANRECURSE);
     if ((error = amr_setup_mbox(sc)) != 0)
        goto out;
 
@@ -285,9 +335,13 @@ amr_pci_attach(device_t dev)
      */
     if (amr_sglist_map(sc))
        goto out;
-
     debug(2, "s/g list mapped");
 
+    if (amr_ccb_map(sc))
+       goto out;
+    debug(2, "ccb mapped");
+
+
     /*
      * Do bus-independant initialisation, bring controller online.
      */
@@ -347,7 +401,6 @@ amr_pci_shutdown(device_t dev)
     device_printf(sc->amr_dev, "flushing cache...");
     kprintf("%s\n", amr_flush(sc) ? "failed" : "done");
 
-    crit_enter();
     error = 0;
 
     /* delete all our child devices */
@@ -362,7 +415,6 @@ amr_pci_shutdown(device_t dev)
     /* XXX disable interrupts? */
 
 shutdown_out:
-    crit_exit();
     return(error);
 }
 
@@ -413,7 +465,7 @@ amr_pci_intr(void *arg)
 {
     struct amr_softc   *sc = (struct amr_softc *)arg;
 
-    debug_called(2);
+    debug_called(3);
 
     /* collect finished commands, queue anything waiting */
     amr_done(sc);
@@ -427,8 +479,8 @@ amr_pci_intr(void *arg)
 static void
 amr_pci_free(struct amr_softc *sc)
 {
-    u_int8_t                   *p;
-    
+    void *p;
+
     debug_called(1);
 
     amr_free(sc);
@@ -436,6 +488,14 @@ amr_pci_free(struct amr_softc *sc)
     /* destroy data-transfer DMA tag */
     if (sc->amr_buffer_dmat)
        bus_dma_tag_destroy(sc->amr_buffer_dmat);
+    if (sc->amr_buffer64_dmat)
+       bus_dma_tag_destroy(sc->amr_buffer64_dmat);
+
+    /* free and destroy DMA memory and tag for passthrough pool */
+    if (sc->amr_ccb)
+       bus_dmamem_free(sc->amr_ccb_dmat, sc->amr_ccb, sc->amr_ccb_dmamap);
+    if (sc->amr_ccb_dmat)
+       bus_dma_tag_destroy(sc->amr_ccb_dmat);
 
     /* free and destroy DMA memory and tag for s/g lists */
     if (sc->amr_sgtable)
@@ -444,9 +504,9 @@ amr_pci_free(struct amr_softc *sc)
        bus_dma_tag_destroy(sc->amr_sg_dmat);
 
     /* free and destroy DMA memory and tag for mailbox */
+    p = (void *)(uintptr_t)(volatile void *)sc->amr_mailbox64;
     if (sc->amr_mailbox) {
-       p = (u_int8_t *)(uintptr_t)(volatile void *)sc->amr_mailbox;
-       bus_dmamem_free(sc->amr_mailbox_dmat, p - 16, sc->amr_mailbox_dmamap);
+       bus_dmamem_free(sc->amr_mailbox_dmat, p, sc->amr_mailbox_dmamap);
     }
     if (sc->amr_mailbox_dmat)
        bus_dma_tag_destroy(sc->amr_mailbox_dmat);
@@ -465,27 +525,28 @@ amr_pci_free(struct amr_softc *sc)
     if (sc->amr_reg != NULL)
        bus_release_resource(sc->amr_dev,
                             AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT,
-                            PCIR_MAPS, sc->amr_reg);
+                            PCIR_BAR(0), sc->amr_reg);
 }
 
 /********************************************************************************
  * Allocate and map the scatter/gather table in bus space.
  */
 static void
-amr_sglist_map_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+amr_sglist_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
 {
-    struct amr_softc   *sc = (struct amr_softc *)arg;
+    uint32_t *addr;
 
     debug_called(1);
 
-    /* save base of s/g table's address in bus space */
-    sc->amr_sgbusaddr = segs->ds_addr;
+    addr = arg;
+    *addr = segs[0].ds_addr;
 }
 
 static int
 amr_sglist_map(struct amr_softc *sc)
 {
     size_t     segsize;
+    void       *p;
     int                error;
 
     debug_called(1);
@@ -494,12 +555,17 @@ amr_sglist_map(struct amr_softc *sc)
      * Create a single tag describing a region large enough to hold all of
      * the s/g lists we will need.
      *
-     * Note that we could probably use AMR_LIMITCMD here, but that may become tunable.
+     * Note that we could probably use AMR_LIMITCMD here, but that may become
+     * tunable.
      */
-    segsize = sizeof(struct amr_sgentry) * AMR_NSEG * AMR_MAXCMD;
+    if (AMR_IS_SG64(sc))
+       segsize = sizeof(struct amr_sg64entry) * AMR_NSEG * AMR_MAXCMD;
+    else
+       segsize = sizeof(struct amr_sgentry) * AMR_NSEG * AMR_MAXCMD;
+
     error = bus_dma_tag_create(sc->amr_parent_dmat,    /* parent */
-                              1, 0,                    /* alignment, boundary */
-                              BUS_SPACE_MAXADDR,       /* lowaddr */
+                              512, 0,                  /* alignment,boundary */
+                              BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
                               BUS_SPACE_MAXADDR,       /* highaddr */
                               NULL, NULL,              /* filter, filterarg */
                               segsize, 1,              /* maxsize, nsegments */
@@ -516,50 +582,46 @@ amr_sglist_map(struct amr_softc *sc)
      * controller-visible space.
      * 
      * XXX this assumes we can get enough space for all the s/g maps in one 
-     * contiguous slab.  We may need to switch to a more complex arrangement where
-     * we allocate in smaller chunks and keep a lookup table from slot to bus address.
+     * contiguous slab.  We may need to switch to a more complex arrangement
+     * where we allocate in smaller chunks and keep a lookup table from slot
+     * to bus address.
      *
-     * XXX HACK ALERT: at least some controllers don't like the s/g memory being
-     *                 allocated below 0x2000.  We leak some memory if we get some
-     *                 below this mark and allocate again.  We should be able to
-     *                avoid this with the tag setup, but that does't seem to work.
+     * XXX HACK ALERT: at least some controllers don't like the s/g memory
+     *                 being allocated below 0x2000.  We leak some memory if
+     *                 we get some below this mark and allocate again.  We
+     *                 should be able to avoid this with the tag setup, but
+     *                 that does't seem to work.
      */
 retry:
-    error = bus_dmamem_alloc(sc->amr_sg_dmat, (void *)&sc->amr_sgtable, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap);
+    error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&p, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap);
     if (error) {
        device_printf(sc->amr_dev, "can't allocate s/g table\n");
        return(ENOMEM);
     }
-    bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, sc->amr_sgtable, segsize, amr_sglist_map_helper, sc, 0);
+    bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, p, segsize, amr_sglist_helper, &sc->amr_sgbusaddr, 0);
     if (sc->amr_sgbusaddr < 0x2000) {
        debug(1, "s/g table too low (0x%x), reallocating\n", sc->amr_sgbusaddr);
        goto retry;
     }
+
+    if (AMR_IS_SG64(sc))
+       sc->amr_sg64table = (struct amr_sg64entry *)p;
+    sc->amr_sgtable = (struct amr_sgentry *)p;
+
     return(0);
 }
 
 /********************************************************************************
  * Allocate and set up mailbox areas for the controller (sc)
  *
- * The basic mailbox structure should be 16-byte aligned.  This means that the
- * mailbox64 structure has 4 bytes hanging off the bottom.
+ * The basic mailbox structure should be 16-byte aligned.
  */
-static void
-amr_setup_mbox_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
-{
-    struct amr_softc   *sc = (struct amr_softc *)arg;
-    
-    debug_called(1);
-
-    /* save phsyical base of the basic mailbox structure */
-    sc->amr_mailboxphys = segs->ds_addr + 16;
-}
-
 static int
 amr_setup_mbox(struct amr_softc *sc)
 {
     int                error;
-    u_int8_t   *p;
+    void       *p;
+    uint32_t   baddr;
     
     debug_called(1);
 
@@ -568,11 +630,12 @@ amr_setup_mbox(struct amr_softc *sc)
      * mailbox.
      */
     error = bus_dma_tag_create(sc->amr_parent_dmat,    /* parent */
-                              16, 0,                   /* alignment, boundary */
-                              BUS_SPACE_MAXADDR,       /* lowaddr */
+                              16, 0,                   /* alignment,boundary */
+                              BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
                               BUS_SPACE_MAXADDR,       /* highaddr */
                               NULL, NULL,              /* filter, filterarg */
-                              sizeof(struct amr_mailbox) + 16, 1, /* maxsize, nsegments */
+                              sizeof(struct amr_mailbox64), /* maxsize */
+                              1,                       /* nsegments */
                               BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
                               0,                       /* flags */
                               &sc->amr_mailbox_dmat);
@@ -585,20 +648,60 @@ amr_setup_mbox(struct amr_softc *sc)
      * Allocate the mailbox structure and permanently map it into
      * controller-visible space.
      */
-    error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void *)&p, BUS_DMA_NOWAIT,
+    error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void **)&p, BUS_DMA_NOWAIT,
                             &sc->amr_mailbox_dmamap);
     if (error) {
        device_printf(sc->amr_dev, "can't allocate mailbox memory\n");
        return(ENOMEM);
     }
-    bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p, 
-                   sizeof(struct amr_mailbox64), amr_setup_mbox_helper, sc, 0);
+    bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p,
+                   sizeof(struct amr_mailbox64), amr_sglist_helper, &baddr, 0);
     /*
      * Conventional mailbox is inside the mailbox64 region.
      */
+    /* save physical base of the basic mailbox structure */
+    sc->amr_mailboxphys = baddr + offsetof(struct amr_mailbox64, mb);
     bzero(p, sizeof(struct amr_mailbox64));
-    sc->amr_mailbox64 = (struct amr_mailbox64 *)(p + 12);
-    sc->amr_mailbox = (struct amr_mailbox *)(p + 16);
+    sc->amr_mailbox64 = (struct amr_mailbox64 *)p;
+    sc->amr_mailbox = &sc->amr_mailbox64->mb;
 
     return(0);
 }
+
+static int
+amr_ccb_map(struct amr_softc *sc)
+{
+    int                ccbsize, error;
+
+    /*
+     * Passthrough and Extended passthrough structures will share the same
+     * memory.
+     */
+    ccbsize = sizeof(union amr_ccb) * AMR_MAXCMD;
+    error = bus_dma_tag_create(sc->amr_parent_dmat,    /* parent */
+                               128, 0,                 /* alignment,boundary */
+                               BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
+                               BUS_SPACE_MAXADDR,      /* highaddr */
+                               NULL, NULL,             /* filter, filterarg */
+                               ccbsize,                /* maxsize */
+                               1,                      /* nsegments */
+                               ccbsize,                /* maxsegsize */
+                               0,                      /* flags */
+                               &sc->amr_ccb_dmat);
+    if (error != 0) {
+       device_printf(sc->amr_dev, "can't allocate ccb tag\n");
+       return (ENOMEM);
+    }
+
+    error = bus_dmamem_alloc(sc->amr_ccb_dmat, (void **)&sc->amr_ccb,
+                            BUS_DMA_NOWAIT, &sc->amr_ccb_dmamap);
+    if (error) {
+       device_printf(sc->amr_dev, "can't allocate ccb memory\n");
+       return (ENOMEM);
+    }
+    bus_dmamap_load(sc->amr_ccb_dmat, sc->amr_ccb_dmamap, sc->amr_ccb,
+                   ccbsize, amr_sglist_helper, &sc->amr_ccb_busaddr, 0);
+    bzero(sc->amr_ccb, ccbsize);
+
+    return (0);
+}
index 1eef464..914438e 100644 (file)
@@ -52,8 +52,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     $FreeBSD: src/sys/dev/amr/amr_tables.h,v 1.1.2.3 2002/11/11 13:19:10 emoore Exp $
- *     $DragonFly: src/sys/dev/raid/amr/amr_tables.h,v 1.2 2003/06/17 04:28:22 dillon Exp $
+ *
+ *     $FreeBSD: src/sys/dev/amr/amr_tables.h,v 1.3 2002/10/30 22:00:11 emoore Exp $
  */
 
 /*
index 0c75da7..8849fe4 100644 (file)
@@ -51,8 +51,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *     $FreeBSD: src/sys/dev/amr/amrio.h,v 1.2.2.3 2002/11/11 13:19:10 emoore Exp $
- *     $DragonFly: src/sys/dev/raid/amr/amrio.h,v 1.2 2003/06/17 04:28:22 dillon Exp $
+ *
+ *     $FreeBSD: src/sys/dev/amr/amrio.h,v 1.7 2005/12/14 03:26:49 scottl Exp $
  */
 
 /*
  */
 
 #include <sys/ioccom.h>
+#include <sys/param.h>
 
 /*
  * Fetch the driver's interface version.
  */
-#define AMR_IO_VERSION_NUMBER  0x01
+#define AMR_IO_VERSION_NUMBER  153
 #define AMR_IO_VERSION _IOR('A', 0x200, int)
 
 /*
@@ -107,3 +108,15 @@ struct amr_user_ioctl {
 
 #define AMR_IO_COMMAND _IOWR('A', 0x201, struct amr_user_ioctl)
 
+#if defined(__amd64__) || defined(__ia64__)
+
+struct amr_user_ioctl32 {
+    unsigned char      au_cmd[32];     /* command text from userspace */
+    u_int32_t          au_buffer;      /* 32-bit pointer to uspace buf */
+    u_int32_t          au_length;      /* length of the uspace buffer */
+    int32_t            au_direction;   /* data transfer direction */
+    int32_t            au_status;      /* command status returned by adapter */
+};
+
+#      define AMR_IO_COMMAND32 _IOWR('A', 0x201, struct amr_user_ioctl32)
+#endif
index 7a8ae87..051f65f 100644 (file)
@@ -52,8 +52,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *      $FreeBSD: src/sys/dev/amr/amrreg.h,v 1.1.2.4 2002/11/11 13:19:10 emoore Exp $
- *      $DragonFly: src/sys/dev/raid/amr/amrreg.h,v 1.2 2003/06/17 04:28:22 dillon Exp $
+ *
+ *      $FreeBSD: src/sys/dev/amr/amrreg.h,v 1.12 2008/02/06 14:26:31 scottl Exp $
  */
 
 /********************************************************************************
@@ -82,7 +82,7 @@
 #define AMR_LIMITCMD           120             /* maximum count of outstanding commands */
 #define AMR_MAXLD                      40
 
-#define AMR_MAX_CHANNELS       4
+#define AMR_MAX_CHANNELS       8
 #define AMR_MAX_TARGETS                15
 #define AMR_MAX_LUNS           7
 #define AMR_MAX_SCSI_CMDS      (15 * AMR_MAX_CHANNELS) /* one for every target? */
 #define AMR_CMD_GET_MACHINEID  0x36
 #define AMR_CMD_GET_INITIATOR  0x7d    /* returns one byte */
 #define AMR_CMD_CONFIG         0xa1
+#define AMR_CMD_LREAD64                0xa7
+#define AMR_CMD_LWRITE64       0xa8
+#define AMR_CMD_PASS_64                0xc3
+#define AMR_CMD_EXTPASS                0xe3
+
+#define AMR_CONFIG_READ_NVRAM_CONFIG   0x04
+#define AMR_CONFIG_WRITE_NVRAM_CONFIG  0x0d
 #define AMR_CONFIG_PRODUCT_INFO                0x0e
 #define AMR_CONFIG_ENQ3                        0x0f
 #define AMR_CONFIG_ENQ3_SOLICITED_NOTIFY       0x01
 #define AMR_CONFIG_ENQ3_SOLICITED_FULL         0x02
 #define AMR_CONFIG_ENQ3_UNSOLICITED            0x03
-#define AMR_CMD_EXTPASS                0xe3
+
+/*
+ * Command for random deletion of logical drives
+ */
+#define FC_DEL_LOGDRV          0xA4
+#define OP_SUP_DEL_LOGDRV      0x2A
+#define OP_GET_LDID_MAP                0x18
+#define OP_DEL_LOGDRV          0x1C
+
+/*
+ * Command for random deletion of logical drives
+ */
+#define FC_DEL_LOGDRV          0xA4
+#define OP_SUP_DEL_LOGDRV      0x2A
+#define OP_GET_LDID_MAP                0x18
+#define OP_DEL_LOGDRV          0x1C
 
 /*
  * Command results
@@ -377,8 +399,9 @@ struct amr_enquiry3
     u_int16_t  ae_opstatus[AMR_40LD_MAXDRIVES / 8];    /* operation status per drive */
     u_int32_t  ae_drivesize[AMR_40LD_MAXDRIVES];       /* logical drive size */
     u_int8_t   ae_driveprop[AMR_40LD_MAXDRIVES];       /* logical drive properties */
-    u_int8_t   ae_drivestate[AMR_40LD_MAXDRIVES];      /* physical drive state */
-    u_int16_t  ae_driveformat[AMR_40LD_MAXPHYSDRIVES];
+    u_int8_t   ae_drivestate[AMR_40LD_MAXDRIVES];      /* logical drive state */
+    u_int8_t   ae_pdrivestate[AMR_40LD_MAXPHYSDRIVES]; /* physical drive state */
+    u_int16_t  ae_pdriveformat[AMR_40LD_MAXPHYSDRIVES / 16];
     u_int8_t   ae_targxfer[80];                        /* physical drive transfer rates */
 
     u_int8_t   res1[263];              /* pad to 1024 bytes */
@@ -397,13 +420,14 @@ struct amr_mailbox
 {
     u_int8_t   mb_command;
     u_int8_t   mb_ident;
-    u_int16_t  mb_blkcount;
+    u_int16_t  mb_blkcount;            /* u_int8_t opcode */
+                                       /* u_int8_t subopcode */
     u_int32_t  mb_lba;
     u_int32_t  mb_physaddr;
     u_int8_t   mb_drive;
-    u_int8_t   mb_nsgelem;
-    u_int8_t   res1;
-    u_int8_t   mb_busy;
+    u_int8_t   mb_nsgelem;             /* u_int8_t rserv[0] */
+    u_int8_t   res1;                   /* u_int8_t rserv[1] */
+    u_int8_t   mb_busy;                /* u_int8_t rserv[2] */
     u_int8_t   mb_nstatus;
     u_int8_t   mb_status;
     u_int8_t   mb_completed[46];
@@ -414,7 +438,9 @@ struct amr_mailbox
 
 struct amr_mailbox64
 {
-    u_int32_t          mb64_segment;   /* for 64-bit controllers */
+    u_int8_t   pad[8];         /* Needed for alignment */
+    u_int32_t  sg64_lo;        /* S/G pointer for 64-bit commands */
+    u_int32_t  sg64_hi;        /* S/G pointer for 64-bit commands */
     struct amr_mailbox mb;
 } __packed;
 
@@ -443,6 +469,12 @@ struct amr_sgentry
     u_int32_t  sg_count;
 } __packed;
 
+struct amr_sg64entry
+{
+    u_int64_t  sg_addr;
+    u_int32_t  sg_count;
+} __packed;
+
 struct amr_passthrough
 {
     u_int8_t   ap_timeout:3;
@@ -489,6 +521,26 @@ struct amr_ext_passthrough
     u_int32_t  ap_data_transfer_length;
 } __packed;
 
+struct amr_linux_ioctl {
+    u_int32_t  inlen;
+    u_int32_t  outlen;
+    union {
+       u_int8_t        fca[16];
+       struct {
+           u_int8_t    opcode;
+           u_int8_t    subopcode;
+           u_int16_t   adapno;
+           u_int32_t   buffer;
+           u_int8_t    pad[4];
+           u_int32_t   length;
+       } __packed fcs;
+    } __packed ui;
+    u_int8_t   mbox[18];
+    struct amr_passthrough     pthru;
+    u_int32_t  data;
+    u_int8_t   pad[4];
+} __packed;
+
 #ifdef _KERNEL
 /********************************************************************************
  ********************************************************************************
index 1b35e75..534914b 100644 (file)
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *      $FreeBSD: src/sys/dev/amr/amrvar.h,v 1.2.2.5 2002/12/20 15:12:04 emoore Exp $
- *      $DragonFly: src/sys/dev/raid/amr/amrvar.h,v 1.11 2008/06/10 17:20:49 dillon Exp $
+ *
+ *      $FreeBSD: src/sys/dev/amr/amrvar.h,v 1.37 2010/07/28 16:24:11 mdf Exp $
  */
 
-#include <sys/thread2.h>
+#include <sys/buf2.h>
+#include <sys/devicestat.h>
+#include <sys/disk.h>
+#include <sys/lock.h>
+#include <sys/sysctl.h>
 
-#if defined(__FreeBSD__) && __FreeBSD_version >= 500005
-# include <sys/taskqueue.h>
-#endif
+#define LSI_DESC_PCI "LSILogic MegaRAID 1.53"
 
 #ifdef AMR_DEBUG
 # define debug(level, fmt, args...)    do {if (level <= AMR_DEBUG) kprintf("%s: " fmt "\n", __func__ , ##args);} while(0)
 # define debug_called(level)           do {if (level <= AMR_DEBUG) kprintf("%s: called\n", __func__);} while(0)
 #else
-# define debug(level, fmt, args...)
-# define debug_called(level)
+# define debug(level, fmt, args...)    do {} while (0)
+# define debug_called(level)           do {} while (0)
 #endif
-#define xdebug(fmt, args...)   kprintf("%s: " fmt "\n", __func__ , ##args)
+#define xdebug(fmt, args...)   printf("%s: " fmt "\n", __func__ , ##args)
 
 /*
  * Per-logical-drive datastructure
@@ -100,41 +102,61 @@ struct amr_logdrive
 
 #define AMR_CMD_CLUSTERSIZE    (16 * 1024)
 
+typedef STAILQ_HEAD(, amr_command)     ac_qhead_t;
+typedef STAILQ_ENTRY(amr_command)      ac_link_t;
+
+union amr_ccb {
+    struct amr_passthrough     ccb_pthru;
+    struct amr_ext_passthrough ccb_epthru;
+    uint8_t                    bytes[128];
+};
+
 /*
  * Per-command control structure.
  */
 struct amr_command
 {
-    TAILQ_ENTRY(amr_command)   ac_link;
+    ac_link_t                  ac_link;
 
     struct amr_softc           *ac_sc;
     u_int8_t                   ac_slot;
     int                                ac_status;      /* command completion status */
+    union {
+       struct amr_sgentry      *sg32;
+       struct amr_sg64entry    *sg64;
+    } ac_sg;
+    u_int32_t                  ac_sgbusaddr;
+    u_int32_t                  ac_sg64_lo;
+    u_int32_t                  ac_sg64_hi;
     struct amr_mailbox         ac_mailbox;
     int                                ac_flags;
 #define AMR_CMD_DATAIN         (1<<0)
 #define AMR_CMD_DATAOUT                (1<<1)
-#define AMR_CMD_CCB_DATAIN     (1<<2)
-#define AMR_CMD_CCB_DATAOUT    (1<<3)
+#define AMR_CMD_CCB            (1<<2)
 #define AMR_CMD_PRIORITY       (1<<4)
 #define AMR_CMD_MAPPED         (1<<5)
 #define AMR_CMD_SLEEP          (1<<6)
 #define AMR_CMD_BUSY           (1<<7)
+#define AMR_CMD_SG64           (1<<8)
+#define AC_IS_SG64(ac)         ((ac)->ac_flags & AMR_CMD_SG64)
+    u_int                      ac_retries;
 
     struct bio                 *ac_bio;
+    void                       (* ac_complete)(struct amr_command *ac);
+    void                       *ac_private;
 
     void                       *ac_data;
     size_t                     ac_length;
     bus_dmamap_t               ac_dmamap;
-    u_int32_t                  ac_dataphys;
+    bus_dmamap_t               ac_dma64map;
 
-    void                       *ac_ccb_data;
-    size_t                     ac_ccb_length;
-    bus_dmamap_t               ac_ccb_dmamap;
-    u_int32_t                  ac_ccb_dataphys;
+    bus_dma_tag_t              ac_tag;
+    bus_dmamap_t               ac_datamap;
+    int                                ac_nsegments;
+    uint32_t                   ac_mb_physaddr;
 
-    void                       (* ac_complete)(struct amr_command *ac);
-    void                       *ac_private;
+    union amr_ccb              *ac_ccb;
+    uint32_t                   ac_ccb_busaddr;
 };
 
 struct amr_command_cluster
@@ -158,6 +180,7 @@ struct amr_softc
     bus_space_tag_t            amr_btag;
     bus_dma_tag_t              amr_parent_dmat;        /* parent DMA tag */
     bus_dma_tag_t              amr_buffer_dmat;        /* data buffer DMA tag */
+    bus_dma_tag_t              amr_buffer64_dmat;
     struct resource            *amr_irq;               /* interrupt */
     void                       *amr_intr;
 
@@ -170,11 +193,18 @@ struct amr_softc
 
     /* scatter/gather lists and their controller-visible mappings */
     struct amr_sgentry         *amr_sgtable;           /* s/g lists */
+    struct amr_sg64entry       *amr_sg64table;         /* 64bit s/g lists */
     u_int32_t                  amr_sgbusaddr;          /* s/g table base address in bus space */
     bus_dma_tag_t              amr_sg_dmat;            /* s/g buffer DMA tag */
     bus_dmamap_t               amr_sg_dmamap;          /* map for s/g buffers */
 
+    union amr_ccb              *amr_ccb;
+    uint32_t                   amr_ccb_busaddr;
+    bus_dma_tag_t              amr_ccb_dmat;
+    bus_dmamap_t               amr_ccb_dmamap;
+
     /* controller limits and features */
+    int                                amr_nextslot;           /* Next slot to use for newly allocated commands */
     int                                amr_maxio;              /* maximum number of I/O transactions */
     int                                amr_maxdrives;          /* max number of logical drives */
     int                                amr_maxchan;            /* count of SCSI channels */
@@ -188,22 +218,30 @@ struct amr_softc
 #define AMR_STATE_SUSPEND      (1<<1)
 #define AMR_STATE_INTEN                (1<<2)
 #define AMR_STATE_SHUTDOWN     (1<<3)
+#define AMR_STATE_CRASHDUMP    (1<<4)
+#define AMR_STATE_QUEUE_FRZN   (1<<5)
+#define AMR_STATE_LD_DELETE    (1<<6)
+#define AMR_STATE_REMAP_LD     (1<<7)
 
     /* per-controller queues */
     struct bio_queue_head      amr_bioq;               /* pending I/O with no commands */
-    TAILQ_HEAD(,amr_command)   amr_ready;              /* commands ready to be submitted */
+    ac_qhead_t                 amr_ready;              /* commands ready to be submitted */
     struct amr_command         *amr_busycmd[AMR_MAXCMD];
     int                                amr_busyslots;
-    TAILQ_HEAD(,amr_command)   amr_completed;
-    TAILQ_HEAD(,amr_command)   amr_freecmds;
+    ac_qhead_t                 amr_freecmds;
     TAILQ_HEAD(,amr_command_cluster)   amr_cmd_clusters;
 
     /* CAM attachments for passthrough */
     struct cam_sim             *amr_cam_sim[AMR_MAX_CHANNELS];
     TAILQ_HEAD(, ccb_hdr)      amr_cam_ccbq;
+    struct cam_devq            *amr_cam_devq;
 
     /* control device */
-    cdev_t                     amr_dev_t;
+    struct cdev                        *amr_dev_t;
+    struct lock                        amr_list_lock;
+
+    struct sysctl_ctx_list     amr_sysctl_ctx;
+    struct sysctl_oid          *amr_sysctl_tree;
 
     /* controller type-specific support */
     int                                amr_type;
@@ -211,17 +249,23 @@ struct amr_softc
 #define AMR_IS_QUARTZ(sc)      ((sc)->amr_type & AMR_TYPE_QUARTZ)
 #define AMR_TYPE_40LD          (1<<1)
 #define AMR_IS_40LD(sc)                ((sc)->amr_type & AMR_TYPE_40LD)
-    int                                (* amr_submit_command)(struct amr_softc *sc);
+#define AMR_TYPE_SG64          (1<<2)
+#define AMR_IS_SG64(sc)                ((sc)->amr_type & AMR_TYPE_SG64)
+    int                                (* amr_submit_command)(struct amr_command *ac);
     int                                (* amr_get_work)(struct amr_softc *sc, struct amr_mailbox *mbsave);
     int                                (*amr_poll_command)(struct amr_command *ac);
+    int                                (*amr_poll_command1)(struct amr_softc *sc, struct amr_command *ac);
     int                        support_ext_cdb;        /* greater than 10 byte cdb support */
 
     /* misc glue */
+    device_t                   amr_pass;
+    int                                (*amr_cam_command)(struct amr_softc *sc, struct amr_command **acp);
     struct intr_config_hook    amr_ich;                /* wait-for-interrupts probe hook */
     struct callout             amr_timeout;            /* periodic status check */
-#if defined(__FreeBSD__) && __FreeBSD_version >= 500005
-    struct task                        amr_task_complete;      /* deferred-completion task */
-#endif
+    int                                amr_allow_vol_config;
+    int                                amr_linux_no_adapters;
+    int                                amr_ld_del_supported;
+    struct lock                        amr_hw_lock;
 };
 
 /*
@@ -240,13 +284,6 @@ extern struct amr_command  *amr_alloccmd(struct amr_softc *sc);
 extern void                    amr_releasecmd(struct amr_command *ac);
 
 /*
- * CAM interface
- */
-extern int             amr_cam_attach(struct amr_softc *sc);
-extern void            amr_cam_detach(struct amr_softc *sc);
-extern int             amr_cam_command(struct amr_softc *sc, struct amr_command **acp);
-
-/*
  * MegaRAID logical disk driver
  */
 struct amrd_softc 
@@ -258,16 +295,14 @@ struct amrd_softc
     struct disk                amrd_disk;
     struct devstat     amrd_stats;
     int                        amrd_unit;
-    int                        amrd_flags;
-#define AMRD_OPEN      (1<<0)          /* drive is open (can't detach) */
 };
 
 /*
  * Interface between driver core and disk driver (should be using a bus?)
  */
 extern int     amr_submit_bio(struct amr_softc *sc, struct bio *bio);
-extern void    amrd_intr(struct bio *bio);
-extern int     amr_dump_blocks(struct amr_softc *sc, int unit, u_int64_t lba, void *data, int blks);
+extern int     amr_dump_blocks(struct amr_softc *sc, int unit, u_int32_t lba, void *data, int blks);
+extern void    amrd_intr(void *data);
 
 /********************************************************************************
  * Enqueue/dequeue functions
@@ -275,9 +310,8 @@ extern int  amr_dump_blocks(struct amr_softc *sc, int unit, u_int64_t lba, void *
 static __inline void
 amr_enqueue_bio(struct amr_softc *sc, struct bio *bio)
 {
-    crit_enter();
-    bioqdisksort(&sc->amr_bioq, bio);
-    crit_exit();
+
+    bioq_insert_tail(&sc->amr_bioq, bio);
 }
 
 static __inline struct bio *
@@ -285,27 +319,30 @@ amr_dequeue_bio(struct amr_softc *sc)
 {
     struct bio *bio;
 
-    crit_enter();
     if ((bio = bioq_first(&sc->amr_bioq)) != NULL)
        bioq_remove(&sc->amr_bioq, bio);
-    crit_exit();
     return(bio);
 }
 
 static __inline void
+amr_init_qhead(ac_qhead_t *head)
+{
+
+       STAILQ_INIT(head);
+}
+
+static __inline void
 amr_enqueue_ready(struct amr_command *ac)
 {
-    crit_enter();
-    TAILQ_INSERT_TAIL(&ac->ac_sc->amr_ready, ac, ac_link);
-    crit_exit();
+
+    STAILQ_INSERT_TAIL(&ac->ac_sc->amr_ready, ac, ac_link);
 }
 
 static __inline void
 amr_requeue_ready(struct amr_command *ac)
 {
-    crit_enter();
-    TAILQ_INSERT_HEAD(&ac->ac_sc->amr_ready, ac, ac_link);
-    crit_exit();
+
+    STAILQ_INSERT_HEAD(&ac->ac_sc->amr_ready, ac, ac_link);
 }
 
 static __inline struct amr_command *
@@ -313,39 +350,33 @@ amr_dequeue_ready(struct amr_softc *sc)
 {
     struct amr_command *ac;
 
-    crit_enter();
-    if ((ac = TAILQ_FIRST(&sc->amr_ready)) != NULL)
-       TAILQ_REMOVE(&sc->amr_ready, ac, ac_link);
-    crit_exit();
+    if ((ac = STAILQ_FIRST(&sc->amr_ready)) != NULL)
+       STAILQ_REMOVE_HEAD(&sc->amr_ready, ac_link);
     return(ac);
 }
 
 static __inline void
-amr_enqueue_completed(struct amr_command *ac)
+amr_enqueue_completed(struct amr_command *ac, ac_qhead_t *head)
 {
-    crit_enter();
-    TAILQ_INSERT_TAIL(&ac->ac_sc->amr_completed, ac, ac_link);
-    crit_exit();
+
+    STAILQ_INSERT_TAIL(head, ac, ac_link);
 }
 
 static __inline struct amr_command *
-amr_dequeue_completed(struct amr_softc *sc)
+amr_dequeue_completed(struct amr_softc *sc, ac_qhead_t *head)
 {
     struct amr_command *ac;
 
-    crit_enter();
-    if ((ac = TAILQ_FIRST(&sc->amr_completed)) != NULL)
-       TAILQ_REMOVE(&sc->amr_completed, ac, ac_link);
-    crit_exit();
+    if ((ac = STAILQ_FIRST(head)) != NULL)
+       STAILQ_REMOVE_HEAD(head, ac_link);
     return(ac);
 }
 
 static __inline void
 amr_enqueue_free(struct amr_command *ac)
 {
-    crit_enter();
-    TAILQ_INSERT_TAIL(&ac->ac_sc->amr_freecmds, ac, ac_link);
-    crit_exit();
+
+    STAILQ_INSERT_HEAD(&ac->ac_sc->amr_freecmds, ac, ac_link);
 }
 
 static __inline struct amr_command *
@@ -353,9 +384,7 @@ amr_dequeue_free(struct amr_softc *sc)
 {
     struct amr_command *ac;
 
-    crit_enter();
-    if ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL)
-       TAILQ_REMOVE(&sc->amr_freecmds, ac, ac_link);
-    crit_exit();
+    if ((ac = STAILQ_FIRST(&sc->amr_freecmds)) != NULL)
+       STAILQ_REMOVE_HEAD(&sc->amr_freecmds, ac_link);
     return(ac);
 }