📄 dvdproxy.cpp
字号:
bool DVDProxy::ConstructUserModeRequest(SCSI_REQUEST_BLOCK* src, ULONG sequence_number, SCSI_REQUEST_BLOCK* dst) {
DBGPRINT((DBGLEVEL "sending request for %x(%d)\n", src, sequence_number));
if (dst->SenseInfoBuffer) {
for (int i=0; i<dst->SenseInfoBufferLength; ++i)
((char*)dst->SenseInfoBuffer)[i] = i+0xAB;
}
unsigned data_bytes = (src->SrbFlags & SRB_FLAGS_DATA_OUT) ? src->DataTransferLength : 0;
unsigned total_bytes = data_bytes + sizeof(UserModeRequest);
if (total_bytes > dst->DataTransferLength) {
// DBGPRINT((DBGLEVEL "DVDPROXY_ERROR_NEED_MORE_DATA_SPACE: had %d, need %d (cdb=%s)\n", dst->DataTransferLength, total_bytes, CDBString(src->Cdb, src->CdbLength)));
return false;
}
UserModeRequest* req = (UserModeRequest*)dst->DataBuffer;
req->sequence_number = sequence_number;
req->target = src->TargetId;
req->data_transfer_direction = UCHAR((src->SrbFlags >> 6) & 3);
req->cdb_length = src->CdbLength;
req->reserved = 0;
req->data_transfer_length = src->DataTransferLength;
CopyCDB(req->cdb, src->Cdb);
UCHAR* data = (UCHAR*)req + sizeof(UserModeRequest);
if (data_bytes)
ScsiPortMoveMemory(data, src->DataBuffer, data_bytes);
dst->DataTransferLength = total_bytes;
dst->SenseInfoBufferLength = 0;
return true;
}
void DVDProxy::UnpackUserModeResponse(SCSI_REQUEST_BLOCK* src, SCSI_REQUEST_BLOCK* dst) {
CDB_RetireSRB* resp = (CDB_RetireSRB*)src->Cdb;
dst->SrbStatus = resp->srb_status;
dst->ScsiStatus = resp->scsi_status;
dst->DataTransferLength = min(dst->DataTransferLength, src->DataTransferLength - 18*resp->sense_included);
if (dst->DataTransferLength)
ScsiPortMoveMemory(dst->DataBuffer, src->DataBuffer, dst->DataTransferLength);
dst->SenseInfoBufferLength = min(dst->SenseInfoBufferLength, 18*resp->sense_included);
if (dst->SenseInfoBufferLength) {
SenseData* sense_data = (SenseData*)((char*)src->DataBuffer + src->DataTransferLength - 18);
ScsiPortMoveMemory(dst->SenseInfoBuffer, sense_data, dst->SenseInfoBufferLength);
}
}
VOID DVDProxy::ScsiTimer() {
if (!user_attached)
return;
DBGPRINT((DBGLEVEL "listen=%d response=%d\n", user_listen_countdown, user_response_countdown));
if (srb_listen) {
if (user_listen_countdown > 0) {
--user_listen_countdown;
}
if (user_listen_countdown == 0) {
CompleteWithScsiSense(srb_listen, DVDPROXY_ERROR_LISTEN_TIMEOUT);
srb_listen = NULL;
}
} else {
if (user_response_countdown > 0) {
--user_response_countdown;
}
if (user_response_countdown == 0) {
user_attached = false;
ResetBus(0);
}
}
ScsiPortNotification(RequestTimerCall, this, HwScsiTimer, tick_length);
}
void DVDProxy::HandleSkeletonDevice(SCSI_REQUEST_BLOCK* Srb) {
InquiryData* data = &inquiry_data[Srb->TargetId-1];
if (data->inquiry_length == 0) {
CompleteWithSrbStatus(Srb, SRB_STATUS_INVALID_TARGET_ID);
} else if (Srb->Cdb[0] == SCSIOP_INQUIRY) {
if (Srb->Cdb[1] == 0 && Srb->Cdb[2] == 0) {
CompleteWithData(Srb, data->inquiry, data->inquiry_length, ScsiByteToInt(Srb->Cdb[4]));
} else {
CompleteWithScsiSense(Srb, 0x52400); // INVALID FIELD IN CDB
}
} else if (Srb->Cdb[0] == SCSIOP_MODE_SENSE10 && data->modepage_2a_length != 0) {
if (Srb->Cdb[1] == 0 && Srb->Cdb[2] == 0x2A) {
// Mode page 0x2A (CD/DVD Capabilities and Mechanical Status)
CompleteWithData(Srb, data->modepage_2a, data->modepage_2a_length, Srb->Cdb[7]*256+Srb->Cdb[8]);
} else {
CompleteWithScsiSense(Srb, 0x52400); // INVALID FIELD IN CDB
}
} else {
CompleteWithScsiSense(Srb, 0x52000); // INVALID COMMAND OPERATION CODE
}
}
void DVDProxy::HandleDummyDevice(SCSI_REQUEST_BLOCK* Srb) {
// I have to simulate this device because the idiot ScsiPort wrapper
// won't pass SCSI commands to my driver unless I convince it there's
// a device on the bus.
if (Srb->Cdb[0] == SCSIOP_INQUIRY && Srb->Cdb[1] == 0 && Srb->Cdb[2] == 0) {
CompleteWithData(Srb, dvdproxy_device_inquiry_data, 36, ScsiByteToInt(Srb->Cdb[4]));
} else if (Srb->Cdb[0] == DVDPROXY_SCSIOP && Srb->Cdb[1] == DVDPROXY_VERSION) {
switch (Srb->Cdb[2]) {
case DVDPROXY_CMD_GET_SRB:
// DBGPRINT((DBGLEVEL "DVDPROXY_CMD_GET_SRB\n"));
if (srb_listen != NULL) {
DBGPRINT((DBGLEVEL "Someone is already listening (this should never happen)\n"));
CompleteWithScsiSense(srb_listen, DVDPROXY_ERROR_OLD_LISTENER);
srb_listen = NULL;
}
{
CDB_GetSRB* cdb = (CDB_GetSRB*)Srb->Cdb;
tick_length = 1000 * cdb->tick_length_in_ms;
user_listen_countdown = cdb->listen_timeout;
user_response_countdown = cdb->response_timeout;
if (!user_attached) {
user_attached = true;
ScsiPortNotification(RequestTimerCall, this, HwScsiTimer, tick_length);
}
ULONG seqnum;
SCSI_REQUEST_BLOCK* dispatch_srb = queue.GetDispatchSRB(&seqnum);
if (dispatch_srb) {
DBGPRINT((DBGLEVEL "Dispatching %x\n", dispatch_srb));
if (ConstructUserModeRequest(dispatch_srb, seqnum, Srb)) {
CompleteWithScsiSuccess(Srb);
} else {
DBGPRINT((DBGLEVEL "Dispatch failed: need more space\n", dispatch_srb));
queue.UngetDispatchSRB();
CompleteWithScsiSense(Srb, DVDPROXY_ERROR_NEED_MORE_DATA_SPACE);
}
} else {
DBGPRINT((DBGLEVEL "Putting request on hold\n"));
srb_listen = Srb;
}
}
break;
case DVDPROXY_CMD_KEEP_ALIVE:
DBGPRINT((DBGLEVEL "DVDPROXY_CMD_KEEP_ALIVE\n"));
user_response_countdown = ((CDB_KeepAlive*)Srb->Cdb)->ticks;
if (!user_attached) {
user_attached = true;
ScsiPortNotification(RequestTimerCall, this, HwScsiTimer, tick_length);
}
break;
case DVDPROXY_CMD_RETIRE_SRB:
// DBGPRINT((DBGLEVEL "DVDPROXY_CMD_RETIRE_SRB\n"));
{
ULONG seqnum = ((CDB_RetireSRB*)Srb->Cdb)->sequence_number;
SCSI_REQUEST_BLOCK* retire_srb = queue.RemoveRetireeByIndex(seqnum);
if (retire_srb) {
DBGPRINT((DBGLEVEL "Retiring %x\n", retire_srb));
UnpackUserModeResponse(Srb, retire_srb);
Complete(retire_srb);
} else {
DBGPRINT((DBGLEVEL "Attemped to retire an unqueued SRB (%d)\n", seqnum));
}
CompleteWithScsiSuccess(Srb);
}
break;
case DVDPROXY_CMD_BUS_CHANGE:
DBGPRINT((DBGLEVEL "DVDPROXY_CMD_BUS_CHANGE\n"));
CompleteWithScsiSuccess(Srb);
ScsiPortNotification(BusChangeDetected, this, 0);
break;
case DVDPROXY_CMD_SET_INQUIRY_DATA:
DBGPRINT((DBGLEVEL "DVDPROXY_CMD_SET_INQUIRY_DATA\n"));
ScsiPortMoveMemory(inquiry_data, Srb->DataBuffer, min(Srb->DataTransferLength, 31*sizeof(InquiryData)));
CompleteWithScsiSuccess(Srb);
break;
case DVDPROXY_CMD_DETACH:
DBGPRINT((DBGLEVEL "DVDPROXY_CMD_DETACH\n"));
if (srb_listen) {
CompleteWithScsiSense(srb_listen, DVDPROXY_ERROR_DETACH);
}
user_attached = false;
CompleteWithScsiSuccess(Srb);
break;
case DVDPROXY_CMD_GENERATE_ERROR:
DBGPRINT((DBGLEVEL "DVDPROXY_CMD_GENERATE_ERROR\n"));
CompleteWithSrbStatus(Srb, ((CDB_GenerateError*)Srb->Cdb)->error_code);
break;
default:
DBGPRINT((DBGLEVEL "invalid operation code (%d)\n", Srb->Cdb[2]));
CompleteWithScsiSense(Srb, DVDPROXY_ERROR_INVALID_COMMAND);
break;
}
} else {
CompleteWithScsiSense(Srb, 0x52000); // INVALID COMMAND OPERATION CODE
}
}
void DVDProxy::QueueSRB(SCSI_REQUEST_BLOCK* Srb) {
DBGPRINT((DBGLEVEL "Queueing %x\n", Srb));
if (!queue.AddIncomingSRB(Srb)) {
DBGPRINT((DBGLEVEL "Queue full!\n"));
CompleteWithSrbStatus(Srb, SRB_STATUS_BUSY);
return;
}
if (srb_listen) {
ULONG seqnum;
SCSI_REQUEST_BLOCK* dispatch_srb = queue.GetDispatchSRB(&seqnum);
// assert(dispatch_srb == Srb);
if (dispatch_srb) {
DBGPRINT((DBGLEVEL "Dispatching %x immediately\n", dispatch_srb));
if (ConstructUserModeRequest(dispatch_srb, seqnum, srb_listen)) {
CompleteWithScsiSuccess(srb_listen);
} else {
DBGPRINT((DBGLEVEL "Dispatch failed: need more space\n", dispatch_srb));
queue.UngetDispatchSRB();
CompleteWithScsiSense(srb_listen, DVDPROXY_ERROR_NEED_MORE_DATA_SPACE);
}
srb_listen = NULL;
} else {
DBGPRINT((DBGLEVEL "No SRB to dispatch! (bug)\n", Srb));
}
} else {
DBGPRINT((DBGLEVEL "No current listener\n", Srb));
}
}
BOOLEAN DVDProxy::StartIo(SCSI_REQUEST_BLOCK* Srb) {
#ifdef DBGLEVEL
queue.DebugPrintQueue();
#endif
unsigned char 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)));
ZeroMemory(Srb->SenseInfoBuffer, 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) {
HandleDummyDevice(Srb);
} else if (user_attached) {
QueueSRB(Srb);
} else {
HandleSkeletonDevice(Srb);
}
break;
case SRB_FUNCTION_IO_CONTROL:
case SRB_FUNCTION_ABORT_COMMAND:
case SRB_FUNCTION_TERMINATE_IO:
DBGPRINT((DBGLEVEL "SRB_FUNCTION_ABORT_COMMAND or SRB_FUNCTION_TERMINATE_IO\n"));
queue.RemoveSRB(Srb->NextSrb);
CompleteWithSrbStatus(Srb->NextSrb, SRB_STATUS_ABORTED);
CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS);
break;
case SRB_FUNCTION_RESET_BUS:
DBGPRINT((DBGLEVEL "SRB_FUNCTION_RESET_BUS\n"));
ResetBus(path);
CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS);
break;
/*
#ifdef DBGLEVEL
case SRB_FUNCTION_IO_CONTROL:
{
struct SRB_IO_CONTROL {
ULONG hdrlen;
UCHAR sig[8];
ULONG timeout;
ULONG ctlcode;
ULONG rtncode;
ULONG len;
};
SRB_IO_CONTROL* ioctl = (SRB_IO_CONTROL*)Srb->DataBuffer;
ioctl->timeout = 0;
DBGPRINT((DBGLEVEL "SRB_FUNCTION_IO_CONTROL: %s %x len=%d\n", ioctl->sig, ioctl->ctlcode, ioctl->len));
CompleteWithSrbStatus(Srb, SRB_STATUS_INVALID_REQUEST);
break;
}
#endif
*/
default:
DBGPRINT((DBGLEVEL "Srb->Function=%d\n", Srb->Function));
CompleteWithSrbStatus(Srb, SRB_STATUS_INVALID_REQUEST);
break;
}
// ScsiPortNotification(NextLuRequest, this, path, targ, lun);
ScsiPortNotification(NextRequest, this, NULL);
return TRUE;
}
extern "C" ULONG DriverEntry(IN PVOID DriverObject, IN PVOID Argument2) {
DBGPRINT((DBGLEVEL "DVDProxy DriverEntry\n"));
HW_INITIALIZATION_DATA hwInitializationData;
ZeroMemory(&hwInitializationData, 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;
return ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, 0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -