⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dvdproxy.cpp

📁 DVD工具dvdsynth的源码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/***********************************************************************
 Copyright 2002 Ben Rudiak-Gould.

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA,
 or visit <http://www.gnu.org/copyleft/gpl.html>.
***********************************************************************/


#include "scsi-miniport.h"
#include "dvdproxy2k.h"
#include "../include/dvdsynth-scsi.h"


#define min(a,b) ((a)<(b)?(a):(b))


// The reason USER_LISTEN_TIMEOUT is set so small is that despite
// my best efforts I cannot get the ScsiPort driver to acknowledge
// the MultipleRequestPerLu or TaggedQueuing flags. As a result,
// the dummy device cannot respond to an INQUIRY command until the
// current listener (if any) has timed out.

//static const unsigned TICK_LENGTH = 1000000;  // in usec
//static const UCHAR USER_LISTEN_TIMEOUT = 1;     // in ticks
//static const UCHAR USER_RESPONSE_TIMEOUT = 2;     // in ticks


#ifdef DBGLEVEL
char* DebugFmtHex(char* p, unsigned x, int digits) {
   for (int i=digits-1; i>=0; --i) {
      p[i] = "0123456789abcdef"[x&15];
      x >>= 4;
   }
   return p+digits;
}

const char* CDBString(const unsigned char* cdb, int cdblen) {
   static char buf[16*3];
   if (cdblen < 0) cdblen = 0;
   if (cdblen > 16) cdblen = 16;
   char* p = buf;
   for (int i=0; i<cdblen; ++i) {
      if (i>0) *p++ = ' ';
      p = DebugFmtHex(p, cdb[i], 2);
   }
   *p = 0;
   return buf;
}
#endif


class SRBQueue {
private:
   enum { SRB_QUEUE_SIZE = 16 };    // should be a power of 2
   SCSI_REQUEST_BLOCK* q[SRB_QUEUE_SIZE];
   ULONG retire, dispatch, incoming;    // retire <= dispatch <= incoming
public:
   void Clear() { retire = dispatch = incoming; }
//   void CompleteAll(DVDProxy* bus);

   bool AddIncomingSRB(SCSI_REQUEST_BLOCK* srb) {
      if (ULONG(incoming-retire) >= ULONG(SRB_QUEUE_SIZE)) {
         return false;
      } else {
         q[(incoming++)%SRB_QUEUE_SIZE] = srb;
         return true;
      }
   }
   SCSI_REQUEST_BLOCK* GetDispatchSRB(ULONG* index) {
      while (dispatch != incoming && q[dispatch%SRB_QUEUE_SIZE] == NULL)
         ++dispatch;
      if (dispatch == incoming) {
         return 0;
      } else {
         *index = dispatch;
         return q[(dispatch++)%SRB_QUEUE_SIZE];
      }
   }
   void UngetDispatchSRB() {     // dangerous unless called immediately after GetDispatchSRB
      --dispatch;
   }
   SCSI_REQUEST_BLOCK* RemoveRetireeByIndex(ULONG index) {
      if (ULONG(index-retire) >= ULONG(dispatch-retire)) {
         return 0;
      } else {
         SCSI_REQUEST_BLOCK* result = q[index%SRB_QUEUE_SIZE];
         q[index%SRB_QUEUE_SIZE] = NULL;
         while (retire != dispatch && q[retire%SRB_QUEUE_SIZE] == NULL)
            ++retire;
         return result;
      }
   }
   bool RemoveSRB(SCSI_REQUEST_BLOCK* srb) {
      for (ULONG i = retire; i != incoming; ++i) {
         if (q[i%SRB_QUEUE_SIZE] == srb) {
            q[i%SRB_QUEUE_SIZE] = NULL;
            while (dispatch != incoming && q[dispatch%SRB_QUEUE_SIZE] == NULL)
               ++dispatch;
            while (retire != dispatch && q[retire%SRB_QUEUE_SIZE] == NULL)
               ++retire;
            return true;
         }
      }
      return false;
   }

#ifdef DBGLEVEL
   char* DebugFmtString(char* p, const char* s) {
      while (*s)
         *p++ = *s++;
      return p;
   }

   void DebugPrintQueue() {
      char buf[20 + SRB_QUEUE_SIZE*10];
      char* p = buf;
      p = DebugFmtString(p, "queue = ");
      for (ULONG i = retire; i != incoming; ++i) {
         if (i == dispatch)
            p = DebugFmtString(p, "| ");
         p = DebugFmtHex(p, (unsigned)q[i%SRB_QUEUE_SIZE], 8);
         *p++ = ' ';
      }
      if (incoming == dispatch)
         *p++ = '|';
      *p = 0;
      DBGPRINT((DBGLEVEL buf));
   }
#endif
};


static const SenseData default_sense = { 0x70, 0, 0, {0,0,0,0}, 10, {0,0,0,0}, 0, 0, 0, {0,0,0} };


struct DVDProxy {
   SRBQueue queue;
   SCSI_REQUEST_BLOCK* srb_listen;     // user-mode app's listening SRB
   bool user_attached;
   UCHAR user_listen_countdown;     // after this many ticks, fail the srb_listen SRB
   UCHAR user_response_countdown;   // after this many ticks, assume the user app has died
   unsigned tick_length;
   InquiryData inquiry_data[31];

   void Complete(SCSI_REQUEST_BLOCK* completed_srb);

   void CompleteWithScsiSense(SCSI_REQUEST_BLOCK* completed_srb, unsigned scsi_sense);

   void CompleteWithScsiSuccess(SCSI_REQUEST_BLOCK* completed_srb) {
      completed_srb->SrbStatus = SRB_STATUS_SUCCESS;
      completed_srb->ScsiStatus = SCSISTAT_GOOD;
      Complete(completed_srb);
   }

   void CompleteWithSrbStatus(SCSI_REQUEST_BLOCK* completed_srb, UCHAR srb_status) {
      completed_srb->SrbStatus = srb_status;
      completed_srb->ScsiStatus = SCSISTAT_GOOD;
      Complete(completed_srb);
   }

   static bool ConstructUserModeRequest(SCSI_REQUEST_BLOCK* src, ULONG sequence_number, SCSI_REQUEST_BLOCK* dst);
   static void UnpackUserModeResponse(SCSI_REQUEST_BLOCK* src, SCSI_REQUEST_BLOCK* dst);

   void CompleteWithData(SCSI_REQUEST_BLOCK* Srb, const unsigned char* data, unsigned len, unsigned alloc);

   void HandleSkeletonDevice(SCSI_REQUEST_BLOCK* Srb);
   void HandleDummyDevice(SCSI_REQUEST_BLOCK* Srb);

   void QueueSRB(SCSI_REQUEST_BLOCK* Srb);

   BOOLEAN ResetBus(ULONG PathId);
   BOOLEAN StartIo(SCSI_REQUEST_BLOCK* Srb);
   VOID ScsiTimer();
};


/*
inline void SRBQueue::CompleteAll(DVDProxy* bus) {
   dispatch = incoming;
   while (retire != dispatch) {
      SCSI_REQUEST_BLOCK* srb = q[retire%SRB_QUEUE_SIZE]
      if (srb)
         bus->HandleSkeletonDevice(srb);
      ++retire;
   }
}
*/


// timer proc is needed iff user_attached is true.
// if user_attached goes to false, immediately fail all SRBs in the queue.


VOID ZeroMemory(IN PVOID Buffer, IN ULONG Count) {
   for (ULONG i=0; i<Count; ++i)
      ((UCHAR*)Buffer)[i] = 0;
}


VOID __stdcall HwScsiTimer(IN PVOID DeviceExtension) {
   ((DVDProxy*)DeviceExtension)->ScsiTimer();
}

BOOLEAN __stdcall HwStartIo(IN PVOID DeviceExtension, IN PSCSI_REQUEST_BLOCK Srb) {
   return ((DVDProxy*)DeviceExtension)->StartIo(Srb);
}

BOOLEAN __stdcall HwResetBus(PVOID DeviceExtension, ULONG PathId) {
   return ((DVDProxy*)DeviceExtension)->ResetBus(PathId);
}

BOOLEAN __stdcall HwInitialize(IN PVOID DeviceExtension) {
   return TRUE;
}


ULONG HwFindAdapter(
        IN PVOID DeviceExtension,
        IN PVOID HwContext,
        IN PVOID BusInformation,
        IN PCHAR ArgumentString,
        IN OUT PORT_CONFIGURATION_INFORMATION* ConfigInfo,
        OUT PBOOLEAN Again
)
{
	DBGPRINT((DBGLEVEL "HwFindAdapter: SystemIoBusNumber=%d, MaximumTransferLength=%d, NumberOfPhysicalBreaks=%d,\nNumberOfAccessRanges=%d, InitiatorBusId[0]=%d, ScatterGather=%d, MapBuffers=%d, BufferAccessScsiPortControlled=%d\n",
		ConfigInfo->SystemIoBusNumber, ConfigInfo->MaximumTransferLength, ConfigInfo->NumberOfPhysicalBreaks, ConfigInfo->NumberOfAccessRanges, ConfigInfo->InitiatorBusId[0], ConfigInfo->ScatterGather, ConfigInfo->MapBuffers, ConfigInfo->BufferAccessScsiPortControlled));

	*Again = FALSE;

	ConfigInfo->NumberOfBuses = 1;
	ConfigInfo->MaximumNumberOfTargets = 32;

	const unsigned offsetof_MaximumNumberOfLogicalUnits = (char*)&ConfigInfo->MaximumNumberOfLogicalUnits - (char*)ConfigInfo;
	if (ConfigInfo->Length > offsetof_MaximumNumberOfLogicalUnits)
		ConfigInfo->MaximumNumberOfLogicalUnits = 1;

	return SP_RETURN_FOUND;
}


SCSI_ADAPTER_CONTROL_STATUS HwAdapterControl(
    IN PVOID DeviceExtension,
    IN SCSI_ADAPTER_CONTROL_TYPE ControlType,
    IN PVOID Parameters
)
{
   DBGPRINT((DBGLEVEL "AdapterControl(%d)\n", ControlType));
	switch (ControlType) {
		case ScsiQuerySupportedControlTypes:
		{
			SCSI_SUPPORTED_CONTROL_TYPE_LIST* ssctl = (SCSI_SUPPORTED_CONTROL_TYPE_LIST*)Parameters;
			ZeroMemory(ssctl->SupportedTypeList, ssctl->MaxControlType);
			if (ScsiQuerySupportedControlTypes < ssctl->MaxControlType)
				ssctl->SupportedTypeList[ScsiQuerySupportedControlTypes] = TRUE;
			if (ScsiStopAdapter < ssctl->MaxControlType)
				ssctl->SupportedTypeList[ScsiStopAdapter] = TRUE;
			if (ScsiRestartAdapter < ssctl->MaxControlType)
				ssctl->SupportedTypeList[ScsiRestartAdapter] = TRUE;
		}
		// fall thru
		case ScsiStopAdapter:
		case ScsiRestartAdapter:
			return ScsiAdapterControlSuccess;
		default:
			return ScsiAdapterControlUnsuccessful;
	}
}


BOOLEAN DVDProxy::ResetBus(ULONG PathId) {
   DBGPRINT((DBGLEVEL "ResetBus(%d)\n", PathId));
   ScsiPortCompleteRequest(this, (UCHAR)PathId, (UCHAR)SP_UNTAGGED, (UCHAR)SP_UNTAGGED, (UCHAR)SRB_STATUS_BUS_RESET);
   queue.Clear();
   srb_listen = NULL;
   ScsiPortNotification(BusChangeDetected, this, 0);
   return TRUE;
}


// the standard says that for a single-byte transfer length 0 means 256
inline ULONG ScsiByteToInt(unsigned byte) { return byte ? byte : 256; }


void DVDProxy::Complete(SCSI_REQUEST_BLOCK* completed_srb) {
   if (completed_srb->SenseInfoBufferLength >= 14) {
      DBGPRINT((DBGLEVEL "Srb %x complete with status %02x:%02x:%02x:%02x:%02x\n",
         completed_srb, completed_srb->SrbStatus, completed_srb->ScsiStatus,
         ((unsigned char*)completed_srb->SenseInfoBuffer)[2],
         ((unsigned char*)completed_srb->SenseInfoBuffer)[12],
         ((unsigned char*)completed_srb->SenseInfoBuffer)[13]));
   } else {
      DBGPRINT((DBGLEVEL "Srb %x complete with status %02x:%02x\n",
         completed_srb, completed_srb->SrbStatus, completed_srb->ScsiStatus));
   }
   ScsiPortNotification(RequestComplete, this, completed_srb);
}

void DVDProxy::CompleteWithScsiSense(SCSI_REQUEST_BLOCK* completed_srb, unsigned scsi_sense) {
   completed_srb->SrbStatus = SRB_STATUS_ERROR | SRB_STATUS_AUTOSENSE_VALID;
   completed_srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
   SenseData sense = default_sense;
   sense.sense_key = (scsi_sense >> 16) & 15;
   sense.asc = (scsi_sense >> 8) & 255;
   sense.ascq = scsi_sense & 255;
   unsigned sense_len = min(18U, completed_srb->SenseInfoBufferLength);
   if (sense_len > 0) {
      ScsiPortMoveMemory(completed_srb->SenseInfoBuffer, const_cast<SenseData*>(&sense), sense_len);
      completed_srb->SenseInfoBufferLength = sense_len;
   }
   completed_srb->DataTransferLength = 0;
   Complete(completed_srb);
}


void DVDProxy::CompleteWithData(SCSI_REQUEST_BLOCK* Srb, const unsigned char* data, unsigned len, unsigned alloc) {
   unsigned transfer_length = min(len, alloc);
   if (transfer_length > Srb->DataTransferLength) transfer_length = Srb->DataTransferLength;
   ScsiPortMoveMemory(Srb->DataBuffer, (void*)data, transfer_length);
   Srb->DataTransferLength = transfer_length;
   CompleteWithScsiSuccess(Srb);
}


static inline void CopyCDB(void* dst, void* src) {
   ((unsigned*)dst)[0] = ((unsigned*)src)[0];
   ((unsigned*)dst)[1] = ((unsigned*)src)[1];
   ((unsigned*)dst)[2] = ((unsigned*)src)[2];
   ((unsigned*)dst)[3] = ((unsigned*)src)[3];
};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -