root/sys/dev/aacraid/aacraid_debug.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2006-2010 Adaptec, Inc.
 * Copyright (c) 2010-2012 PMC-Sierra, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/cdefs.h>
/*
 * Debugging support.
 */
#include "opt_aacraid.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>

#include <sys/bus.h>

#include <machine/resource.h>
#include <machine/bus.h>

#include <dev/aacraid/aacraid_reg.h>
#include <sys/aac_ioctl.h>
#include <dev/aacraid/aacraid_var.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/stdarg.h>

#include <machine/resource.h>
#include <machine/bus.h>

#include <dev/aacraid/aacraid_debug.h>

#ifdef AACRAID_DEBUG
/*
 * Dump the command queue indices
 */
void
aacraid_print_queues(struct aac_softc *sc)
{
        device_printf(sc->aac_dev, "AACQ_FREE      %d/%d\n",
            sc->aac_qstat[AACQ_FREE].q_length, sc->aac_qstat[AACQ_FREE].q_max);
        device_printf(sc->aac_dev, "AACQ_READY     %d/%d\n",
            sc->aac_qstat[AACQ_READY].q_length,
            sc->aac_qstat[AACQ_READY].q_max);
        device_printf(sc->aac_dev, "AACQ_BUSY      %d/%d\n",
            sc->aac_qstat[AACQ_BUSY].q_length, sc->aac_qstat[AACQ_BUSY].q_max);
}

/*
 * Print a FIB
 */
void
aacraid_print_fib(struct aac_softc *sc, struct aac_fib *fib, const char *caller)
{
        if (fib == NULL) {
                device_printf(sc->aac_dev,
                              "aac_print_fib called with NULL fib\n");
                return;
        }
        device_printf(sc->aac_dev, "%s: FIB @ %p\n", caller, fib);
        device_printf(sc->aac_dev, "  XferState %b\n", fib->Header.XferState,
                      "\20"
                      "\1HOSTOWNED"
                      "\2ADAPTEROWNED"
                      "\3INITIALISED"
                      "\4EMPTY"
                      "\5FROMPOOL"
                      "\6FROMHOST"
                      "\7FROMADAP"
                      "\10REXPECTED"
                      "\11RNOTEXPECTED"
                      "\12DONEADAP"
                      "\13DONEHOST"
                      "\14HIGH"
                      "\15NORM"
                      "\16ASYNC"
                      "\17PAGEFILEIO"
                      "\20SHUTDOWN"
                      "\21LAZYWRITE"
                      "\22ADAPMICROFIB"
                      "\23BIOSFIB"
                      "\24FAST_RESPONSE"
                      "\25APIFIB\n");
        device_printf(sc->aac_dev, "  Command       %d\n", fib->Header.Command);
        device_printf(sc->aac_dev, "  StructType    %d\n",
                      fib->Header.StructType);
        device_printf(sc->aac_dev, "  Size          %d\n", fib->Header.Size);
        device_printf(sc->aac_dev, "  SenderSize    %d\n",
                      fib->Header.SenderSize);
        device_printf(sc->aac_dev, "  SenderAddress 0x%x\n",
                      fib->Header.SenderFibAddress);
        device_printf(sc->aac_dev, "  RcvrAddress   0x%x\n",
                      fib->Header.u.ReceiverFibAddress);
        device_printf(sc->aac_dev, "  Handle    0x%x\n",
                      fib->Header.Handle);
        switch(fib->Header.Command) {
        case ContainerCommand:
        {
                struct aac_blockread *br;
                struct aac_blockwrite *bw;
                struct aac_sg_table *sg;
                int i;

                br = (struct aac_blockread*)fib->data;
                bw = (struct aac_blockwrite*)fib->data;
                sg = NULL;

                if (br->Command == VM_CtBlockRead) {
                        device_printf(sc->aac_dev,
                                      "  BlockRead: container %d  0x%x/%d\n",
                                      br->ContainerId, br->BlockNumber,
                                      br->ByteCount);
                        sg = &br->SgMap;
                }
                if (bw->Command == VM_CtBlockWrite) {
                        device_printf(sc->aac_dev,
                                      "  BlockWrite: container %d  0x%x/%d "
                                      "(%s)\n", bw->ContainerId,
                                      bw->BlockNumber, bw->ByteCount,
                                      bw->Stable == CSTABLE ? "stable" :
                                      "unstable");
                        sg = &bw->SgMap;
                }
                if (sg != NULL) {
                        device_printf(sc->aac_dev,
                                      "  %d s/g entries\n", sg->SgCount);
                        for (i = 0; i < sg->SgCount; i++)
                                device_printf(sc->aac_dev, "  0x%08x/%d\n",
                                              sg->SgEntry[i].SgAddress,
                                              sg->SgEntry[i].SgByteCount);
                }
                break;
        }
        default:
                device_printf(sc->aac_dev, "   %16D\n", fib->data, " ");
                device_printf(sc->aac_dev, "   %16D\n", fib->data + 16, " ");
                break;
        }
}

/*
 * Describe an AIF we have received.
 */
void
aacraid_print_aif(struct aac_softc *sc, struct aac_aif_command *aif)
{
        switch(aif->command) {
        case AifCmdEventNotify:
                device_printf(sc->aac_dev, "EventNotify(%d)\n", aif->seqNumber);
                switch(aif->data.EN.type) {
                case AifEnGeneric:              /* Generic notification */
                        device_printf(sc->aac_dev, "(Generic) %.*s\n",
                                  (int)sizeof(aif->data.EN.data.EG),
                                  aif->data.EN.data.EG.text);
                        break;
                case AifEnTaskComplete:         /* Task has completed */
                        device_printf(sc->aac_dev, "(TaskComplete)\n");
                        break;
                case AifEnConfigChange:         /* Adapter configuration change
                                                 * occurred */
                        device_printf(sc->aac_dev, "(ConfigChange)\n");
                        break;
                case AifEnContainerChange:      /* Adapter specific container
                                                 * configuration change */
                        device_printf(sc->aac_dev, "(ContainerChange) "
                                      "container %d,%d\n",
                                      aif->data.EN.data.ECC.container[0],
                                      aif->data.EN.data.ECC.container[1]);
                        break;
                case AifEnDeviceFailure:        /* SCSI device failed */
                        device_printf(sc->aac_dev, "(DeviceFailure) "
                                      "handle %d\n",
                                      aif->data.EN.data.EDF.deviceHandle);
                        break;
                case AifEnMirrorFailover:       /* Mirror failover started */
                        device_printf(sc->aac_dev, "(MirrorFailover) "
                                      "container %d failed, "
                                      "migrating from slice %d to %d\n",
                                      aif->data.EN.data.EMF.container,
                                      aif->data.EN.data.EMF.failedSlice,
                                      aif->data.EN.data.EMF.creatingSlice);
                        break;
                case AifEnContainerEvent:       /* Significant container
                                                 * event */
                        device_printf(sc->aac_dev, "(ContainerEvent) "
                                      "container %d event "
                                      "%d\n", aif->data.EN.data.ECE.container,
                                      aif->data.EN.data.ECE.eventType); 
                        break;
                case AifEnFileSystemChange:     /* File system changed */
                        device_printf(sc->aac_dev, "(FileSystemChange)\n");
                        break;
                case AifEnConfigPause:          /* Container pause event */
                        device_printf(sc->aac_dev, "(ConfigPause)\n");
                        break;
                case AifEnConfigResume:         /* Container resume event */
                        device_printf(sc->aac_dev, "(ConfigResume)\n");
                        break;
                case AifEnFailoverChange:       /* Failover space assignment
                                                 * changed */
                        device_printf(sc->aac_dev, "(FailoverChange)\n");
                        break;
                case AifEnRAID5RebuildDone:     /* RAID5 rebuild finished */
                        device_printf(sc->aac_dev, "(RAID5RebuildDone)\n");
                        break;
                case AifEnEnclosureManagement:  /* Enclosure management event */
                        device_printf(sc->aac_dev, "(EnclosureManagement) "
                                      "EMPID %d unit %d "
                                      "event %d\n", aif->data.EN.data.EEE.empID,
                                      aif->data.EN.data.EEE.unitID,
                                      aif->data.EN.data.EEE.eventType);
                        break;
                case AifEnBatteryEvent:         /* Significant NV battery
                                                 * event */
                        device_printf(sc->aac_dev, "(BatteryEvent) %d "
                                      "(state was %d, is %d\n",
                                      aif->data.EN.data.EBE.transition_type,
                                      aif->data.EN.data.EBE.current_state,
                                      aif->data.EN.data.EBE.prior_state);
                        break;
                case AifEnAddContainer:         /* A new container was
                                                 * created. */
                        device_printf(sc->aac_dev, "(AddContainer)\n");
                        break;          
                case AifEnDeleteContainer:      /* A container was deleted. */
                        device_printf(sc->aac_dev, "(DeleteContainer)\n");
                        break;
                case AifEnBatteryNeedsRecond:   /* The battery needs
                                                 * reconditioning */
                        device_printf(sc->aac_dev, "(BatteryNeedsRecond)\n");
                        break;
                case AifEnClusterEvent:         /* Some cluster event */
                        device_printf(sc->aac_dev, "(ClusterEvent) event %d\n",
                                      aif->data.EN.data.ECLE.eventType);
                        break;
                case AifEnDiskSetEvent:         /* A disk set event occurred. */
                        device_printf(sc->aac_dev, "(DiskSetEvent) event %d "
                                      "diskset %jd creator %jd\n",
                                      aif->data.EN.data.EDS.eventType,
                                      (intmax_t)aif->data.EN.data.EDS.DsNum,
                                      (intmax_t)aif->data.EN.data.EDS.CreatorId);
                        break;
                case AifDenMorphComplete:       /* A morph operation
                                                 * completed */
                        device_printf(sc->aac_dev, "(MorphComplete)\n");
                        break;
                case AifDenVolumeExtendComplete: /* A volume expand operation
                                                  * completed */
                        device_printf(sc->aac_dev, "(VolumeExtendComplete)\n");
                        break;
                default:
                        device_printf(sc->aac_dev, "(%d)\n", aif->data.EN.type);
                        break;
                }
                break;
        case AifCmdJobProgress:
        {
                char    *status;
                switch(aif->data.PR[0].status) {
                case AifJobStsSuccess:
                        status = "success"; break;
                case AifJobStsFinished:
                        status = "finished"; break;
                case AifJobStsAborted:
                        status = "aborted"; break;
                case AifJobStsFailed:
                        status = "failed"; break;
                case AifJobStsSuspended:
                        status = "suspended"; break;
                case AifJobStsRunning:
                        status = "running"; break;
                default:
                        status = "unknown status"; break;
                }               

                device_printf(sc->aac_dev, "JobProgress (%d) - %s (%d, %d)\n",
                              aif->seqNumber, status,
                              aif->data.PR[0].currentTick,
                              aif->data.PR[0].finalTick);
                switch(aif->data.PR[0].jd.type) {
                case AifJobScsiZero:            /* SCSI dev clear operation */
                        device_printf(sc->aac_dev, "(ScsiZero) handle %d\n",
                                      aif->data.PR[0].jd.client.scsi_dh);
                        break;
                case AifJobScsiVerify:          /* SCSI device Verify operation
                                                 * NO REPAIR */
                        device_printf(sc->aac_dev, "(ScsiVerify) handle %d\n",
                                      aif->data.PR[0].jd.client.scsi_dh);
                        break;
                case AifJobScsiExercise:        /* SCSI device Exercise
                                                 * operation */
                        device_printf(sc->aac_dev, "(ScsiExercise) handle %d\n",
                                      aif->data.PR[0].jd.client.scsi_dh);
                        break;
                case AifJobScsiVerifyRepair:    /* SCSI device Verify operation
                                                 * WITH repair */
                        device_printf(sc->aac_dev,
                                      "(ScsiVerifyRepair) handle %d\n",
                                      aif->data.PR[0].jd.client.scsi_dh);
                        break;
                case AifJobCtrZero:             /* Container clear operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerZero) container %d\n",
                                      aif->data.PR[0].jd.client.container.src);
                        break;
                case AifJobCtrCopy:             /* Container copy operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerCopy) container %d to %d\n",
                                      aif->data.PR[0].jd.client.container.src,
                                      aif->data.PR[0].jd.client.container.dst);
                        break;
                case AifJobCtrCreateMirror:     /* Container Create Mirror
                                                 * operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerCreateMirror) container %d\n",
                                      aif->data.PR[0].jd.client.container.src);
                                      /* XXX two containers? */
                        break;
                case AifJobCtrMergeMirror:      /* Container Merge Mirror
                                                 * operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerMergeMirror) container %d\n",
                                      aif->data.PR[0].jd.client.container.src);
                                      /* XXX two containers? */
                        break;
                case AifJobCtrScrubMirror:      /* Container Scrub Mirror
                                                 * operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerScrubMirror) container %d\n",
                                      aif->data.PR[0].jd.client.container.src);
                        break;
                case AifJobCtrRebuildRaid5:     /* Container Rebuild Raid5
                                                 * operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerRebuildRaid5) container %d\n",
                                      aif->data.PR[0].jd.client.container.src);
                        break;
                case AifJobCtrScrubRaid5:       /* Container Scrub Raid5
                                                 * operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerScrubRaid5) container %d\n",
                                      aif->data.PR[0].jd.client.container.src);
                        break;
                case AifJobCtrMorph:            /* Container morph operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerMorph) container %d\n",
                                      aif->data.PR[0].jd.client.container.src);
                                      /* XXX two containers? */
                        break;
                case AifJobCtrPartCopy:         /* Container Partition copy
                                                 * operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerPartCopy) container %d to "
                                      "%d\n",
                                      aif->data.PR[0].jd.client.container.src,
                                      aif->data.PR[0].jd.client.container.dst);
                        break;
                case AifJobCtrRebuildMirror:    /* Container Rebuild Mirror
                                                 * operation */
                        device_printf(sc->aac_dev,
                                      "(ContainerRebuildMirror) container "
                                      "%d\n",
                                      aif->data.PR[0].jd.client.container.src);
                        break;
                case AifJobCtrCrazyCache:       /* crazy cache */
                        device_printf(sc->aac_dev,
                                      "(ContainerCrazyCache) container %d\n",
                                      aif->data.PR[0].jd.client.container.src);
                                      /* XXX two containers? */
                        break;
                case AifJobFsCreate:            /* File System Create
                                                 * operation */
                        device_printf(sc->aac_dev, "(FsCreate)\n");
                        break;
                case AifJobFsVerify:            /* File System Verify
                                                 * operation */
                        device_printf(sc->aac_dev, "(FsVerivy)\n");
                        break;
                case AifJobFsExtend:            /* File System Extend
                                                 * operation */
                        device_printf(sc->aac_dev, "(FsExtend)\n");
                        break;
                case AifJobApiFormatNTFS:       /* Format a drive to NTFS */
                        device_printf(sc->aac_dev, "(FormatNTFS)\n");
                        break;
                case AifJobApiFormatFAT:        /* Format a drive to FAT */
                        device_printf(sc->aac_dev, "(FormatFAT)\n");
                        break;
                case AifJobApiUpdateSnapshot:   /* update the read/write half
                                                 * of a snapshot */
                        device_printf(sc->aac_dev, "(UpdateSnapshot)\n");
                        break;
                case AifJobApiFormatFAT32:      /* Format a drive to FAT32 */
                        device_printf(sc->aac_dev, "(FormatFAT32)\n");
                        break;
                case AifJobCtlContinuousCtrVerify: /* Adapter operation */
                        device_printf(sc->aac_dev, "(ContinuousCtrVerify)\n");
                        break;
                default:
                        device_printf(sc->aac_dev, "(%d)\n",
                                      aif->data.PR[0].jd.type);
                        break;
                }
                break;
        }
        case AifCmdAPIReport:
                device_printf(sc->aac_dev, "APIReport (%d)\n", aif->seqNumber);
                break;
        case AifCmdDriverNotify:
                device_printf(sc->aac_dev, "DriverNotify (%d)\n",
                              aif->seqNumber);
                break;
        default:
                device_printf(sc->aac_dev, "AIF %d (%d)\n", aif->command,
                              aif->seqNumber);
                break;
        }
}
#endif /* AACRAID_DEBUG */

/*
 * Debug flags to be put into the HBA flags field when initialized
 */
const unsigned long aacraid_debug_flags = /* Variable to setup with above flags. */
/*                      HBA_FLAGS_DBG_KERNEL_PRINT_B |          */
                        HBA_FLAGS_DBG_FW_PRINT_B |
/*                      HBA_FLAGS_DBG_FUNCTION_ENTRY_B |        */
                        HBA_FLAGS_DBG_FUNCTION_EXIT_B |
                        HBA_FLAGS_DBG_ERROR_B |
                        HBA_FLAGS_DBG_INIT_B |                  
/*                      HBA_FLAGS_DBG_OS_COMMANDS_B |           */
/*                      HBA_FLAGS_DBG_SCAN_B |                  */
/*                      HBA_FLAGS_DBG_COALESCE_B |              */
/*                      HBA_FLAGS_DBG_IOCTL_COMMANDS_B |        */
/*                      HBA_FLAGS_DBG_SYNC_COMMANDS_B |         */
                        HBA_FLAGS_DBG_COMM_B |                  
/*                      HBA_FLAGS_DBG_AIF_B |                   */
/*                      HBA_FLAGS_DBG_CSMI_COMMANDS_B |         */
                        HBA_FLAGS_DBG_DEBUG_B |                 
/*                      HBA_FLAGS_DBG_FLAGS_MASK |              */
0;

int aacraid_get_fw_debug_buffer(struct aac_softc *sc)
{
        u_int32_t MonDriverBufferPhysAddrLow = 0;
        u_int32_t MonDriverBufferPhysAddrHigh = 0;
        u_int32_t MonDriverBufferSize = 0;
        u_int32_t MonDriverHeaderSize = 0;

        /*
         * Get the firmware print buffer parameters from the firmware
         * If the command was successful map in the address.
         */
        if (!aacraid_sync_command(sc, AAC_MONKER_GETDRVPROP, 0, 0, 0, 0, NULL, NULL)) {
                MonDriverBufferPhysAddrLow = AAC_GET_MAILBOX(sc, 1);
                MonDriverBufferPhysAddrHigh = AAC_GET_MAILBOX(sc, 2);
                MonDriverBufferSize = AAC_GET_MAILBOX(sc, 3);
                MonDriverHeaderSize = AAC_GET_MAILBOX(sc, 4); 
                if (MonDriverBufferSize) {
                        unsigned long Offset = MonDriverBufferPhysAddrLow
                                - rman_get_start(sc->aac_regs_res1);

                        /*
                         * See if the address is already mapped in and if so set it up
                         * from the base address
                         */
                        if ((MonDriverBufferPhysAddrHigh == 0) && 
                                (Offset + MonDriverBufferSize < 
                                rman_get_size(sc->aac_regs_res1))) {
                                sc->DebugOffset = Offset;
                                sc->DebugHeaderSize = MonDriverHeaderSize;
                                sc->FwDebugBufferSize = MonDriverBufferSize;
                                sc->FwDebugFlags = 0;
                                sc->DebugFlags = aacraid_debug_flags;
                                return 1;
                        }
                }
        }

        /*
         * The GET_DRIVER_BUFFER_PROPERTIES command failed
         */
        return 0;
}

#define PRINT_TIMEOUT 250000 /* 1/4 second */

void aacraid_fw_printf(struct aac_softc *sc, unsigned long PrintFlags, const char * fmt, ...)
{
        va_list args;
        u_int32_t Count, i;
        char PrintBuffer_P[PRINT_BUFFER_SIZE];
        unsigned long PrintType;

        PrintType = PrintFlags & 
                ~(HBA_FLAGS_DBG_KERNEL_PRINT_B|HBA_FLAGS_DBG_FW_PRINT_B);
        if (((PrintType!=0) && (sc!=NULL) && ((sc->DebugFlags & PrintType)==0))
                || ((sc!=NULL) && (sc->DebugFlags
                & (HBA_FLAGS_DBG_KERNEL_PRINT_B|HBA_FLAGS_DBG_FW_PRINT_B)) == 0))
                return;

        /*
         * Set up parameters and call sprintf function to format the data
         */
        va_start(args, fmt);
        vsprintf(PrintBuffer_P, fmt, args);
        va_end(args);

        /*
         * Make sure the HBA structure has been passed in for this section
         */
        if ((sc != NULL) && (sc->FwDebugBufferSize)) {
                /*
                 * If we are set up for a Firmware print
                 */
                if ((sc->DebugFlags & HBA_FLAGS_DBG_FW_PRINT_B)
                        && ((PrintFlags
                        & (HBA_FLAGS_DBG_KERNEL_PRINT_B|HBA_FLAGS_DBG_FW_PRINT_B))
                        != HBA_FLAGS_DBG_KERNEL_PRINT_B)) {
                        /*
                         * Make sure the string size is within boundaries
                         */
                        Count = strlen(PrintBuffer_P);
                        if (Count > sc->FwDebugBufferSize)
                                Count = (u_int16_t)sc->FwDebugBufferSize;

                        /*
                         * Wait for no more than PRINT_TIMEOUT for the previous
                         * message length to clear (the handshake).
                         */
                        for (i = 0; i < PRINT_TIMEOUT; ++i) {
                                if (!AAC_MEM1_GETREG4(sc,
                                        sc->DebugOffset + FW_DEBUG_STR_LENGTH_OFFSET)) {
                                        break;
                                }
                                DELAY(1);
            }

                        /*
                         * If the Length is clear, copy over the message, the
                         * flags, and the length. Make sure the length is the
                         * last because that is the signal for the Firmware to
                         * pick it up.
                         */
                        if (!AAC_MEM1_GETREG4(sc,
                                sc->DebugOffset + FW_DEBUG_STR_LENGTH_OFFSET)) {
                                for (i = 0; i < Count; ++i) {
                                        AAC_MEM1_SETREG1(sc, sc->DebugOffset + sc->DebugHeaderSize + i,
                                                                PrintBuffer_P[i]);
                                }
                                AAC_MEM1_SETREG4(sc, sc->DebugOffset + FW_DEBUG_FLAGS_OFFSET,
                                                        sc->FwDebugFlags);
                                AAC_MEM1_SETREG4(sc, sc->DebugOffset + FW_DEBUG_STR_LENGTH_OFFSET,
                            Count);
                        } else
                                sc->DebugFlags &= ~HBA_FLAGS_DBG_FW_PRINT_B;
                }

                /*
                 * If the Kernel Debug Print flag is set, send it off to the
                 * Kernel debugger
                 */
                if ((sc->DebugFlags & HBA_FLAGS_DBG_KERNEL_PRINT_B)
                        && ((PrintFlags
                        & (HBA_FLAGS_DBG_KERNEL_PRINT_B|HBA_FLAGS_DBG_FW_PRINT_B))
                        != HBA_FLAGS_DBG_FW_PRINT_B)) {
                        if (sc->FwDebugFlags & FW_DEBUG_FLAGS_NO_HEADERS_B)
                                printf ("%s\n", PrintBuffer_P);
                        else
                                device_printf (sc->aac_dev, "%s\n", PrintBuffer_P);
                }

        } else {
                /*
                 * No HBA structure passed in so it has to be for the Kernel Debugger
                 */
                if ((sc != NULL) && (sc->FwDebugFlags & FW_DEBUG_FLAGS_NO_HEADERS_B))
                        printf ("%s\n", PrintBuffer_P);
                else if (sc != NULL)
                        device_printf (sc->aac_dev, "%s\n", PrintBuffer_P);
                else
                        printf("%s\n", PrintBuffer_P);
        }
}

void aacraid_fw_print_mem(struct aac_softc *sc, unsigned long PrintFlags, u_int8_t *Addr, int Count)
{
        int Offset, i;
        u_int32_t DebugFlags = 0;
        char Buffer[100];
        char *LineBuffer_P;

        /*
         * If we have an HBA structure, save off the flags and set the no
         * headers flag so we don't have garbage between our lines of data
         */
        if (sc != NULL) {
                DebugFlags = sc->FwDebugFlags;
                sc->FwDebugFlags |= FW_DEBUG_FLAGS_NO_HEADERS_B;
        }

        Offset = 0;

        /*
         * Loop through all the data
         */
        while (Offset < Count) {
                /*
                 * We will format each line into a buffer and then print out
                 * the entire line so set the pointer to the beginning of the
                 * buffer
                 */
                LineBuffer_P = Buffer;

                /*
                 * Set up the address in HEX
                 */
                sprintf(LineBuffer_P, "\n%04x  ", Offset);
                LineBuffer_P += 6;

                /*
                 * Set up 16 bytes in HEX format
                 */
                for (i = 0; i < 16; ++i) {
                        /*
                         * If we are past the count of data bytes to output,
                         * pad with blanks
                         */
                        if ((Offset + i) >= Count)
                                sprintf (LineBuffer_P, "   ");
                        else
                                sprintf (LineBuffer_P, "%02x ", Addr[Offset+i]);
                        LineBuffer_P += 3;

                        /*
                         * At the mid point we will put in a divider
                         */
                        if (i == 7) {
                                sprintf (LineBuffer_P, "- ");
                                LineBuffer_P += 2;
                        }
                }
                /*
                 * Now do the same 16 bytes at the end of the line in ASCII
                 * format
                 */
                sprintf (LineBuffer_P, "  ");
                LineBuffer_P += 2;
                for (i = 0; i < 16; ++i) {
                        /*
                         * If all data processed, OUT-O-HERE
                         */
                        if ((Offset + i) >= Count)
                                break;

                        /*
                         * If this is a printable ASCII character, convert it
                         */
                        if ((Addr[Offset+i] > 0x1F) && (Addr[Offset+i] < 0x7F))
                                sprintf (LineBuffer_P, "%c", Addr[Offset+i]);
                        else
                                sprintf (LineBuffer_P, ".");
                        ++LineBuffer_P;
                }
                /*
                 * The line is now formatted, so print it out
                 */
                aacraid_fw_printf(sc, PrintFlags, "%s", Buffer);

                /*
                 * Bump the offset by 16 for the next line
                 */
                Offset += 16;
        }

        /*
         * Restore the saved off flags
         */
        if (sc != NULL)
                sc->FwDebugFlags = DebugFlags;
}