/*********************************************************************** 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 . ***********************************************************************/ // Under Win2K the DVDSynth "kernel" runs in user mode. It duplicates some // of the functionality of the Win95 kernel-mode code. #include "kernel.h" #include "miniport.h" #include "asm.h" #include "../dvdproxy2k/dvdproxy2k.h" #include "../include/dvdsynth-device.h" #include #include namespace KernelThread { void AsyncDispatch(unsigned char* buffer, scsi_result_t* result_storage, HANDLE event_done); } extern DvsBasePluginCallbacks* g_callbacks; // in VDevice.cpp void AsyncUserModeCall(void (*f)(void*, int), void* p, int i); // in VDevice.cpp /*******************************************************************\ \*******************************************************************/ class Semaphore { HANDLE s; public: Semaphore() { s = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); } void P() { WaitForSingleObject(s, INFINITE); } bool TimedP(unsigned timeout) { return WaitForSingleObject(s, timeout) == WAIT_OBJECT_0; } void V() { ReleaseSemaphore(s, 1, NULL); } }; class CriticalSection { CRITICAL_SECTION cs; public: CriticalSection() { InitializeCriticalSection(&cs); } ~CriticalSection() { DeleteCriticalSection(&cs); } void Acquire() { EnterCriticalSection(&cs); } void Release() { LeaveCriticalSection(&cs); } }; // clobbers "next" pointer in Msg class template class MessageQueue { CriticalSection mutex; Semaphore slots_used; Msg* head; Msg** tail; public: MessageQueue() { head = 0; tail = &head; } void Put(Msg* msg) { mutex.Acquire(); *tail = msg; tail = &msg->next; *tail = 0; mutex.Release(); slots_used.V(); } Msg* Get() { slots_used.P(); mutex.Acquire(); Msg* result = head; if (result) { head = result->next; if (head == 0) { tail = &head; } } mutex.Release(); return result; } }; /*******************************************************************\ \*******************************************************************/ #ifdef __GNUC__ #define ASM __asm__ ( #define ENDASM ); #define L(l) #l ":\n" #define EAX "%eax" #define EBX "%ebx" #define ECX "%ecx" #define EDX "%edx" #define ESI "%esi" #define EDI "%edi" #define EBP "%ebp" #define ESP "%esp" #define RI(r,i) #i "(" r ")" #define RR(r1,r2) "(" r1 "," r2 ")" #define RRI(r1,r2,i) #i "(" r1 "," r2 ")" #define RRS(r1,r2,s) "(" r1 "," r2 "," #s ")" #define RRSI(r1,r2,s,i) #i "(" r1 "," r2 "," #s ")" #define PUSH(r) "\tpush\t" r "\n" #define POP(r) "\tpop\t" r "\n" #define RET "\tret\n" #define RETP(n) "\tretn\t" #n "\n" #define MOV(d,s) "\tmovl\t" s "," d "\n" #define DEC(r) "\tdecl\t" r "\n" #define SHR(r,n) "\tshrl\t" r "," #n "\n" #define JZ(l) "\tjz\t" #l "\n" #define JNZ(l) "\tjnz\t" #l "\n" #define CALL(a) "\tcall\t" a "\n" #if 0 __declspec(naked) void* __cdecl MakeThunkCall(void* func, void* args, unsigned arg_bytes) { ASM PUSH(EBP) MOV(EBP,ESP) MOV(EDX,RI(EBP,12)) /* args */ MOV(ECX,RI(EBP,16)) /* arg_bytes */ SHR(ECX,2) JZ(make_call) L(copy_loop) DEC(ECX) PUSH(RRS(EDX,ECX,4)) JNZ(copy_loop) L(make_call) CALL(RI(EBP,8)) /* func */ MOV(ESP,EBP) POP(EBP) RET ENDASM } #endif #else __declspec(naked) void* __cdecl 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 } } #endif /*******************************************************************\ \*******************************************************************/ namespace KernelThread { DvsDeviceKernel* handler[num_bays]; struct KernelRequest { enum { dispatch, setHandler, callInitFunc, driverCall, quit } type; union { struct { unsigned char* buffer; scsi_result_t* result_storage; HANDLE event_done; } dispatch_args; struct { int target; DvsDeviceKernel* kernel_handler; } setHandler_args; struct { HMODULE hmodule; } callInitFunc_args; struct { void* func; void* args; int arg_bytes; void** return_value; HANDLE return_notify; } driverCall_args; }; KernelRequest* next; }; MessageQueue request_queue; void MemSet(void* dst, int val, unsigned long count) { memset(dst, val, count); } void MemCpy(void* dst, const void* src, unsigned long count) { memcpy(dst, src, count); } void DebugPrintf(const char* fmt, ...) { OutputDebugString("DvsDockingBayKernelGlobal::DebugPrintf\n"); } int ReadFile(dvs_file_handle file_handle, unsigned offset, unsigned offset_high, unsigned count, void* buf) { HANDLE h = HANDLE(file_handle); LONG oh = offset_high; ::SetFilePointer(h, offset, &oh, FILE_BEGIN); DWORD read = count; if (::ReadFile(h, buf, count, &read, NULL)) return read; else // FIXME: error reporting return 0; } int WriteFile(dvs_file_handle file_handle, unsigned offset, unsigned offset_high, unsigned count, void* buf) { HANDLE h = HANDLE(file_handle); LONG oh = offset_high; ::SetFilePointer(h, offset, &oh, FILE_BEGIN); DWORD written = count; if (::WriteFile(h, buf, count, &written, NULL)) return written; else // FIXME: error reporting return 0; } void SetEvent(unsigned event_handle) { ::SetEvent(HANDLE(event_handle)); } DvsDockingBayKernelGlobal kernel_callbacks = { MemSet, MemCpy, DebugPrintf, AsyncUserModeCall, ReadFile, WriteFile, SetEvent, }; scsi_result_t SendUserModeRequest(UserModeRequest* req, unsigned char* buffer, SenseData* sense) { // check CDB length static const BYTE expected_cdb_lengths[] = { 6, 10, 10, 0, 0, 12, 0, 0 }; BYTE expected_cdb_length = expected_cdb_lengths[req->cdb[0]>>5]; if (expected_cdb_length ? (expected_cdb_length > req->cdb_length) : (req->cdb_length < 6 || req->cdb_length > 16)) { return MAKE_SCSIRESULT_ERROR(0x51A00); // PARAMETER LIST LENGTH ERROR } // convert READ(6) and READ(12) commands into READ(10) if (req->cdb[0] == SCSIOP_READ6) { req->cdb_length = 10; req->cdb[9] = req->cdb[5]; // control req->cdb[8] = req->cdb[4]; // transfer length req->cdb[7] = !req->cdb[4]; req->cdb[6] = 0; req->cdb[5] = req->cdb[3]; // logical block address req->cdb[4] = req->cdb[2]; req->cdb[3] = req->cdb[1] & 0x1F; req->cdb[2] = 0; req->cdb[1] = req->cdb[1] & 0xE0; req->cdb[0] = SCSIOP_READ; } else if (req->cdb[0] == SCSIOP_READ12) { req->cdb_length = 10; if (req->cdb[6] == 0 && req->cdb[7] == 0) { req->cdb[7] = req->cdb[8]; req->cdb[8] = req->cdb[9]; req->cdb[9] = req->cdb[11]; req->cdb[0] = SCSIOP_READ; } else { // The caller wants to transfer 65536 or // more *blocks* in one call -- unlikely! return SCSIRESULT_INVALID_CDB; } } // dispatch DvsDeviceKernel* handlr = handler[req->target-1]; if (handlr) { scsi_result_t result = handlr->ScsiCommand(handlr, req->cdb, req->cdb_length, buffer, &req->data_transfer_length, req->data_transfer_direction, sense); return result; } else { return MAKE_SCSIRESULT(SRB_STATUS_INVALID_TARGET_ID,0,0,0,0); } } const SenseData default_sense = { 0x70, 0, 0, {0,0,0,0}, 10, {0,0,0,0}, 0, 0, 0, {0,0,0} }; scsi_result_t DoDispatch(unsigned char* buffer) { SenseData sense = default_sense; UserModeRequest* req = (UserModeRequest*)buffer; scsi_result_t result = SendUserModeRequest(req, buffer+sizeof(UserModeRequest), &sense); sense.sense_key = (sense.sense_key & 0xF0) | SCSIRESULT_SENSEKEY(result); sense.asc = SCSIRESULT_ASC(result); sense.ascq = SCSIRESULT_ASCQ(result); if (!(req->data_transfer_direction & 1)) { req->data_transfer_length = 0; } *(SenseData*)(buffer + sizeof(UserModeRequest) + req->data_transfer_length) = sense; return result; } unsigned __stdcall ThreadMain(void*) { for (;;) { KernelRequest* req = request_queue.Get(); switch (req->type) { case KernelRequest::quit: return 0; case KernelRequest::dispatch: *req->dispatch_args.result_storage = DoDispatch(req->dispatch_args.buffer); ::SetEvent(req->dispatch_args.event_done); break; case KernelRequest::setHandler: //assert(req->setHandler_args.target > 0 && req->setHandler_args.target <= num_bays); handler[req->setHandler_args.target-1] = req->setHandler_args.kernel_handler; ListenerThread::AsyncRescanBus(); break; case KernelRequest::callInitFunc: { typedef void __cdecl DvdsynthDriverInit_func(DvsDockingBayKernelGlobal*); DvdsynthDriverInit_func* init_func = (DvdsynthDriverInit_func*) GetProcAddress(req->callInitFunc_args.hmodule, "DvdsynthDriverInit"); if (init_func) { init_func(&kernel_callbacks); } } break; case KernelRequest::driverCall: *req->driverCall_args.return_value = MakeThunkCall(req->driverCall_args.func, req->driverCall_args.args, req->driverCall_args.arg_bytes); ::SetEvent(req->driverCall_args.return_notify); break; default: /*assert(0)*/; } delete req; } } HANDLE hthread; void Spawn() { unsigned id; hthread = (HANDLE)_beginthreadex(NULL, 0, ThreadMain, NULL, 0, &id); } void AsyncDispatch(unsigned char* buffer, scsi_result_t* result_storage, HANDLE event_done) { KernelRequest* req = new KernelRequest; req->type = KernelRequest::dispatch; req->dispatch_args.buffer = buffer; req->dispatch_args.result_storage = result_storage; req->dispatch_args.event_done = event_done; request_queue.Put(req); } void AsyncSetHandler(int target, DvsDeviceKernel* kernel_handler) { KernelRequest* req = new KernelRequest; req->type = KernelRequest::setHandler; req->setHandler_args.target = target; req->setHandler_args.kernel_handler = kernel_handler; request_queue.Put(req); } void AsyncCallInitFunc(HMODULE hmodule) { KernelRequest* req = new KernelRequest; req->type = KernelRequest::callInitFunc; req->callInitFunc_args.hmodule = hmodule; request_queue.Put(req); } void* _stdcall AsyncDriverCall(dvs_driver_handle handle, const char* exported_name, const char* types, ...) { void* func = GetProcAddress(HMODULE(handle), exported_name); if (func) { KernelRequest* req = new KernelRequest; req->type = KernelRequest::driverCall; req->driverCall_args.func = func; req->driverCall_args.args = (&types)+1; req->driverCall_args.arg_bytes = lstrlen(types)*sizeof(void*); void* result; req->driverCall_args.return_value = &result; static HANDLE hDone = CreateEvent(NULL, FALSE, FALSE, NULL); req->driverCall_args.return_notify = hDone; request_queue.Put(req); WaitForSingleObject(hDone, INFINITE); return result; } else { char buf[256]; g_callbacks->Sprint(buf, 256, "Call to driver-exported function \"%1\" failed: the function was not found.", 0, exported_name); MessageBox(NULL, buf, "DVDSynth", MB_OK); return 0; } } } /*******************************************************************\ \*******************************************************************/ namespace ListenerThread { HANDLE heventFinishedSRB = CreateEvent(NULL, FALSE, FALSE, NULL); HANDLE heventRescanBus = CreateEvent(NULL, FALSE, FALSE, NULL); unsigned char* buffer = new unsigned char[66606]; unsigned long buffer_size = 66606; unsigned __stdcall ThreadProc(void*) { for (;;) { scsi_result_t result = Miniport::GetSRB(buffer, buffer_size, 1000, 2, 5); if (result == SCSIRESULT_SUCCESS) { // send message UserModeRequest* req = (UserModeRequest*)buffer; unsigned required_buffer_size = sizeof(UserModeRequest) + req->data_transfer_length + 18 + 1024; if (buffer_size < required_buffer_size) { buffer_size = required_buffer_size; printf("Resizing buffer to %d\n", buffer_size); BYTE* new_buffer = new BYTE[buffer_size]; req = (UserModeRequest*)new_buffer; *req = *(UserModeRequest*)buffer; delete[] buffer; buffer = new_buffer; } scsi_result_t scsi_result; KernelThread::AsyncDispatch(buffer, &scsi_result, heventFinishedSRB); HANDLE events[] = { heventFinishedSRB, heventRescanBus }; for (;;) { DWORD obj = WaitForMultipleObjects(2, events, FALSE, 4000); if (obj == WAIT_OBJECT_0) { Miniport::RetireRequest(buffer, scsi_result); break; } else if (obj == WAIT_OBJECT_0+1) { Miniport::BusChange(); } else { Miniport::KeepAlive(5); } } } else { if (result == MAKE_SCSIRESULT_ERROR(DVDPROXY_ERROR_NEED_MORE_DATA_SPACE)) { delete[] buffer; buffer_size += 2048; buffer = new unsigned char[buffer_size]; } else if (result != MAKE_SCSIRESULT_ERROR(DVDPROXY_ERROR_LISTEN_TIMEOUT)) { printf("result = %x!\n", result); Sleep(200); Miniport::Init(); } if (WAIT_OBJECT_0 == WaitForSingleObject(heventRescanBus, 0)) { Miniport::BusChange(); } } } } void AsyncRescanBus() { SetEvent(heventRescanBus); } HANDLE hthread; void Spawn() { unsigned id; hthread = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, NULL, 0, &id); } void Join() { WaitForSingleObject(hthread, INFINITE); } }