text: /*++
  
  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"
  #include "debug.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_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
  
      #pragma data_seg("NONPAGE")
  
  
  
      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 = 0x00000000;
  
      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;
                  PUCHAR buffer;
                  index = InterlockedIncrement(&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(DPFLTR_CLASSPNP_ID, DPFLTR_INFO_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));
              }            
          }
  
          /*
           *  If the port driver returned STATUS_INSUFFICIENT_RESOURCES,
           *  make sure this is also the InternalStatus in the SRB so that we process it correctly.
           */
          if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
              ASSERT(SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_INTERNAL_ERROR);
              ASSERT(Pkt->Srb.InternalStatus == STATUS_INSUFFICIENT_RESOURCES);
          }
  
          /*
           *  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;
          }
          
      }
  
  
      VOID DbgLogSendPacket(TRANSFER_PACKET *Pkt)
      {
          PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
          PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
          KIRQL oldIrql;
  
          if (Pkt->OriginalIrp){
              Pkt->DbgOriginalIrpCopy = *Pkt->OriginalIrp;
              if (Pkt->OriginalIrp->MdlAddress){
                  Pkt->DbgMdlCopy = *Pkt->OriginalIrp->MdlAddress;
              }
          }
          
          KeQueryTickCount(&Pkt->DbgTimeSent);
          Pkt->DbgTimeReturned.QuadPart = 0L;
          
          KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
          fdoData->DbgPacketLogs[fdoData->DbgPacketLogNextIndex] = *Pkt;        
          fdoData->DbgPacketLogNextIndex++;
          fdoData->DbgPacketLogNextIndex %= DBG_NUM_PACKET_LOG_ENTRIES;
          KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
      }
  
      VOID DbgLogReturnPacket(TRANSFER_PACKET *Pkt)
      {
          PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
          PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
          KIRQL oldIrql;
  
          KeQueryTickCount(&Pkt->DbgTimeReturned);
  
          #if 0
              // ISSUE: there are some problems with this check (e.g. multiproc), so don't include it yet
              if (Pkt->OriginalIrp){
                  /*
                   *  No one should have touched the original irp while the packet was outstanding,
                   *  except for a couple fields that we ourselves update during the transfer 
                   *  or that are allowed to change;
                   *  make those couple fields the same and then to a bytewise compare
                   */
                  ULONG lenSame;
                  
                  Pkt->DbgOriginalIrpCopy.IoStatus.Status = Pkt->OriginalIrp->IoStatus.Status;
                  Pkt->DbgOriginalIrpCopy.IoStatus.Information = Pkt->OriginalIrp->IoStatus.Information;
                  Pkt->DbgOriginalIrpCopy.Tail.Overlay.DriverContext[0] = Pkt->OriginalIrp->Tail.Overlay.DriverContext[0];
                  Pkt->DbgOriginalIrpCopy.ThreadListEntry = Pkt->OriginalIrp->ThreadListEntry;
                  Pkt->DbgOriginalIrpCopy.Cancel = Pkt->OriginalIrp->Cancel;
                  
                  lenSame = (ULONG)RtlCompareMemory(Pkt->OriginalIrp, &Pkt->DbgOriginalIrpCopy, sizeof(IRP));
                  ASSERT(lenSame == sizeof(IRP));
              }
          #endif
          
          KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
          fdoData->DbgPacketLogs[fdoData->DbgPacketLogNextIndex] = *Pkt;        
          fdoData->DbgPacketLogNextIndex++;
          fdoData->DbgPacketLogNextIndex %= DBG_NUM_PACKET_LOG_ENTRIES;
          KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
      }
  
  
      VOID DbgLogFlushInfo(PCLASS_PRIVATE_FDO_DATA FdoData, BOOLEAN IsIO, BOOLEAN IsFUA, BOOLEAN IsFlush)
      {
  
          /*
           *  Reset all FUA/Flush logging fields.
           */
          if (FdoData->DbgInitFlushLogging){
              FdoData->DbgNumIORequests = 0;
              FdoData->DbgNumFUAs = 0;       
              FdoData->DbgNumFlushes = 0;    
              FdoData->DbgIOsSinceFUA = 0;
              FdoData->DbgIOsSinceFlush = 0;
              FdoData->DbgAveIOsToFUA = 0;      
              FdoData->DbgAveIOsToFlush = 0;   
              FdoData->DbgMaxIOsToFUA = 0;
              FdoData->DbgMaxIOsToFlush = 0;
              FdoData->DbgMinIOsToFUA = 0xffffffff;
              FdoData->DbgMinIOsToFlush = 0xffffffff;
              FdoData->DbgInitFlushLogging = FALSE;
          }
  
          if (IsIO){
              FdoData->DbgNumIORequests++;
              FdoData->DbgIOsSinceFlush++;
              if (IsFUA){
                  if (FdoData->DbgNumFUAs > 0){
                      FdoData->DbgMinIOsToFUA = min(FdoData->DbgMinIOsToFUA, FdoData->DbgIOsSinceFUA);
                  }
                  FdoData->DbgNumFUAs++;
                  FdoData->DbgAveIOsToFUA =  FdoData->DbgNumIORequests/FdoData->DbgNumFUAs;
                  FdoData->DbgIOsSinceFUA = 0;
              }
              else {
                  FdoData->DbgIOsSinceFUA++;
                  FdoData->DbgMaxIOsToFUA = max(FdoData->DbgMaxIOsToFUA, FdoData->DbgIOsSinceFUA);
              }
              FdoData->DbgMaxIOsToFlush = max(FdoData->DbgMaxIOsToFlush, FdoData->DbgIOsSinceFlush);
          }
          else if (IsFlush){
              if (FdoData->DbgNumFlushes > 0){
                  FdoData->DbgMinIOsToFlush = min(FdoData->DbgMinIOsToFlush, FdoData->DbgIOsSinceFlush);
              }
              FdoData->DbgNumFlushes++;
              FdoData->DbgAveIOsToFlush =  FdoData->DbgNumIORequests/FdoData->DbgNumFlushes;
              FdoData->DbgIOsSinceFlush = 0;
          }
          
      }
  
  #else
  
      // We have to keep this in the retail build for legacy.
      VOID ClassDebugPrint(CLASS_DEBUG_LEVEL DebugPrintLevel, PCCHAR DebugMessage, ...)
      {
      } 
  
  #endif 
  