📄 worker_thread.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"
// Returns 0 on success, MS-DOS error code on error.
int __stdcall OpenFileRO(unsigned* phandle, const char* pathname) {
const unsigned flags = RING0_OPENCREATE_FLAG_MM|RING0_OPENCREATE_FLAG_ALLOW_EXISTING|RING0_OPENCREATE_FLAG_NO_INT_24|RING0_OPENCREATE_FLAG_EXTENDED_SIZE|RING0_OPENCREATE_FLAG_DENYNONE|RING0_OPENCREATE_FLAG_READONLY;
return Ring0_OpenCreate(flags, pathname, phandle);
}
//80013040
// Returns 0 on success, MS-DOS error code on error.
int __stdcall CreateOrOpenFileRW(unsigned* phandle, const char* pathname, int creation_disposition) {
const unsigned flags = (RING0_OPENCREATE_FLAG_MM|RING0_OPENCREATE_FLAG_NO_INT_24|RING0_OPENCREATE_FLAG_EXTENDED_SIZE|RING0_OPENCREATE_FLAG_DENYWRITE|RING0_OPENCREATE_FLAG_READWRITE) + RING0_OPENCREATE_FLAG_ALLOW_EXISTING*(creation_disposition<=1) + RING0_OPENCREATE_FLAG_ALLOW_CREATE*(creation_disposition>=1);
return Ring0_OpenCreate(flags, pathname, phandle);
}
// Returns bytes read on success, negative of MS-DOS error code on error
int ReadFile(dvs_file_handle handle, unsigned offset, unsigned offset_high, unsigned count, void* buf) {
if (offset_high != 0)
return 0;
return Ring0_ReadWrite(R0_READFILE, (unsigned)handle, offset, count, buf);
}
// Returns bytes written on success, negative of MS-DOS error code on error
int WriteFile(dvs_file_handle handle, unsigned offset, unsigned offset_high, unsigned count, void* buf) {
if (offset_high != 0)
return 0;
return Ring0_ReadWrite(R0_WRITEFILE, (unsigned)handle, offset, count, buf);
}
// Returns file size on success, 0 on error
unsigned GetFileSize(dvs_file_handle handle, unsigned* size_high) {
if (size_high)
*size_high = 0;
return Ring0_SizeClose(R0_GETFILESIZE, (unsigned)handle);
}
void CloseFile(dvs_file_handle handle) {
Ring0_SizeClose(R0_CLOSEFILE, (unsigned)handle);
}
__declspec(naked)
void* __stdcall MakeThunkCall(void* func, void* args, unsigned arg_bytes) {
__asm {
push ebp
mov ebp,esp
mov edx,[ebp+12] /* args */
mov ecx,[ebp+16] /* arg_bytes */
shr ecx,2
jz make_call
copy_loop:
dec ecx
push [edx+ecx*4]
jnz copy_loop
make_call:
call [ebp+8] /* func */
mov esp,ebp
pop ebp
ret 12
}
}
/*******************************************************************\
\*******************************************************************/
struct AsyncCallRecord {
void (__stdcall*f)(void*, int);
void* p;
int i;
};
struct AsyncCallQueue {
unsigned head,tail;
enum { queue_size = 16 };
AsyncCallRecord q[queue_size];
unsigned records_lost;
void Reset() {
tail = head;
}
void Put(void (__stdcall*f)(void*, int), void* p, int i) {
unsigned h = head;
// this is safe because VxDs are cooperatively multitasked
if (h > tail && q[(h-1u)%queue_size].f == f
&& q[(h-1u)%queue_size].p == p && q[(h-1u)%queue_size].i == i)
{
return;
}
if (h - tail < queue_size) {
q[h%queue_size].f = f;
q[h%queue_size].p = p;
q[h%queue_size].i = i;
head = h+1;
} else {
++records_lost;
}
}
};
AsyncCallQueue async_call_queue;
ULONG notification_hwnd;
#define WM_APP_APC 0xA5FC
__declspec(naked)
void AsyncPostMessage() {
__asm {
pushad
mov esi,offset callback
VXD_CALL(1,14) // Schedule_VM_Event
popad
retn
callback:
mov eax,[notification_hwnd]
test eax,eax
jz exit
push 0
push 0
push offset async_call_queue
push 0
push WM_APP_APC
push eax
VXD_CALL(23,6) // _SHELL_PostMessage
add esp,24
exit:
retn
}
}
void __stdcall AsyncUserModeCall(void (__stdcall*f)(void*, int), void* p, int i) {
async_call_queue.Put(f,p,i);
AsyncPostMessage();
}
void __cdecl SetAsyncNotificationWindow(ULONG hwnd) {
notification_hwnd = hwnd;
async_call_queue.Reset();
}
__declspec(naked)
void AsyncSetEvent(unsigned event) {
__asm {
pushad
VXD_CALL(1,3) // Get_Sys_VM_Handle (in ebx)
mov esi,offset callback
mov edx,[esp+36]
VXD_CALL(1,17) // Call_VM_Event
popad
retn
callback:
mov eax,edx
VXD_JUMP(42,14) // _VWIN32_SetWin32Event
}
}
__declspec(naked)
void __stdcall CloseEvent(unsigned event) {
__asm {
pushad
mov eax,[esp+36]
VXD_CALL(42,20) // _VWIN32_CloseVxDHandle
popad
retn 4
}
}
/*******************************************************************\
\*******************************************************************/
void __cdecl MemCpy(void* dst, const void* src, unsigned long count) {
ScsiPortMoveMemory(dst, src, count);
}
/*******************************************************************\
\*******************************************************************/
void CallDriverInitFunc(dvs_driver_handle hmodule) {
static DvsDockingBayKernelGlobal callbacks = {
MemSet,
MemCpy,
DebugPrintf,
0, //AsyncUserModeCall,
ReadFile,
WriteFile,
AsyncSetEvent,
};
void* function = _PELDR_GetProcAddress(hmodule, "DvdsynthDriverInit");
if (function) {
((void(__cdecl*)(DvsDockingBayKernelGlobal*))function)(&callbacks);
}
}
//__declspec(naked)
scsi_result_t ScsiDispatch(DvsDeviceKernel* device, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) {
return device->ScsiCommand(device, cdb, cdblen, buffer, pbuflen, inout, sense);
}
void __cdecl Init(ULONG notification_hwnd) {
// SetAsyncNotificationWindow(notification_hwnd);
// IPSA::IPSAInit();
}
/*******************************************************************\
\*******************************************************************/
DVDProxy* g_dvdproxy;
void __cdecl SetHandler(int scsi_id, DvsDeviceKernel* handler) {
// DebugPrintf("SetHandler(%d,%X)\n", scsi_id, handler);
// if (handler==(DvsDeviceKernel*)-1) {
// extern DvsDeviceKernel dummy_device;
// handler = &dummy_device;
// }
if (scsi_id > 0 && scsi_id < 32) {
g_dvdproxy->handlers[scsi_id] = handler;
}
}
/*******************************************************************\
\*******************************************************************/
// This must match the mpdfunc_* enum in miniport.h!
void* mpdfunc_pointers[] = {
Init,
_PELDR_LoadModule,
_PELDR_FreeModule,
CallDriverInitFunc,
SetHandler,
ScsiDispatch,
_PageReserve,
_PageCommit,
_PageFree,
CONFIGMG_Locate_DevNode,
CONFIGMG_Get_Child,
CONFIGMG_Get_Sibling,
CONFIGMG_Query_Remove_SubTree,
CONFIGMG_Remove_SubTree,
CONFIGMG_Reenumerate_DevNode,
OpenFileRO,
CreateOrOpenFileRW,
ReadFile,
WriteFile,
GetFileSize,
CloseFile,
CloseEvent,
};
static const void* miniport_handle = (void*)~0;
/*******************************************************************\
\*******************************************************************/
scsi_result_t __cdecl HandleDummyDevice(DvsDeviceKernel*, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) {
if (cdb[0] == DVDPROXY_SCSIOP) {
if (cdb[1] != DVDPROXY_VERSION) {
return MAKE_SCSIRESULT_ERROR(DVDPROXY_ERROR_WRONG_VERSION);
} else if (cdb[2] != DVDPROXY_CMD_THUNK_CALL) {
return MAKE_SCSIRESULT_ERROR(DVDPROXY_ERROR_INVALID_COMMAND);
} else {
CDB_Thunk_Call* call_cdb = (CDB_Thunk_Call*)cdb;
void* function = 0;
unsigned index = (unsigned)call_cdb->export_name;
if (call_cdb->hmodule == hmodule_miniport) {
DebugPrintf("looking up miniport function #%d\n", index);
if (index < sizeof(mpdfunc_pointers)/sizeof(mpdfunc_pointers[0])) {
function = mpdfunc_pointers[index];
}
} else {
if (index<65536) {
DebugPrintf("looking up export #%d in module %X\n", call_cdb->export_name, call_cdb->hmodule);
} else {
DebugPrintf("looking up export %X in module %X\n", call_cdb->export_name, call_cdb->hmodule);
DebugPrintf(" (%s)\n", call_cdb->export_name);
}
function = _PELDR_GetProcAddress(call_cdb->hmodule, call_cdb->export_name);
}
if (function == 0) {
return MAKE_SCSIRESULT_ERROR(DVDPROXY_ERROR_EXPORT_NOT_FOUND);
} else {
*(void**)sense->command_specific = MakeThunkCall(function, buffer, *pbuflen);
return MAKE_SCSIRESULT_ERROR(DVDPROXY_ERROR_THUNK_CALL_SUCCEEDED);
}
}
} else {
return MAKE_SCSIRESULT_ERROR(0x52000); // INVALID COMMAND OPERATION CODE
}
}
DvsDeviceKernel dummy_device = { HandleDummyDevice };
/*******************************************************************\
\*******************************************************************/
scsi_result_t HandleSRB(SCSI_REQUEST_BLOCK* Srb, DvsDeviceKernel* handler, SenseData* sense) {
if (handler == 0) {
return MAKE_SCSIRESULT(SRB_STATUS_INVALID_TARGET_ID, 0, 0, 0, 0);
}
const unsigned char* cdb = Srb->Cdb;
int cdblen = Srb->CdbLength;
// check CDB length
static const BYTE expected_cdb_lengths[] = { 6, 10, 10, 0, 0, 12, 0, 0 };
BYTE expected_cdb_length = expected_cdb_lengths[cdb[0]>>5];
if (expected_cdb_length ? (expected_cdb_length != cdblen) : (cdblen < 6 || cdblen > 16)) {
return MAKE_SCSIRESULT_ERROR(0x51A00); // PARAMETER LIST LENGTH ERROR
}
// convert READ(6) and READ(12) commands into READ(10)
unsigned char cdbbuf[10];
if (cdb[0] == SCSIOP_READ6) {
cdbbuf[0] = SCSIOP_READ;
cdbbuf[1] = cdb[1] & 0xE0;
cdbbuf[2] = 0;
cdbbuf[3] = cdb[1] & 0x1F;
cdbbuf[4] = cdb[2];
cdbbuf[5] = cdb[3]; // logical block address
cdbbuf[6] = 0;
cdbbuf[7] = !cdb[4];
cdbbuf[8] = cdb[4]; // transfer length
cdbbuf[9] = cdb[5]; // control
cdb = cdbbuf;
cdblen = 10;
} else if (cdb[0] == SCSIOP_READ12) {
if (cdb[6] == 0 && cdb[7] == 0) {
cdbbuf[0] = SCSIOP_READ;
cdbbuf[1] = cdb[1];
cdbbuf[2] = cdb[2];
cdbbuf[3] = cdb[3];
cdbbuf[4] = cdb[4];
cdbbuf[5] = cdb[5]; // logical block address
cdbbuf[6] = 0;
cdbbuf[7] = cdb[8];
cdbbuf[8] = cdb[9]; // transfer length
cdbbuf[9] = cdb[11]; // control
cdb = cdbbuf;
cdblen = 10;
} else {
// The caller wants to transfer 65536 or
// more *blocks* in one call -- unlikely!
return SCSIRESULT_INVALID_CDB;
}
}
// dispatch
DBGPRINT((DBGLEVEL "Dispatching (handler %X)\n", handler));
return handler->ScsiCommand(handler, cdb, cdblen, (unsigned char*)Srb->DataBuffer, &Srb->DataTransferLength, (Srb->SrbFlags & 0xC0) >> 6, sense);
}
/*******************************************************************\
\*******************************************************************/
void SetScsiSense(SCSI_REQUEST_BLOCK* completed_srb, SenseData* sense, scsi_result_t result) {
completed_srb->SrbStatus = SCSIRESULT_SRBSTAT(result);
completed_srb->ScsiStatus = SCSIRESULT_TARGSTAT(result);
// Windows doesn't seem to like it if you set
// SRB_STATUS_AUTOSENSE_VALID on success.
if (result != SCSIRESULT_SUCCESS
&& (SCSIRESULT_SRBSTAT(result) == SRB_STATUS_SUCCESS
|| SCSIRESULT_SRBSTAT(result) == SRB_STATUS_ERROR))
{
unsigned sense_len = min(18U, completed_srb->SenseInfoBufferLength);
if (sense_len > 0) {
sense->sense_key = (sense->sense_key & 0xF0) | SCSIRESULT_SENSEKEY(result);
sense->asc = SCSIRESULT_ASC(result);
sense->ascq = SCSIRESULT_ASCQ(result);
ScsiPortMoveMemory(completed_srb->SenseInfoBuffer, sense, sense_len);
completed_srb->SenseInfoBufferLength = sense_len;
completed_srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
}
}
}
/*******************************************************************\
\*******************************************************************/
static const SenseData default_sense = { 0x70, 0, 0, {0,0,0,0}, 10, {0,0,0,0}, 0, 0, 0, {0,0,0} };
#ifdef WORKER_THREAD
void __cdecl WorkerThreadProc(void* _self) {
DBGPRINT((DBGLEVEL "WorkerThreadProc start"));
DVDProxy* self = (DVDProxy*)_self;
g_dvdproxy = self;
for (;;) {
Wait_Semaphore(self->semaphore_handle, BLOCK_SVC_INTS|BLOCK_THREAD_IDLE);
for (int n = 0; n < DVDProxy::max_outstanding_srbs; ++n) {
if (self->srbs[n] != 0 && !self->srb_complete[n]) {
SCSI_REQUEST_BLOCK* srb = self->srbs[n];
DBGPRINT((DBGLEVEL "Worker thread: got SRB %X\n", srb));
SenseData sense = default_sense;
scsi_result_t result = HandleSRB(srb, self->handlers[srb->TargetId], &sense);
DBGPRINT((DBGLEVEL "Worker thread: finished %X with result %X\n", srb, result));
SetScsiSense(srb, &sense, result);
self->srb_complete[n] = true;
//WakeMiniportThread();
}
}
}
}
#else
void __cdecl WorkProc(DVDProxy* self, SCSI_REQUEST_BLOCK* srb) {
g_dvdproxy = self;
SenseData sense = default_sense;
scsi_result_t result = HandleSRB(srb, self->handlers[srb->TargetId], &sense);
SetScsiSense(srb, &sense, result);
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -