/*********************************************************************** 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 . ***********************************************************************/ #include "scsi-miniport.h" #include "../dvdproxy95/dvdproxy95.h" #include "../include/dvdsynth-device.h" #include "ddk.h" #include "internal.h" #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; i0) *p++ = ' '; p = DebugFmtHex(p, cdb[i], 2); } *p = 0; return buf; } #endif /*******************************************************************\ \*******************************************************************/ #ifdef WORKER_THREAD void __cdecl ThreadCreateFailure(void* self) { DBGPRINT((DBGLEVEL "****** _VWIN32_CreateRing0Thread FAILED ******\n")); } void DVDProxy::InitWorkerThread() { // _VWIN32_CreateRing0Thread can't be called until late in the boot // cycle, so this function isn't called until the user-mode DVDSynth // app requests it. semaphore_handle = Create_Semaphore(0); _VWIN32_CreateRing0Thread(WorkerThreadProc, this, ThreadCreateFailure); } #endif void MemSet(void* dst, int val, unsigned long count) { unsigned char* p = (unsigned char*)dst; if (val == 0 && count >= 4) { do { *(unsigned*)p = 0; p += 4; count -= 4; } while (count >= 4); } while (count > 0) { *p++ = val; --count; } } BOOLEAN __stdcall HwStartIo(IN PVOID DeviceExtension, IN PSCSI_REQUEST_BLOCK Srb) { return ((DVDProxy*)DeviceExtension)->StartIo(Srb); } BOOLEAN __stdcall HwInitialize(IN PVOID DeviceExtension) { return TRUE; } BOOLEAN __stdcall HwResetBus(IN PVOID DeviceExtension, ULONG PathId) { return TRUE; } int number_of_HwFindAdapter_calls; ULONG __stdcall 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, NumberOfAccessRanges=%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)); ++number_of_HwFindAdapter_calls; *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 __stdcall 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; MemSet(ssctl->SupportedTypeList, 0, 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; } } 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, 0); } DEVNODE GetDevnodeParent(DEVNODE dn) { if (CR_SUCCESS == CONFIGMG_Get_Parent(&dn, dn, 0)) { return dn; } else { return 0; } } void Test() { /* for (DCB* dcb = GetNextDCB(0); dcb; dcb = GetNextDCB(dcb)) { if (dcb->DCB_cmn.DCB_device_flags & DCB_DEV_PHYSICAL) { if ((dcb->DCB_bus_type == DCB_BUS_SCSI) || (dcb->DCB_bus_type == DCB_BUS_ESDI && (dcb->DCB_cmn.DCB_device_flags2 & DCB_DEV2_ATAPI_DEVICE))) { DebugPrintf("\ndevnode=%x parent=%x addr=%d:%d:%d letter=%c\n", dcb->DCB_dev_node, CONFIGMG_Get_Parent(dcb->DCB_dev_node), dcb->DCB_scsi_hba, dcb->DCB_scsi_target_id, dcb->DCB_scsi_lun, dcb->DCB_cmn.DCB_drive_lttr_equiv ? 'A'+dcb->DCB_cmn.DCB_drive_lttr_equiv : '_'); DebugPrintf(" type=%d cAssoc=%d bus_number=%d max_xfer_len=%x max_sense_data_len=%d\n", dcb->DCB_cmn.DCB_device_type, dcb->DCB_cmn.DCB_cAssoc, dcb->DCB_bus_number, dcb->DCB_max_xfer_len, dcb->DCB_max_sense_data_len); DebugPrintf(" inquiry='%s'\n", dcb->DCB_vendor_id); } } } */ } inline bool PageCommit(void* p, unsigned npages, int locked) { unsigned page = unsigned(p) >> 12; return !!_PageCommit(page, npages, locked ? PD_FIXEDZERO : PD_ZEROINIT, 0, locked ? PC_USER+PC_WRITEABLE+PC_FIXED : PC_USER+PC_WRITEABLE); } /*******************************************************************\ \*******************************************************************/ inline unsigned GetScsiWord1(const unsigned char* p) { return p[0] + 256 * !p[0]; } /*******************************************************************\ \*******************************************************************/ #ifdef WORKER_THREAD void __stdcall SrbCleanup(void* _self) { DVDProxy* self = (DVDProxy*)_self; bool pending_srb_left = false; for (int n = 0; n < DVDProxy::max_outstanding_srbs; ++n) { if (self->srbs[n] == 0) continue; if (self->srb_complete[n]) { DBGPRINT((DBGLEVEL "SrbCleanup: completing %X\n", self->srbs[n])); self->Complete(self->srbs[n]); self->srbs[n] = 0; } else { pending_srb_left = true; } } ScsiPortNotification(RequestTimerCall, self, SrbCleanup, pending_srb_left * 1000); } #endif /*******************************************************************\ \*******************************************************************/ BOOLEAN DVDProxy::StartIo(SCSI_REQUEST_BLOCK* Srb) { ScsiPortNotification(NextRequest, this, NULL); unsigned 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))); MemSet(Srb->SenseInfoBuffer, 0, 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 && Srb->Cdb[0] == 0x12) { // FIXME: validate other bytes? unsigned len = min(GetScsiWord1(&Srb->Cdb[4]), min(Srb->DataTransferLength, 36)); ScsiPortMoveMemory(Srb->DataBuffer, dvdproxy_device_inquiry_data, len); Srb->DataTransferLength = len; CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS); } else if (targ == 0 && Srb->Cdb[0] == 0xEE && Srb->Cdb[1] == DVDPROXY_VERSION && Srb->Cdb[2] == DVDPROXY_CMD_HELLO) { DBGPRINT((DBGLEVEL "Got HELLO\n")); handlers[0] = &dummy_device; #ifdef WORKER_THREAD if (!WorkerThreadInitialized()) { InitWorkerThread(); } #endif CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS); } else if (targ < 32 && handlers[targ] != 0) { #ifdef WORKER_THREAD for (int n = 0; n < max_outstanding_srbs; ++n) { if (srbs[n] == 0) { DBGPRINT((DBGLEVEL "StartIO: queueing %X\n", Srb)); srbs[n] = Srb; srb_complete[n] = false; Signal_Semaphore_No_Switch(semaphore_handle); break; } } if (n >= max_outstanding_srbs) { CompleteWithSrbStatus(Srb, SRB_STATUS_BUSY); } #else WorkProc(this, Srb); Complete(Srb); #endif } else { // FIXME: should return INVALID FIELD IN CDB instead for target 0 CompleteWithSrbStatus(Srb, SRB_STATUS_INVALID_TARGET_ID); } break; case SRB_FUNCTION_ABORT_COMMAND: DBGPRINT((DBGLEVEL "SRB_FUNCTION_ABORT_COMMAND\n")); // CompleteWithSrbStatus(Srb->NextSrb, SRB_STATUS_ABORTED); // CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS); CompleteWithSrbStatus(Srb, SRB_STATUS_ABORT_FAILED); break; case SRB_FUNCTION_TERMINATE_IO: DBGPRINT((DBGLEVEL "SRB_FUNCTION_TERMINATE_IO\n")); CompleteWithSrbStatus(Srb, SRB_STATUS_MESSAGE_REJECTED); break; case SRB_FUNCTION_RESET_BUS: DBGPRINT((DBGLEVEL "SRB_FUNCTION_RESET_BUS\n")); CompleteWithSrbStatus(Srb, SRB_STATUS_SUCCESS); break; default: DBGPRINT((DBGLEVEL "Srb->Function=%d\n", Srb->Function)); CompleteWithSrbStatus(Srb, SRB_STATUS_INVALID_REQUEST); break; } #ifdef WORKER_THREAD SrbCleanup(this); #endif return TRUE; } /* unsigned mythread; __declspec(naked) unsigned __stdcall VWIN32_CreateRing0Thread(void(__stdcall*func)(void*), void* context, unsigned stack) { __asm { push ebx mov ecx,[esp+12] mov edx,[esp+8] mov ebx,[esp+4] xor esi,esi int 0x20 _emit 19 _emit 0 _emit 42 _emit 0 test eax,eax jz error xchg eax,edx error: pop ebx retn 12 } } */ /*******************************************************************\ \*******************************************************************/ extern "C" ULONG __stdcall DriverEntry(IN PVOID DriverObject, IN PVOID Argument2) { HW_INITIALIZATION_DATA hwInitializationData; MemSet(&hwInitializationData, 0, 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; // On some versions of Windows, ScsiPortInitialize fails if the // struct length is greater than 76. But there's no portable way to // check the version of Windows from a miniport driver -- and // Microsoft doesn't document the return value of ScsiPortInitialize, // not even to distinguish between success and failure. My hacky // solution: make HwFindAdapter tell me whether it was called inside // ScsiPortInitialize, and if not try again with the smaller struct. number_of_HwFindAdapter_calls = 0; ULONG rtn = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, 0); if (number_of_HwFindAdapter_calls > 0) { return rtn; } else { hwInitializationData.HwInitializationDataSize = 76; return ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, 0); } }