📄 miniport.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>.
***********************************************************************/
#define WIN32_LEAN_AND_MEAN
#define STRICT
#include <windows.h>
#include "miniport.h"
#include "aspi.h"
#include "../dvdproxy95/dvdproxy95.h"
#include "../include/dvdsynth-plugin.h"
#include "SharedPool.h"
/*******************************************************************\
\*******************************************************************/
#include <stdio.h>
extern DvsBasePluginCallbacks* g_callbacks;
/*******************************************************************\
\*******************************************************************/
// ASPI is only used to send DVDPROXY_CMD_THUNK_CALL requests to the
// kernel driver. I wish I could avoid this, but there doesn't seem to
// be any way to contact a SCSI miniport driver from ring 3 except by
// sending SCSI commands.
namespace ASPI {
const SenseData default_sense = { 0x70, 0, 0, {0,0,0,0}, 10, {0,0,0,0}, 0, 0, 0, {0,0,0} };
DWORD (*SendASPI32Command)(LPSRB) = 0;
int dvdsynth_ha_id = -1;
enum { dvdsynth_targ_id = 0 };
scsi_result_t SendSCSICommand(int ha, int targ, int lun, const unsigned char* cdb, int cdblen, unsigned char* buffer, unsigned long* pbuflen, int inout, SenseData* sense) {
// Creating a new event for each call seems wasteful, but I'd
// rather that this function be thread-safe.
HANDLE hCompleted = CreateEvent(NULL, TRUE, FALSE, NULL);
SRB_ExecSCSICmd srb;
memset(&srb, 0, sizeof(srb));
srb.SRB_Cmd = SC_EXEC_SCSI_CMD;
srb.SRB_HaId = ha;
srb.SRB_Flags = SRB_EVENT_NOTIFY + SRB_ENABLE_RESIDUAL_COUNT + (inout==1 ? SRB_DIR_IN : inout==2 ? SRB_DIR_OUT : SRB_DIR_SCSI);
srb.SRB_Target = targ;
srb.SRB_Lun = lun;
srb.SRB_BufLen = *pbuflen;
srb.SRB_BufPointer = buffer;
srb.SRB_SenseLen = 16;
srb.SRB_CDBLen = cdblen;
memcpy(srb.CDBByte, cdb, cdblen);
srb.SRB_PostProc = hCompleted;
if (SS_PENDING == SendASPI32Command(&srb)) {
WaitForSingleObject(hCompleted, INFINITE);
}
CloseHandle(hCompleted);
*pbuflen -= srb.SRB_BufLen;
*sense = default_sense;
memcpy(sense, srb.SenseArea, srb.SRB_SenseLen);
return MAKE_SCSIRESULT(srb.SRB_Status, srb.SRB_TargStat, sense->sense_key, sense->asc, sense->ascq);
}
int GetHATargetMax(int ha) {
SRB_HAInquiry srb;
memset(&srb, 0, sizeof(srb));
srb.SRB_Cmd = SC_HA_INQUIRY;
srb.SRB_HaId = ha;
SendASPI32Command(&srb);
if (srb.SRB_Status != SS_COMP) {
return 0;
} else {
return max(8, srb.HA_Unique[3]);
}
}
int Init() {
// Initialize ASPI
HMODULE hASPI = LoadLibrary("wnaspi32.dll");
if (!hASPI)
return -1;
DWORD (*GetASPI32SupportInfo)();
GetASPI32SupportInfo = (DWORD (*)()) GetProcAddress(hASPI, "GetASPI32SupportInfo");
SendASPI32Command = (DWORD (*)(LPSRB)) GetProcAddress(hASPI, "SendASPI32Command");
if (GetASPI32SupportInfo == 0 || SendASPI32Command == 0)
return -2;
DWORD rtn = GetASPI32SupportInfo();
if ((rtn & 0xFF00) != (SS_COMP * 256) || (rtn & 0xFF) == 0)
return -3;
int num_host_adapters = rtn & 0xFF;
// Scan for the DVDSynth miniport by comparing INQUIRY data
static const unsigned char cdb_inquiry[6] = { 0x12, 0, 0, 0, 36, 0 };
for (int i=0; i<num_host_adapters; ++i) {
unsigned char buf[36];
memset(buf, 0xEE, 36);
unsigned long buflen = 36;
SenseData sense;
if (SendSCSICommand(i, dvdsynth_targ_id, 0, cdb_inquiry, 6, buf, &buflen, 1, &sense) == SCSIRESULT_SUCCESS
&& memcmp(buf, dvdproxy_device_inquiry_data, 36) == 0)
{
dvdsynth_ha_id = i;
return 0;
}
}
MessageBox(NULL, "DVDProxy device not found.", "DVDSynth", MB_OK);
return -4;
}
inline scsi_result_t SendMiniportCommand(const unsigned char* cdb, const void* buf, unsigned long buflen, int inout, SenseData* sense) {
return ASPI::SendSCSICommand(dvdsynth_ha_id, dvdsynth_targ_id, 0, (BYTE*)cdb, 12, (BYTE*)buf, &buflen, inout, sense);
}
/*
unsigned char ASPIStatusToSRBStatus(unsigned char aspi_srb_status, unsigned char aspi_ha_status) {
if (aspi_srb_status == SS_COMP) {
// note: can get SS_COMP with HASTAT_DO_DU (data overrun/underrun) -- handle specially?
return SRB_STATUS_SUCCESS;
} else if (aspi_srb_status == SS_ERR) {
if (aspi_ha_status == 0)
return SRB_STATUS_ERROR;
else if (aspi_ha_status == 0x11)
return SRB_STATUS_SELECTION_TIMEOUT;
else
return aspi_ha_status;
} else if (aspi_srb_status == SS_ABORTED || aspi_srb_status == SS_ABORT_FAIL) {
return aspi_srb_status;
} else if (aspi_srb_status >= SS_INVALID_CMD && aspi_srb_status <= SS_NO_DEVICE) {
return aspi_srb_status - SS_INVALID_CMD + SRB_STATUS_INVALID_REQUEST;
} else if (aspi_srb_status == SS_INVALID_SRB) {
// SS_INVALID_SRB is returned for several different errors.
// I'm assuming no one checks these codes too closely...
return SRB_STATUS_INVALID_TARGET_ID;
} else {
return SRB_STATUS_BAD_FUNCTION; // ???
}
}
*/
}
/*******************************************************************\
\*******************************************************************/
namespace Miniport {
void Hello() {
static const CDB_Dvdproxy cdb = { DVDPROXY_SCSIOP, DVDPROXY_VERSION, DVDPROXY_CMD_HELLO };
SenseData sense;
scsi_result_t result = ASPI::SendMiniportCommand((const unsigned char*)&cdb, "", 0, 1, &sense);
}
void* DriverCall(void* module, const char* entry_name, const char* types, ...) {
bool numerical_export = (unsigned(entry_name) < 65536);
const char* printable_entry_name = entry_name;
char printable_entry_name_buf[16];
if (numerical_export) {
g_callbacks->Sprint(printable_entry_name_buf, 16, "#%1", "d", entry_name);
printable_entry_name = printable_entry_name_buf;
}
enum { max_args=16 };
int num_args = strspn(types, "ips&<>E");
if (types[num_args] != 0) {
char buf[256];
g_callbacks->Sprint(buf, 256, "\"%1\" had an argument of an unrecognized type (\"%2\").", "sc", entry_name, types[num_args]);
MessageBox(NULL, buf, "DVDSynth", MB_OK);
}
if (num_args > max_args) {
char buf[256];
g_callbacks->Sprint(buf, 256, "DriverCall invoked with %1 arguments (max %2)", "dd", num_args, max_args);
MessageBox(NULL, buf, "DVDSynth", MB_OK);
return 0;
}
// get args
void* args[max_args];
void* original_args[max_args];
{
va_list val;
va_start(val, types);
for (int i=0; i<num_args; ++i) {
// I'm assuming void* and int take the same amount of stack space...
original_args[i] = args[i] = va_arg(val, void*);
// also check for invalid buffer length while I'm at it
if ((types[i] == '<' && i == 0)
|| (types[i] == '<' && types[i-1] != 'i' && types[i-1] != '&')
|| (types[i] == '>' && types[i+1] != 'i' && types[i+1] != '&'))
{
char buf[256];
g_callbacks->Sprint(buf, 256, "DriverCall type '<' or '>' does not point to integer argument (in call of \"%1\" with args \"%2\")",
0, printable_entry_name, types);
MessageBox(NULL, buf, "DVDSynth", MB_OK);
return 0;
}
}
va_end(val);
}
// determine shared memory space needed
bool need_pool = !numerical_export;
unsigned object_sizes[max_args];
for (int i=0; i<num_args; ++i) {
unsigned object_size = 0;
// Don't translate pointers in the range 0-65535; they probably
// don't point to anything. And don't translate pointers with the
// high bit set; either they don't point to anything or they
// already point to a buffer in the system area.
if (signed(args[i]) >= 65536) {
switch (types[i]) {
case '&':
object_size = 4;
break;
case '<':
object_size = (types[i-1] == '&') ? *(unsigned*)args[i-1] : (unsigned)args[i-1];
break;
case '>':
object_size = (types[i+1] == '&') ? *(unsigned*)args[i+1] : (unsigned)args[i+1];
break;
case 's':
object_size = lstrlen((const char*)args[i]) + 1;
break;
}
}
object_sizes[i] = object_size;
if (object_size > 0) {
need_pool = true;
}
}
// NB: the SharedPool_* functions sometimes call DriverCall recursively.
// But none of these calls requires the use of buffer_pool, so infinite
// recursion is avoided by creating/using buffer_pool only when it's needed.
static void* buffer_pool;
if (need_pool) {
if (buffer_pool == 0) {
buffer_pool = SharedPool_Create(true);
}
if (!numerical_export) {
int len = lstrlen(entry_name)+1;
char* buf = (char*)SharedPool_Alloc(buffer_pool, len);
memcpy(buf, entry_name, len);
entry_name = buf;
}
for (int i=0; i<num_args; ++i) {
if (object_sizes[i]) {
args[i] = SharedPool_Alloc(buffer_pool, object_sizes[i]);
memcpy(args[i], original_args[i], object_sizes[i]);
}
}
}
// set up the CDB
CDB_Thunk_Call cdb;
cdb.opcode = DVDPROXY_SCSIOP;
cdb.version = DVDPROXY_VERSION;
cdb.cmd = DVDPROXY_CMD_THUNK_CALL;
cdb.reserved = 0;
cdb.hmodule = module;
cdb.export_name = entry_name;
// send the SCSI command
SenseData sense;
scsi_result_t result = ASPI::SendMiniportCommand((const unsigned char*)&cdb, args, num_args*sizeof(void*), 2, &sense);
// update buffers
if (need_pool) {
// copy the data back
for (int i=0; i<num_args; ++i) {
if (object_sizes[i] && types[i] != 's') {
memcpy(original_args[i], args[i], object_sizes[i]);
}
}
SharedPool_Clear(buffer_pool);
}
// unpack return value
if (result == MAKE_SCSIRESULT_ERROR(DVDPROXY_ERROR_THUNK_CALL_SUCCEEDED)) {
return *(void**)&sense.command_specific;
} else if (result == MAKE_SCSIRESULT_ERROR(DVDPROXY_ERROR_EXPORT_NOT_FOUND)) {
char buf[256];
g_callbacks->Sprint(buf, 256, "Call to driver-exported function \"%1\" failed: the function was not found.", 0, entry_name);
MessageBox(NULL, buf, "DVDSynth", MB_OK);
return 0;
} else {
char buf[256];
g_callbacks->Sprint(buf, 256, "Call to driver-exported function \"%1\" failed with error code %2.\n\nPlease report this as a DVDSynth bug.", "sX", entry_name, result);
MessageBox(NULL, buf, "DVDSynth", MB_OK);
return 0;
}
}
int Init() {
int aspi_init_result = ASPI::Init();
if (aspi_init_result != 0)
return aspi_init_result;
Hello();
DriverCall(miniport_handle, (char*)mpdfunc_Init, "p", g_callbacks->GetTaskbarHWND());
return 0;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -