📄 dvdproxy.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 + -