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

📄 dvdproxy.cpp

📁 DVD工具dvdsynth的源码
💻 CPP
字号:
/***********************************************************************
 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 "../dvdproxy95/dvdproxy95.h"
#include "../include/dvdsynth-device.h"

#include "ddk.h"
#include "internal.h"


#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


/*******************************************************************\
\*******************************************************************/


#ifdef WORKER_THREAD

void __cdecl ThreadCreateFailure(void* self) {
	DBGPRINT((DBGLEVEL "****** _VWIN32_CreateRing0Thread FAILED ******\n"));
}


void DVDProxy::InitWorkerThread() {
   // _VWIN32_CreateRing0Thread can't be called until late in the boot
   // cycle, so this function isn't called until the user-mode DVDSynth
   // app requests it.
   semaphore_handle = Create_Semaphore(0);
   _VWIN32_CreateRing0Thread(WorkerThreadProc, this, ThreadCreateFailure);
}

#endif


void MemSet(void* dst, int val, unsigned long count) {
   unsigned char* p = (unsigned char*)dst;
   if (val == 0 && count >= 4) {
      do {
         *(unsigned*)p = 0;
         p += 4;
         count -= 4;
      } while (count >= 4);
   }
   while (count > 0) {
      *p++ = val;
      --count;
   }
}


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

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

BOOLEAN __stdcall HwResetBus(IN PVOID DeviceExtension, ULONG PathId) {
   return TRUE;
}

int number_of_HwFindAdapter_calls;

ULONG __stdcall 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, NumberOfAccessRanges=%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));

	++number_of_HwFindAdapter_calls;

	*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 __stdcall 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;
			MemSet(ssctl->SupportedTypeList, 0, 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;
	}
}


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, 0);
}


DEVNODE GetDevnodeParent(DEVNODE dn) {
   if (CR_SUCCESS == CONFIGMG_Get_Parent(&dn, dn, 0)) {
      return dn;
   } else {
      return 0;
   }
}


void Test() {
/*
   for (DCB* dcb = GetNextDCB(0); dcb; dcb = GetNextDCB(dcb)) {
      if (dcb->DCB_cmn.DCB_device_flags & DCB_DEV_PHYSICAL) {
         if ((dcb->DCB_bus_type == DCB_BUS_SCSI) || (dcb->DCB_bus_type == DCB_BUS_ESDI && (dcb->DCB_cmn.DCB_device_flags2 & DCB_DEV2_ATAPI_DEVICE))) {
            DebugPrintf("\ndevnode=%x parent=%x addr=%d:%d:%d letter=%c\n",
               dcb->DCB_dev_node, CONFIGMG_Get_Parent(dcb->DCB_dev_node),
               dcb->DCB_scsi_hba, dcb->DCB_scsi_target_id, dcb->DCB_scsi_lun,
               dcb->DCB_cmn.DCB_drive_lttr_equiv ? 'A'+dcb->DCB_cmn.DCB_drive_lttr_equiv : '_');
            DebugPrintf(" type=%d cAssoc=%d bus_number=%d max_xfer_len=%x max_sense_data_len=%d\n",
               dcb->DCB_cmn.DCB_device_type, dcb->DCB_cmn.DCB_cAssoc,
               dcb->DCB_bus_number, dcb->DCB_max_xfer_len, dcb->DCB_max_sense_data_len);
            DebugPrintf(" inquiry='%s'\n", dcb->DCB_vendor_id);
         }
      }
   }
*/
}


inline bool PageCommit(void* p, unsigned npages, int locked) {
   unsigned page = unsigned(p) >> 12;
   return !!_PageCommit(page, npages,
      locked ? PD_FIXEDZERO : PD_ZEROINIT, 0,
      locked ? PC_USER+PC_WRITEABLE+PC_FIXED : PC_USER+PC_WRITEABLE);
}


/*******************************************************************\
\*******************************************************************/


inline unsigned GetScsiWord1(const unsigned char* p) {
   return p[0] + 256 * !p[0];
}


/*******************************************************************\
\*******************************************************************/


#ifdef WORKER_THREAD

void __stdcall SrbCleanup(void* _self) {
   DVDProxy* self = (DVDProxy*)_self;
   bool pending_srb_left = false;
   for (int n = 0; n < DVDProxy::max_outstanding_srbs; ++n) {
      if (self->srbs[n] == 0) continue;
      if (self->srb_complete[n]) {
         DBGPRINT((DBGLEVEL "SrbCleanup: completing %X\n", self->srbs[n]));
         self->Complete(self->srbs[n]);
         self->srbs[n] = 0;
      } else {
         pending_srb_left = true;
      }
   }
   ScsiPortNotification(RequestTimerCall, self, SrbCleanup, pending_srb_left * 1000);
}

#endif


/*******************************************************************\
\*******************************************************************/


BOOLEAN DVDProxy::StartIo(SCSI_REQUEST_BLOCK* Srb) {

   ScsiPortNotification(NextRequest, this, NULL);

   unsigned path = Srb->PathId, targ = Srb->TargetId, lun = Srb->Lun;

   switch (Srb->Function) {

   case SRB_FUNCTION_EXECUTE_SCSI:

      DBGPRINT((DBGLEVEL "SCSI command: Srb=%x Addr=%d:%d:%d Data=%d Cdb=%s\n",
         Srb, path, targ, lun, Srb->DataTransferLength, CDBString(Srb->Cdb, Srb->CdbLength)));

      MemSet(Srb->SenseInfoBuffer, 0, Srb->SenseInfoBufferLength);

      if (path != 0) {
         DBGPRINT((DBGLEVEL "pathid != 0\n"));
         CompleteWithSrbStatus(Srb, SRB_STATUS_INVALID_PATH_ID);
      } else if (lun != 0) {
         DBGPRINT((DBGLEVEL "lun != 0\n"));
         CompleteWithSrbStatus(Srb, SRB_STATUS_INVALID_LUN);
      } else if (targ == 0 && Srb->Cdb[0] == 0x12) {
         // FIXME: validate other bytes?
         unsigned len = min(GetScsiWord1(&Srb->Cdb[4]), min(Srb->DataTransferLength, 36));
         ScsiPortMoveMemory(Srb->DataBuffer, dvdproxy_device_inquiry_data, len);
         Srb->DataTransferLength = len;
         CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS);
      } else if (targ == 0 && Srb->Cdb[0] == 0xEE && Srb->Cdb[1] == DVDPROXY_VERSION && Srb->Cdb[2] == DVDPROXY_CMD_HELLO) {
         DBGPRINT((DBGLEVEL "Got HELLO\n"));
         handlers[0] = &dummy_device;
#ifdef WORKER_THREAD
         if (!WorkerThreadInitialized()) {
            InitWorkerThread();
         }
#endif
         CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS);
      } else if (targ < 32 && handlers[targ] != 0) {
#ifdef WORKER_THREAD
         for (int n = 0; n < max_outstanding_srbs; ++n) {
            if (srbs[n] == 0) {
               DBGPRINT((DBGLEVEL "StartIO: queueing %X\n", Srb));
               srbs[n] = Srb;
               srb_complete[n] = false;
               Signal_Semaphore_No_Switch(semaphore_handle);
               break;
            }
         }
         if (n >= max_outstanding_srbs) {
            CompleteWithSrbStatus(Srb, SRB_STATUS_BUSY);
         }
#else
         WorkProc(this, Srb);
         Complete(Srb);
#endif
      } else {
         // FIXME: should return INVALID FIELD IN CDB instead for target 0
         CompleteWithSrbStatus(Srb, SRB_STATUS_INVALID_TARGET_ID);
      }
      break;

   case SRB_FUNCTION_ABORT_COMMAND:
      DBGPRINT((DBGLEVEL "SRB_FUNCTION_ABORT_COMMAND\n"));
//      CompleteWithSrbStatus(Srb->NextSrb, SRB_STATUS_ABORTED);
//      CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS);
      CompleteWithSrbStatus(Srb, SRB_STATUS_ABORT_FAILED);
      break;

   case SRB_FUNCTION_TERMINATE_IO:
      DBGPRINT((DBGLEVEL "SRB_FUNCTION_TERMINATE_IO\n"));
      CompleteWithSrbStatus(Srb, SRB_STATUS_MESSAGE_REJECTED);
      break;

   case SRB_FUNCTION_RESET_BUS:
      DBGPRINT((DBGLEVEL "SRB_FUNCTION_RESET_BUS\n"));
      CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS);
      break;

   default:
      DBGPRINT((DBGLEVEL "Srb->Function=%d\n", Srb->Function));
      CompleteWithSrbStatus(Srb, SRB_STATUS_INVALID_REQUEST);
      break;
   }

#ifdef WORKER_THREAD
   SrbCleanup(this);
#endif

   return TRUE;
}

/*
unsigned mythread;


__declspec(naked)
unsigned __stdcall VWIN32_CreateRing0Thread(void(__stdcall*func)(void*), void* context, unsigned stack) {
   __asm {
      push  ebx
      mov   ecx,[esp+12]
      mov   edx,[esp+8]
      mov   ebx,[esp+4]
      xor   esi,esi
      int   0x20
      _emit 19
      _emit 0
      _emit 42
      _emit 0
      test  eax,eax
      jz    error
      xchg  eax,edx
error:
      pop   ebx
      retn  12
   }
}
*/


/*******************************************************************\
\*******************************************************************/


extern "C" ULONG __stdcall DriverEntry(IN PVOID DriverObject, IN PVOID Argument2) {

   HW_INITIALIZATION_DATA hwInitializationData;
   MemSet(&hwInitializationData, 0, sizeof(hwInitializationData));

   hwInitializationData.HwInitializationDataSize = 80;/*sizeof(hwInitializationData)*/

   hwInitializationData.AdapterInterfaceType = Isa;

   hwInitializationData.HwInitialize = HwInitialize;
   hwInitializationData.HwStartIo = HwStartIo;
   hwInitializationData.HwFindAdapter = HwFindAdapter;
   hwInitializationData.HwResetBus = HwResetBus;

   hwInitializationData.DeviceExtensionSize = sizeof(DVDProxy);

//   hwInitializationData.NumberOfAccessRanges = 1;

   hwInitializationData.MapBuffers = TRUE;
   hwInitializationData.AutoRequestSense = TRUE;
//   hwInitializationData.MultipleRequestPerLu = TRUE;
//   hwInitializationData.TaggedQueuing = TRUE;

   hwInitializationData.HwAdapterControl = HwAdapterControl;

   // On some versions of Windows, ScsiPortInitialize fails if the
   // struct length is greater than 76. But there's no portable way to
   // check the version of Windows from a miniport driver -- and
   // Microsoft doesn't document the return value of ScsiPortInitialize,
   // not even to distinguish between success and failure. My hacky
   // solution: make HwFindAdapter tell me whether it was called inside
   // ScsiPortInitialize, and if not try again with the smaller struct.
   number_of_HwFindAdapter_calls = 0;
   ULONG rtn = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, 0);
   if (number_of_HwFindAdapter_calls > 0) {
      return rtn;
   } else {
      hwInitializationData.HwInitializationDataSize = 76;
      return ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, 0);
   }
}

⌨️ 快捷键说明

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