/*++

Copyright (C) Microsoft Corporation, 1991 - 1999

Module Name:

    debug.c

Abstract:

    CLASSPNP debug code and data

Environment:

    kernel mode only

Notes:


Revision History:

--*/

#include "classp.h"

#if DBG

    //
    // default to not breaking in for lost irps, five minutes before we even
    // bother checking for lost irps, using standard debug print macros, and
    // using a 64k debug print buffer
    //

    #ifndef     CLASS_GLOBAL_BREAK_ON_LOST_IRPS
        #error "CLASS_GLOBAL_BREAK_ON_LOST_IRPS undefined"
        #define CLASS_GLOBAL_BREAK_ON_LOST_IRPS 0
    #endif   // CLASS_GLOBAL_BREAK_ON_LOST_IRPS

    #ifndef     CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB
        #error "CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB undefined"
        #define CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB 300
    #endif   // CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB

    #ifndef     CLASS_GLOBAL_USE_DELAYED_RETRY
        #error "CLASS_GLOBAL_USE_DELAYED_RETRY undefined"
        #define CLASS_GLOBAL_USE_DELAYED_RETRY 1
    #endif   // CLASS_GLOBAL_USE_DELAYED_RETRY

    #ifndef     CLASS_GLOBAL_BUFFERED_DEBUG_PRINT
        #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT undefined"
        #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT 0
    #endif   // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT

    #ifndef     CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE
        #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE undefined"
        #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE 512
    #endif   // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE

    #ifndef     CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS
        #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS undefined"
        #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS 512
    #endif   // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS

    #ifdef ALLOC_DATA_PRAGMA
        #pragma data_seg("NONPAGE")
    #endif



    CLASSPNP_GLOBALS ClasspnpGlobals;

    //
    // the low sixteen bits are used to see if the debug level is high enough
    // the high sixteen bits are used to singly enable debug levels 1-16
    //
    LONG ClassDebug = 0xFFFFFFFF;

    BOOLEAN DebugTrapOnWarn = FALSE;

    VOID ClasspInitializeDebugGlobals()
    {
        KIRQL irql;

        if (InterlockedCompareExchange(&ClasspnpGlobals.Initializing, 1, 0) == 0) {

            KeInitializeSpinLock(&ClasspnpGlobals.SpinLock);

            KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql);

            DebugPrint((1, "CLASSPNP.SYS => Initializing ClasspnpGlobals...\n"));

            ClasspnpGlobals.Buffer = NULL;
            ClasspnpGlobals.Index = -1;
            ClasspnpGlobals.BreakOnLostIrps = CLASS_GLOBAL_BREAK_ON_LOST_IRPS;
            ClasspnpGlobals.EachBufferSize = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE;
            ClasspnpGlobals.NumberOfBuffers = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS;
            ClasspnpGlobals.SecondsToWaitForIrps = CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB;

            //
            // this should be the last item set
            //

            ClasspnpGlobals.UseBufferedDebugPrint = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT;

            KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql);

            InterlockedExchange(&ClasspnpGlobals.Initialized, 1);

        }
    }



    /*++////////////////////////////////////////////////////////////////////////////

    ClassDebugPrint()

    Routine Description:

        Debug print for all class drivers, NOOP on FRE versions.
        Allows printing to a debug buffer (with auto fallback to kdprint) by
        properly setting the Globals in classpnp on CHK versions.

    Arguments:

        Debug print level, or from 0 to 3 for legacy drivers.

    Return Value:

        None

    --*/
    VOID ClassDebugPrint(CLASS_DEBUG_LEVEL DebugPrintLevel, PCCHAR DebugMessage, ...)
    {
        va_list ap;
        va_start(ap, DebugMessage);

        if ((DebugPrintLevel <= (ClassDebug & 0x0000ffff)) ||
            ((1 << (DebugPrintLevel + 15)) & ClassDebug)) {

            if (ClasspnpGlobals.UseBufferedDebugPrint &&
                ClasspnpGlobals.Buffer == NULL) {

                //
                // this double-check prevents always taking
                // a spinlock just to ensure we have a buffer
                //

                KIRQL irql;

                KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql);
                if (ClasspnpGlobals.Buffer == NULL) {

                    SIZE_T bufferSize;
                    bufferSize = ClasspnpGlobals.NumberOfBuffers *
                                 ClasspnpGlobals.EachBufferSize;
                    DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
                               "ClassDebugPrint: Allocating %x bytes for "
                               "classdebugprint buffer\n", bufferSize);
                    ClasspnpGlobals.Index       = -1;
                    ClasspnpGlobals.Buffer =
                        ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'bDcS');
                    DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
                               "ClassDebugPrint: Allocated buffer at %p\n",
                               ClasspnpGlobals.Buffer);

                }
                KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql);

            }

            if (ClasspnpGlobals.UseBufferedDebugPrint &&
                ClasspnpGlobals.Buffer != NULL) {

                //
                // we never free the buffer, so once it exists,
                // we can just print to it with immunity
                //

                ULONG index;
                PSTR buffer;
                index = InterlockedIncrement((PLONG)&ClasspnpGlobals.Index);
                index %= ClasspnpGlobals.NumberOfBuffers;
                index *= (ULONG)ClasspnpGlobals.EachBufferSize;

                buffer = ClasspnpGlobals.Buffer;
                buffer += index;

                _vsnprintf(buffer, ClasspnpGlobals.EachBufferSize, DebugMessage, ap);

            } else {

                //
                // either we could not allocate a buffer for debug prints
                // or buffered debug prints are disabled
                //
                vDbgPrintEx(-1, DPFLTR_ERROR_LEVEL, DebugMessage, ap);

            }

        }

        va_end(ap);

    } 


    char *DbgGetIoctlStr(ULONG ioctl)
    {
        char *ioctlStr = "?";
        
        switch (ioctl){
            
            #undef MAKE_CASE             
            #define MAKE_CASE(ioctlCode) case ioctlCode: ioctlStr = #ioctlCode; break;

            MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY)
            MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY2)
            MAKE_CASE(IOCTL_STORAGE_MEDIA_REMOVAL)
            MAKE_CASE(IOCTL_STORAGE_EJECT_MEDIA)
            MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA)
            MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA2)
            MAKE_CASE(IOCTL_STORAGE_RESERVE)
            MAKE_CASE(IOCTL_STORAGE_RELEASE)
            MAKE_CASE(IOCTL_STORAGE_FIND_NEW_DEVICES)
            MAKE_CASE(IOCTL_STORAGE_EJECTION_CONTROL)
            MAKE_CASE(IOCTL_STORAGE_MCN_CONTROL)
            MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES)
            MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES_EX)
            MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER)
            MAKE_CASE(IOCTL_STORAGE_GET_HOTPLUG_INFO)
            MAKE_CASE(IOCTL_STORAGE_RESET_BUS)
            MAKE_CASE(IOCTL_STORAGE_RESET_DEVICE)
            MAKE_CASE(IOCTL_STORAGE_GET_DEVICE_NUMBER)
            MAKE_CASE(IOCTL_STORAGE_PREDICT_FAILURE)
            MAKE_CASE(IOCTL_STORAGE_QUERY_PROPERTY)
            MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_BUS)
            MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_DEVICE)
        }
      
        return ioctlStr;
    }

    char *DbgGetScsiOpStr(PSCSI_REQUEST_BLOCK Srb)
    {
        PCDB pCdb = (PCDB)Srb->Cdb;
        UCHAR scsiOp = pCdb->CDB6GENERIC.OperationCode;
        char *scsiOpStr = "?";

        switch (scsiOp){

            #undef MAKE_CASE             
            #define MAKE_CASE(scsiOpCode) case scsiOpCode: scsiOpStr = #scsiOpCode; break;
            
            MAKE_CASE(SCSIOP_TEST_UNIT_READY)
            MAKE_CASE(SCSIOP_REWIND)    // aka SCSIOP_REZERO_UNIT
            MAKE_CASE(SCSIOP_REQUEST_BLOCK_ADDR)
            MAKE_CASE(SCSIOP_REQUEST_SENSE)
            MAKE_CASE(SCSIOP_FORMAT_UNIT)
            MAKE_CASE(SCSIOP_READ_BLOCK_LIMITS)
            MAKE_CASE(SCSIOP_INIT_ELEMENT_STATUS)   // aka SCSIOP_REASSIGN_BLOCKS
            MAKE_CASE(SCSIOP_RECEIVE)       // aka SCSIOP_READ6
            MAKE_CASE(SCSIOP_SEND)  // aka SCSIOP_WRITE6, SCSIOP_PRINT
            MAKE_CASE(SCSIOP_SLEW_PRINT)    // aka SCSIOP_SEEK6, SCSIOP_TRACK_SELECT
            MAKE_CASE(SCSIOP_SEEK_BLOCK)
            MAKE_CASE(SCSIOP_PARTITION)
            MAKE_CASE(SCSIOP_READ_REVERSE)
            MAKE_CASE(SCSIOP_FLUSH_BUFFER)      // aka SCSIOP_WRITE_FILEMARKS
            MAKE_CASE(SCSIOP_SPACE)
            MAKE_CASE(SCSIOP_INQUIRY)
            MAKE_CASE(SCSIOP_VERIFY6)
            MAKE_CASE(SCSIOP_RECOVER_BUF_DATA)
            MAKE_CASE(SCSIOP_MODE_SELECT)
            MAKE_CASE(SCSIOP_RESERVE_UNIT)
            MAKE_CASE(SCSIOP_RELEASE_UNIT)
            MAKE_CASE(SCSIOP_COPY)
            MAKE_CASE(SCSIOP_ERASE)
            MAKE_CASE(SCSIOP_MODE_SENSE)
            MAKE_CASE(SCSIOP_START_STOP_UNIT)   // aka SCSIOP_STOP_PRINT, SCSIOP_LOAD_UNLOAD
            MAKE_CASE(SCSIOP_RECEIVE_DIAGNOSTIC)
            MAKE_CASE(SCSIOP_SEND_DIAGNOSTIC)
            MAKE_CASE(SCSIOP_MEDIUM_REMOVAL)
            MAKE_CASE(SCSIOP_READ_FORMATTED_CAPACITY)
            MAKE_CASE(SCSIOP_READ_CAPACITY)
            MAKE_CASE(SCSIOP_READ)
            MAKE_CASE(SCSIOP_WRITE)
            MAKE_CASE(SCSIOP_SEEK)  // aka SCSIOP_LOCATE, SCSIOP_POSITION_TO_ELEMENT
            MAKE_CASE(SCSIOP_WRITE_VERIFY)
            MAKE_CASE(SCSIOP_VERIFY)
            MAKE_CASE(SCSIOP_SEARCH_DATA_HIGH)
            MAKE_CASE(SCSIOP_SEARCH_DATA_EQUAL)
            MAKE_CASE(SCSIOP_SEARCH_DATA_LOW)
            MAKE_CASE(SCSIOP_SET_LIMITS)
            MAKE_CASE(SCSIOP_READ_POSITION)
            MAKE_CASE(SCSIOP_SYNCHRONIZE_CACHE)
            MAKE_CASE(SCSIOP_COMPARE)
            MAKE_CASE(SCSIOP_COPY_COMPARE)
            MAKE_CASE(SCSIOP_WRITE_DATA_BUFF)
            MAKE_CASE(SCSIOP_READ_DATA_BUFF)
            MAKE_CASE(SCSIOP_CHANGE_DEFINITION)
            MAKE_CASE(SCSIOP_READ_SUB_CHANNEL)
            MAKE_CASE(SCSIOP_READ_TOC)
            MAKE_CASE(SCSIOP_READ_HEADER)
            MAKE_CASE(SCSIOP_PLAY_AUDIO)
            MAKE_CASE(SCSIOP_GET_CONFIGURATION)
            MAKE_CASE(SCSIOP_PLAY_AUDIO_MSF)
            MAKE_CASE(SCSIOP_PLAY_TRACK_INDEX)
            MAKE_CASE(SCSIOP_PLAY_TRACK_RELATIVE)
            MAKE_CASE(SCSIOP_GET_EVENT_STATUS)
            MAKE_CASE(SCSIOP_PAUSE_RESUME)
            MAKE_CASE(SCSIOP_LOG_SELECT)
            MAKE_CASE(SCSIOP_LOG_SENSE)
            MAKE_CASE(SCSIOP_STOP_PLAY_SCAN)
            MAKE_CASE(SCSIOP_READ_DISK_INFORMATION)
            MAKE_CASE(SCSIOP_READ_TRACK_INFORMATION)
            MAKE_CASE(SCSIOP_RESERVE_TRACK_RZONE)
            MAKE_CASE(SCSIOP_SEND_OPC_INFORMATION)
            MAKE_CASE(SCSIOP_MODE_SELECT10)
            MAKE_CASE(SCSIOP_MODE_SENSE10)
            MAKE_CASE(SCSIOP_CLOSE_TRACK_SESSION)
            MAKE_CASE(SCSIOP_READ_BUFFER_CAPACITY)
            MAKE_CASE(SCSIOP_SEND_CUE_SHEET)
            MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_IN)
            MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_OUT)
            MAKE_CASE(SCSIOP_REPORT_LUNS)
            MAKE_CASE(SCSIOP_BLANK)
            MAKE_CASE(SCSIOP_SEND_KEY)
            MAKE_CASE(SCSIOP_REPORT_KEY)
            MAKE_CASE(SCSIOP_MOVE_MEDIUM)
            MAKE_CASE(SCSIOP_LOAD_UNLOAD_SLOT)  // aka SCSIOP_EXCHANGE_MEDIUM
            MAKE_CASE(SCSIOP_SET_READ_AHEAD)
            MAKE_CASE(SCSIOP_READ_DVD_STRUCTURE)
            MAKE_CASE(SCSIOP_REQUEST_VOL_ELEMENT)
            MAKE_CASE(SCSIOP_SEND_VOLUME_TAG)
            MAKE_CASE(SCSIOP_READ_ELEMENT_STATUS)
            MAKE_CASE(SCSIOP_READ_CD_MSF)
            MAKE_CASE(SCSIOP_SCAN_CD)
            MAKE_CASE(SCSIOP_SET_CD_SPEED)
            MAKE_CASE(SCSIOP_PLAY_CD)
            MAKE_CASE(SCSIOP_MECHANISM_STATUS)
            MAKE_CASE(SCSIOP_READ_CD)
            MAKE_CASE(SCSIOP_SEND_DVD_STRUCTURE)
            MAKE_CASE(SCSIOP_INIT_ELEMENT_RANGE)
        }
        
        return scsiOpStr;
    }


    char *DbgGetSrbStatusStr(PSCSI_REQUEST_BLOCK Srb)
    {
        char *srbStatStr = "?";
        
        switch (Srb->SrbStatus){

            #undef MAKE_CASE
            #define MAKE_CASE(srbStat) \
                        case srbStat: \
                            srbStatStr = #srbStat; \
                            break; \
                        case srbStat|SRB_STATUS_QUEUE_FROZEN: \
                            srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN"; \
                            break; \
                        case srbStat|SRB_STATUS_AUTOSENSE_VALID: \
                            srbStatStr = #srbStat "|SRB_STATUS_AUTOSENSE_VALID"; \
                            break; \
                        case srbStat|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID: \
                            srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID"; \
                            break; 

            MAKE_CASE(SRB_STATUS_PENDING)
            MAKE_CASE(SRB_STATUS_SUCCESS)
            MAKE_CASE(SRB_STATUS_ABORTED)
            MAKE_CASE(SRB_STATUS_ABORT_FAILED)
            MAKE_CASE(SRB_STATUS_ERROR)
            MAKE_CASE(SRB_STATUS_BUSY)
            MAKE_CASE(SRB_STATUS_INVALID_REQUEST)
            MAKE_CASE(SRB_STATUS_INVALID_PATH_ID)
            MAKE_CASE(SRB_STATUS_NO_DEVICE)
            MAKE_CASE(SRB_STATUS_TIMEOUT)
            MAKE_CASE(SRB_STATUS_SELECTION_TIMEOUT)
            MAKE_CASE(SRB_STATUS_COMMAND_TIMEOUT)
            MAKE_CASE(SRB_STATUS_MESSAGE_REJECTED)
            MAKE_CASE(SRB_STATUS_BUS_RESET)
            MAKE_CASE(SRB_STATUS_PARITY_ERROR)
            MAKE_CASE(SRB_STATUS_REQUEST_SENSE_FAILED)
            MAKE_CASE(SRB_STATUS_NO_HBA)
            MAKE_CASE(SRB_STATUS_DATA_OVERRUN)
            MAKE_CASE(SRB_STATUS_UNEXPECTED_BUS_FREE)
            MAKE_CASE(SRB_STATUS_PHASE_SEQUENCE_FAILURE)
            MAKE_CASE(SRB_STATUS_BAD_SRB_BLOCK_LENGTH)
            MAKE_CASE(SRB_STATUS_REQUEST_FLUSHED)
            MAKE_CASE(SRB_STATUS_INVALID_LUN)
            MAKE_CASE(SRB_STATUS_INVALID_TARGET_ID)
            MAKE_CASE(SRB_STATUS_BAD_FUNCTION)
            MAKE_CASE(SRB_STATUS_ERROR_RECOVERY)
            MAKE_CASE(SRB_STATUS_NOT_POWERED)
            MAKE_CASE(SRB_STATUS_INTERNAL_ERROR)
        }

        return srbStatStr;
    }

    
    char *DbgGetSenseCodeStr(PSCSI_REQUEST_BLOCK Srb)
    {
        char *senseCodeStr = "?";

        if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
            PSENSE_DATA senseData;
            UCHAR senseCode;

            ASSERT(Srb->SenseInfoBuffer);
            senseData = Srb->SenseInfoBuffer;
            senseCode = senseData->SenseKey & 0xf;
                        
            switch (senseCode){

                #undef MAKE_CASE             
                #define MAKE_CASE(snsCod) case snsCod: senseCodeStr = #snsCod; break;
            
                MAKE_CASE(SCSI_SENSE_NO_SENSE)
                MAKE_CASE(SCSI_SENSE_RECOVERED_ERROR)
                MAKE_CASE(SCSI_SENSE_NOT_READY)
                MAKE_CASE(SCSI_SENSE_MEDIUM_ERROR)
                MAKE_CASE(SCSI_SENSE_HARDWARE_ERROR)
                MAKE_CASE(SCSI_SENSE_ILLEGAL_REQUEST)
                MAKE_CASE(SCSI_SENSE_UNIT_ATTENTION)
                MAKE_CASE(SCSI_SENSE_DATA_PROTECT)
                MAKE_CASE(SCSI_SENSE_BLANK_CHECK)
                MAKE_CASE(SCSI_SENSE_UNIQUE)
                MAKE_CASE(SCSI_SENSE_COPY_ABORTED)
                MAKE_CASE(SCSI_SENSE_ABORTED_COMMAND)
                MAKE_CASE(SCSI_SENSE_EQUAL)
                MAKE_CASE(SCSI_SENSE_VOL_OVERFLOW)
                MAKE_CASE(SCSI_SENSE_MISCOMPARE)
                MAKE_CASE(SCSI_SENSE_RESERVED)               
            }
        }

        return senseCodeStr;
    }


    char *DbgGetAdditionalSenseCodeStr(PSCSI_REQUEST_BLOCK Srb)
    {
        char *adSenseCodeStr = "?";
        
        if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
            PSENSE_DATA senseData;
            UCHAR adSenseCode;

            ASSERT(Srb->SenseInfoBuffer);
            senseData = Srb->SenseInfoBuffer;
            adSenseCode = senseData->AdditionalSenseCode;
                        
            switch (adSenseCode){

                #undef MAKE_CASE             
                #define MAKE_CASE(adSnsCod) case adSnsCod: adSenseCodeStr = #adSnsCod; break;
    
                MAKE_CASE(SCSI_ADSENSE_NO_SENSE)
                MAKE_CASE(SCSI_ADSENSE_LUN_NOT_READY)
                MAKE_CASE(SCSI_ADSENSE_TRACK_ERROR)
                MAKE_CASE(SCSI_ADSENSE_SEEK_ERROR)
                MAKE_CASE(SCSI_ADSENSE_REC_DATA_NOECC)
                MAKE_CASE(SCSI_ADSENSE_REC_DATA_ECC)
                MAKE_CASE(SCSI_ADSENSE_ILLEGAL_COMMAND)
                MAKE_CASE(SCSI_ADSENSE_ILLEGAL_BLOCK)
                MAKE_CASE(SCSI_ADSENSE_INVALID_CDB)
                MAKE_CASE(SCSI_ADSENSE_INVALID_LUN)
                MAKE_CASE(SCSI_ADSENSE_WRITE_PROTECT)   // aka SCSI_ADWRITE_PROTECT
                MAKE_CASE(SCSI_ADSENSE_MEDIUM_CHANGED)
                MAKE_CASE(SCSI_ADSENSE_BUS_RESET)
                MAKE_CASE(SCSI_ADSENSE_INVALID_MEDIA)
                MAKE_CASE(SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)
                MAKE_CASE(SCSI_ADSENSE_POSITION_ERROR)
                MAKE_CASE(SCSI_ADSENSE_OPERATOR_REQUEST)
                MAKE_CASE(SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED)
                MAKE_CASE(SCSI_ADSENSE_COPY_PROTECTION_FAILURE)
                MAKE_CASE(SCSI_ADSENSE_VENDOR_UNIQUE)
                MAKE_CASE(SCSI_ADSENSE_MUSIC_AREA)
                MAKE_CASE(SCSI_ADSENSE_DATA_AREA)
                MAKE_CASE(SCSI_ADSENSE_VOLUME_OVERFLOW)
            }
        }

        return adSenseCodeStr;
    }


    char *DbgGetAdditionalSenseCodeQualifierStr(PSCSI_REQUEST_BLOCK Srb)
    {
        char *adSenseCodeQualStr = "?";
        
        if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
            PSENSE_DATA senseData;
            UCHAR adSenseCode;
            UCHAR adSenseCodeQual;
            
            ASSERT(Srb->SenseInfoBuffer);
            senseData = Srb->SenseInfoBuffer;
            adSenseCode = senseData->AdditionalSenseCode;
            adSenseCodeQual = senseData->AdditionalSenseCodeQualifier;
            
            switch (adSenseCode){

                #undef MAKE_CASE             
                #define MAKE_CASE(adSnsCodQual) case adSnsCodQual: adSenseCodeQualStr = #adSnsCodQual; break;

                case SCSI_ADSENSE_LUN_NOT_READY:
                    switch (adSenseCodeQual){
                        MAKE_CASE(SCSI_SENSEQ_CAUSE_NOT_REPORTABLE)
                        MAKE_CASE(SCSI_SENSEQ_BECOMING_READY)
                        MAKE_CASE(SCSI_SENSEQ_INIT_COMMAND_REQUIRED)
                        MAKE_CASE(SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED)
                        MAKE_CASE(SCSI_SENSEQ_FORMAT_IN_PROGRESS)
                        MAKE_CASE(SCSI_SENSEQ_REBUILD_IN_PROGRESS)
                        MAKE_CASE(SCSI_SENSEQ_RECALCULATION_IN_PROGRESS)
                        MAKE_CASE(SCSI_SENSEQ_OPERATION_IN_PROGRESS)
                        MAKE_CASE(SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS)                        
                    }
                    break;
                case SCSI_ADSENSE_NO_SENSE:
                    switch (adSenseCodeQual){
                        MAKE_CASE(SCSI_SENSEQ_FILEMARK_DETECTED)
                        MAKE_CASE(SCSI_SENSEQ_END_OF_MEDIA_DETECTED)
                        MAKE_CASE(SCSI_SENSEQ_SETMARK_DETECTED)
                        MAKE_CASE(SCSI_SENSEQ_BEGINNING_OF_MEDIA_DETECTED)
                    }
                    break;
                case SCSI_ADSENSE_ILLEGAL_BLOCK:
                    switch (adSenseCodeQual){
                        MAKE_CASE(SCSI_SENSEQ_ILLEGAL_ELEMENT_ADDR)
                    }
                    break;
                case SCSI_ADSENSE_POSITION_ERROR:
                    switch (adSenseCodeQual){
                        MAKE_CASE(SCSI_SENSEQ_DESTINATION_FULL)
                        MAKE_CASE(SCSI_SENSEQ_SOURCE_EMPTY)
                    }
                    break;
                case SCSI_ADSENSE_INVALID_MEDIA:
                    switch (adSenseCodeQual){
                        MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_MEDIA_INSTALLED)
                        MAKE_CASE(SCSI_SENSEQ_UNKNOWN_FORMAT)
                        MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_FORMAT)
                        MAKE_CASE(SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED)
                    }
                    break;
                case SCSI_ADSENSE_OPERATOR_REQUEST:
                    switch (adSenseCodeQual){
                        MAKE_CASE(SCSI_SENSEQ_STATE_CHANGE_INPUT)
                        MAKE_CASE(SCSI_SENSEQ_MEDIUM_REMOVAL)
                        MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_ENABLE)
                        MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_DISABLE)
                    }
                    break;
                case SCSI_ADSENSE_COPY_PROTECTION_FAILURE:
                    switch (adSenseCodeQual){
                        MAKE_CASE(SCSI_SENSEQ_AUTHENTICATION_FAILURE)
                        MAKE_CASE(SCSI_SENSEQ_KEY_NOT_PRESENT)
                        MAKE_CASE(SCSI_SENSEQ_KEY_NOT_ESTABLISHED)
                        MAKE_CASE(SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION)
                        MAKE_CASE(SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT)
                        MAKE_CASE(SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR)
                    }
                    break;
            }
        }

        return adSenseCodeQualStr;
    }


    /*
     *  DbgCheckReturnedPkt
     *
     *      Check a completed TRANSFER_PACKET for all sorts of error conditions
     *      and warn/trap appropriately.
     */
    VOID DbgCheckReturnedPkt(TRANSFER_PACKET *Pkt)
    {
        PCDB pCdb = (PCDB)Pkt->Srb.Cdb;
        
        ASSERT(Pkt->Srb.OriginalRequest == Pkt->Irp);
        ASSERT(Pkt->Srb.DataBuffer == Pkt->BufPtrCopy);
        ASSERT(Pkt->Srb.DataTransferLength <= Pkt->BufLenCopy);
        ASSERT(!Pkt->Irp->CancelRoutine);
            
        if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_PENDING){
            DBGERR(("SRB completed with status PENDING in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
                        Pkt, 
                        DBGGETSCSIOPSTR(&Pkt->Srb), 
                        DBGGETSRBSTATUSSTR(&Pkt->Srb),
                        (ULONG)Pkt->Srb.SrbStatus, 
                        Pkt->Irp->IoStatus.Status));
        }
        else if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS){
            /*
             *  Make sure SRB and IRP status match.
             */
            if (!NT_SUCCESS(Pkt->Irp->IoStatus.Status)){
                DBGWARN(("SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
                            Pkt, 
                            DBGGETSCSIOPSTR(&Pkt->Srb), 
                            DBGGETSRBSTATUSSTR(&Pkt->Srb),
                            (ULONG)Pkt->Srb.SrbStatus, 
                            Pkt->Irp->IoStatus.Status));
            }

            if (Pkt->Irp->IoStatus.Information != Pkt->Srb.DataTransferLength){
                DBGERR(("SRB and IRP result transfer lengths don't match in succeeded packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%xh).",
                            Pkt, 
                            DBGGETSCSIOPSTR(&Pkt->Srb), 
                            DBGGETSRBSTATUSSTR(&Pkt->Srb),
                            Pkt->Srb.DataTransferLength,
                            Pkt->Irp->IoStatus.Information));
            }            
        }
        else {
            if (NT_SUCCESS(Pkt->Irp->IoStatus.Status)){
                DBGWARN(("SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
                            Pkt, 
                            DBGGETSCSIOPSTR(&Pkt->Srb), 
                            DBGGETSRBSTATUSSTR(&Pkt->Srb),
                            (ULONG)Pkt->Srb.SrbStatus, 
                            Pkt->Irp->IoStatus.Status));
            }            
            DBGTRACE(ClassDebugWarning, ("Packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", 
                            Pkt, 
                            DBGGETSCSIOPSTR(&Pkt->Srb), 
                            DBGGETSRBSTATUSSTR(&Pkt->Srb),
                            (ULONG)Pkt->Srb.SrbStatus, 
                            Pkt->Irp->IoStatus.Status, 
                            DBGGETSENSECODESTR(&Pkt->Srb), 
                            DBGGETADSENSECODESTR(&Pkt->Srb), 
                            DBGGETADSENSEQUALIFIERSTR(&Pkt->Srb)));

            /*
             *  If the SRB failed with underrun or overrun, then the actual
             *  transferred length should be returned in both SRB and IRP.
             *  (SRB's only have an error status for overrun, so it's overloaded).
             */
            if ((SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) &&
               (Pkt->Irp->IoStatus.Information != Pkt->Srb.DataTransferLength)){
                DBGERR(("SRB and IRP result transfer lengths don't match in failed packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%xh).",
                            Pkt, 
                            DBGGETSCSIOPSTR(&Pkt->Srb), 
                            DBGGETSRBSTATUSSTR(&Pkt->Srb),
                            Pkt->Srb.DataTransferLength,
                            Pkt->Irp->IoStatus.Information));
            }            
        }


        /*
         *  Some miniport drivers have been caught changing the SCSI operation
         *  code in the SRB.  This is absolutely disallowed as it breaks our error handling.
         */
        switch (pCdb->CDB10.OperationCode){
            case SCSIOP_MEDIUM_REMOVAL:
            case SCSIOP_MODE_SENSE:
            case SCSIOP_READ_CAPACITY:
            case SCSIOP_READ:
            case SCSIOP_WRITE:
            case SCSIOP_START_STOP_UNIT:    
                break;
            default:
                DBGERR(("Miniport illegally changed Srb.Cdb.OperationCode in packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", 
                                Pkt, 
                                DBGGETSCSIOPSTR(&Pkt->Srb), 
                                DBGGETSRBSTATUSSTR(&Pkt->Srb),
                                (ULONG)Pkt->Srb.SrbStatus, 
                                Pkt->Irp->IoStatus.Status, 
                                DBGGETSENSECODESTR(&Pkt->Srb), 
                                DBGGETADSENSECODESTR(&Pkt->Srb), 
                                DBGGETADSENSEQUALIFIERSTR(&Pkt->Srb)));
                break;
        }
        
    }
    
#else

    // We have to keep this in the retail build for legacy.
    VOID ClassDebugPrint(CLASS_DEBUG_LEVEL DebugPrintLevel, PCCHAR DebugMessage, ...)
    {
        DbgPrint("retail build\n");
    } 

#endif