📄 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 "dvdproxy2k.h"
#include "../include/dvdsynth-scsi.h"
#define min(a,b) ((a)<(b)?(a):(b))
// The reason USER_LISTEN_TIMEOUT is set so small is that despite
// my best efforts I cannot get the ScsiPort driver to acknowledge
// the MultipleRequestPerLu or TaggedQueuing flags. As a result,
// the dummy device cannot respond to an INQUIRY command until the
// current listener (if any) has timed out.
//static const unsigned TICK_LENGTH = 1000000; // in usec
//static const UCHAR USER_LISTEN_TIMEOUT = 1; // in ticks
//static const UCHAR USER_RESPONSE_TIMEOUT = 2; // in ticks
#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
class SRBQueue {
private:
enum { SRB_QUEUE_SIZE = 16 }; // should be a power of 2
SCSI_REQUEST_BLOCK* q[SRB_QUEUE_SIZE];
ULONG retire, dispatch, incoming; // retire <= dispatch <= incoming
public:
void Clear() { retire = dispatch = incoming; }
// void CompleteAll(DVDProxy* bus);
bool AddIncomingSRB(SCSI_REQUEST_BLOCK* srb) {
if (ULONG(incoming-retire) >= ULONG(SRB_QUEUE_SIZE)) {
return false;
} else {
q[(incoming++)%SRB_QUEUE_SIZE] = srb;
return true;
}
}
SCSI_REQUEST_BLOCK* GetDispatchSRB(ULONG* index) {
while (dispatch != incoming && q[dispatch%SRB_QUEUE_SIZE] == NULL)
++dispatch;
if (dispatch == incoming) {
return 0;
} else {
*index = dispatch;
return q[(dispatch++)%SRB_QUEUE_SIZE];
}
}
void UngetDispatchSRB() { // dangerous unless called immediately after GetDispatchSRB
--dispatch;
}
SCSI_REQUEST_BLOCK* RemoveRetireeByIndex(ULONG index) {
if (ULONG(index-retire) >= ULONG(dispatch-retire)) {
return 0;
} else {
SCSI_REQUEST_BLOCK* result = q[index%SRB_QUEUE_SIZE];
q[index%SRB_QUEUE_SIZE] = NULL;
while (retire != dispatch && q[retire%SRB_QUEUE_SIZE] == NULL)
++retire;
return result;
}
}
bool RemoveSRB(SCSI_REQUEST_BLOCK* srb) {
for (ULONG i = retire; i != incoming; ++i) {
if (q[i%SRB_QUEUE_SIZE] == srb) {
q[i%SRB_QUEUE_SIZE] = NULL;
while (dispatch != incoming && q[dispatch%SRB_QUEUE_SIZE] == NULL)
++dispatch;
while (retire != dispatch && q[retire%SRB_QUEUE_SIZE] == NULL)
++retire;
return true;
}
}
return false;
}
#ifdef DBGLEVEL
char* DebugFmtString(char* p, const char* s) {
while (*s)
*p++ = *s++;
return p;
}
void DebugPrintQueue() {
char buf[20 + SRB_QUEUE_SIZE*10];
char* p = buf;
p = DebugFmtString(p, "queue = ");
for (ULONG i = retire; i != incoming; ++i) {
if (i == dispatch)
p = DebugFmtString(p, "| ");
p = DebugFmtHex(p, (unsigned)q[i%SRB_QUEUE_SIZE], 8);
*p++ = ' ';
}
if (incoming == dispatch)
*p++ = '|';
*p = 0;
DBGPRINT((DBGLEVEL buf));
}
#endif
};
static const SenseData default_sense = { 0x70, 0, 0, {0,0,0,0}, 10, {0,0,0,0}, 0, 0, 0, {0,0,0} };
struct DVDProxy {
SRBQueue queue;
SCSI_REQUEST_BLOCK* srb_listen; // user-mode app's listening SRB
bool user_attached;
UCHAR user_listen_countdown; // after this many ticks, fail the srb_listen SRB
UCHAR user_response_countdown; // after this many ticks, assume the user app has died
unsigned tick_length;
InquiryData inquiry_data[31];
void Complete(SCSI_REQUEST_BLOCK* completed_srb);
void CompleteWithScsiSense(SCSI_REQUEST_BLOCK* completed_srb, unsigned scsi_sense);
void CompleteWithScsiSuccess(SCSI_REQUEST_BLOCK* completed_srb) {
completed_srb->SrbStatus = SRB_STATUS_SUCCESS;
completed_srb->ScsiStatus = SCSISTAT_GOOD;
Complete(completed_srb);
}
void CompleteWithSrbStatus(SCSI_REQUEST_BLOCK* completed_srb, UCHAR srb_status) {
completed_srb->SrbStatus = srb_status;
completed_srb->ScsiStatus = SCSISTAT_GOOD;
Complete(completed_srb);
}
static bool ConstructUserModeRequest(SCSI_REQUEST_BLOCK* src, ULONG sequence_number, SCSI_REQUEST_BLOCK* dst);
static void UnpackUserModeResponse(SCSI_REQUEST_BLOCK* src, SCSI_REQUEST_BLOCK* dst);
void CompleteWithData(SCSI_REQUEST_BLOCK* Srb, const unsigned char* data, unsigned len, unsigned alloc);
void HandleSkeletonDevice(SCSI_REQUEST_BLOCK* Srb);
void HandleDummyDevice(SCSI_REQUEST_BLOCK* Srb);
void QueueSRB(SCSI_REQUEST_BLOCK* Srb);
BOOLEAN ResetBus(ULONG PathId);
BOOLEAN StartIo(SCSI_REQUEST_BLOCK* Srb);
VOID ScsiTimer();
};
/*
inline void SRBQueue::CompleteAll(DVDProxy* bus) {
dispatch = incoming;
while (retire != dispatch) {
SCSI_REQUEST_BLOCK* srb = q[retire%SRB_QUEUE_SIZE]
if (srb)
bus->HandleSkeletonDevice(srb);
++retire;
}
}
*/
// timer proc is needed iff user_attached is true.
// if user_attached goes to false, immediately fail all SRBs in the queue.
VOID ZeroMemory(IN PVOID Buffer, IN ULONG Count) {
for (ULONG i=0; i<Count; ++i)
((UCHAR*)Buffer)[i] = 0;
}
VOID __stdcall HwScsiTimer(IN PVOID DeviceExtension) {
((DVDProxy*)DeviceExtension)->ScsiTimer();
}
BOOLEAN __stdcall HwStartIo(IN PVOID DeviceExtension, IN PSCSI_REQUEST_BLOCK Srb) {
return ((DVDProxy*)DeviceExtension)->StartIo(Srb);
}
BOOLEAN __stdcall HwResetBus(PVOID DeviceExtension, ULONG PathId) {
return ((DVDProxy*)DeviceExtension)->ResetBus(PathId);
}
BOOLEAN __stdcall HwInitialize(IN PVOID DeviceExtension) {
return TRUE;
}
ULONG 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,\nNumberOfAccessRanges=%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));
*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 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;
ZeroMemory(ssctl->SupportedTypeList, 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;
}
}
BOOLEAN DVDProxy::ResetBus(ULONG PathId) {
DBGPRINT((DBGLEVEL "ResetBus(%d)\n", PathId));
ScsiPortCompleteRequest(this, (UCHAR)PathId, (UCHAR)SP_UNTAGGED, (UCHAR)SP_UNTAGGED, (UCHAR)SRB_STATUS_BUS_RESET);
queue.Clear();
srb_listen = NULL;
ScsiPortNotification(BusChangeDetected, this, 0);
return TRUE;
}
// the standard says that for a single-byte transfer length 0 means 256
inline ULONG ScsiByteToInt(unsigned byte) { return byte ? byte : 256; }
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);
}
void DVDProxy::CompleteWithScsiSense(SCSI_REQUEST_BLOCK* completed_srb, unsigned scsi_sense) {
completed_srb->SrbStatus = SRB_STATUS_ERROR | SRB_STATUS_AUTOSENSE_VALID;
completed_srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
SenseData sense = default_sense;
sense.sense_key = (scsi_sense >> 16) & 15;
sense.asc = (scsi_sense >> 8) & 255;
sense.ascq = scsi_sense & 255;
unsigned sense_len = min(18U, completed_srb->SenseInfoBufferLength);
if (sense_len > 0) {
ScsiPortMoveMemory(completed_srb->SenseInfoBuffer, const_cast<SenseData*>(&sense), sense_len);
completed_srb->SenseInfoBufferLength = sense_len;
}
completed_srb->DataTransferLength = 0;
Complete(completed_srb);
}
void DVDProxy::CompleteWithData(SCSI_REQUEST_BLOCK* Srb, const unsigned char* data, unsigned len, unsigned alloc) {
unsigned transfer_length = min(len, alloc);
if (transfer_length > Srb->DataTransferLength) transfer_length = Srb->DataTransferLength;
ScsiPortMoveMemory(Srb->DataBuffer, (void*)data, transfer_length);
Srb->DataTransferLength = transfer_length;
CompleteWithScsiSuccess(Srb);
}
static inline void CopyCDB(void* dst, void* src) {
((unsigned*)dst)[0] = ((unsigned*)src)[0];
((unsigned*)dst)[1] = ((unsigned*)src)[1];
((unsigned*)dst)[2] = ((unsigned*)src)[2];
((unsigned*)dst)[3] = ((unsigned*)src)[3];
};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -