501 lines
11 KiB
C++
501 lines
11 KiB
C++
|
#include <stdio.h>
|
||
|
#include <stddef.h>
|
||
|
#include "NTScsi.h"
|
||
|
|
||
|
typedef struct {
|
||
|
BYTE ha;
|
||
|
BYTE tgt;
|
||
|
BYTE lun;
|
||
|
BYTE driveLetter;
|
||
|
BOOL bUsed;
|
||
|
HANDLE hDevice;
|
||
|
BYTE inqData[36];
|
||
|
} NTSCSIDRIVE;
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
BYTE numAdapters;
|
||
|
NTSCSIDRIVE drive[26];
|
||
|
} NTSCSIDRIVES;
|
||
|
|
||
|
void GetDriveInformation( BYTE i, NTSCSIDRIVE *pDrive );
|
||
|
|
||
|
static HANDLE GetFileHandle( BYTE i );
|
||
|
|
||
|
static BOOL bNtScsiAvailable = FALSE;
|
||
|
static NTSCSIDRIVES NtScsiDrives;
|
||
|
static BOOL bUseNtScsi = FALSE;
|
||
|
|
||
|
/*
|
||
|
* Initialization of SCSI Pass Through Interface code. Responsible for
|
||
|
* setting up the array of SCSI devices. This code will be a little
|
||
|
* different from the normal code -- it will query each drive letter from
|
||
|
* C: through Z: to see if it is a CD. When we identify a CD, we then
|
||
|
* send CDB with the INQUIRY command to it -- NT will automagically fill in
|
||
|
* the PathId, TargetId, and Lun for us.
|
||
|
*/
|
||
|
|
||
|
int NtScsiInit( void )
|
||
|
{
|
||
|
BYTE i;
|
||
|
wchar_t buf[4] = {0};
|
||
|
UINT uDriveType;
|
||
|
int retVal = 0;
|
||
|
|
||
|
if ( bNtScsiAvailable )
|
||
|
{
|
||
|
for( i = 2; i < 26; i++ ) if ( NtScsiDrives.drive[i].bUsed ) retVal++;
|
||
|
bUseNtScsi = (retVal > 0 );
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
memset( &NtScsiDrives, 0x00, sizeof(NtScsiDrives) );
|
||
|
|
||
|
for( i = 0; i < 26; i++ )
|
||
|
{
|
||
|
NtScsiDrives.drive[i].hDevice = INVALID_HANDLE_VALUE;
|
||
|
}
|
||
|
|
||
|
for( i = 2; i < 26; i++ )
|
||
|
{
|
||
|
wsprintf( buf, L"%c:\\", (wchar_t)('A'+i) );
|
||
|
uDriveType = GetDriveType( buf );
|
||
|
|
||
|
/* check if this is a CDROM drive */
|
||
|
if ( uDriveType == DRIVE_CDROM )
|
||
|
{
|
||
|
GetDriveInformation( i, &NtScsiDrives.drive[i] );
|
||
|
|
||
|
if ( NtScsiDrives.drive[i].bUsed )
|
||
|
retVal++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NtScsiDrives.numAdapters = NtScsiGetNumAdapters( );
|
||
|
|
||
|
bNtScsiAvailable = TRUE;
|
||
|
|
||
|
if ( retVal > 0 )
|
||
|
{
|
||
|
bUseNtScsi = TRUE;
|
||
|
}
|
||
|
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
|
||
|
int NtScsiDeInit( void )
|
||
|
{
|
||
|
BYTE i;
|
||
|
|
||
|
if ( !bNtScsiAvailable )
|
||
|
return 0;
|
||
|
|
||
|
for( i = 2; i < 26; i++ )
|
||
|
{
|
||
|
if ( NtScsiDrives.drive[i].bUsed )
|
||
|
{
|
||
|
CloseHandle( NtScsiDrives.drive[i].hDevice );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NtScsiDrives.numAdapters = NtScsiGetNumAdapters( );
|
||
|
|
||
|
ZeroMemory( &NtScsiDrives, sizeof(NtScsiDrives) );
|
||
|
bNtScsiAvailable = FALSE;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Returns the number of "adapters" present.
|
||
|
*/
|
||
|
BYTE NtScsiGetNumAdapters( void )
|
||
|
{
|
||
|
BYTE buf[256] = {0};
|
||
|
WORD i;
|
||
|
BYTE numAdapters = 0;
|
||
|
|
||
|
// PortNumber 0 should exist, so pre-mark it. This avoids problems
|
||
|
// when the primary IDE drives are on PortNumber 0, but can't be opened
|
||
|
// because of insufficient privelege (ie. non-admin).
|
||
|
buf[0] = 1;
|
||
|
|
||
|
for( i = 0; i < 26; i++ )
|
||
|
{
|
||
|
if ( NtScsiDrives.drive[i].bUsed )
|
||
|
buf[NtScsiDrives.drive[i].ha] = 1;
|
||
|
}
|
||
|
|
||
|
for( i = 0; i <= 255; i++ )
|
||
|
{
|
||
|
if ( buf[i] )
|
||
|
numAdapters++;
|
||
|
}
|
||
|
|
||
|
return numAdapters;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Replacement for GetASPI32SupportInfo from wnaspi32.dll
|
||
|
*/
|
||
|
DWORD NtScsiGetASPI32SupportInfo( void )
|
||
|
{
|
||
|
DWORD retVal;
|
||
|
|
||
|
|
||
|
if ( !NtScsiDrives.numAdapters )
|
||
|
retVal = (DWORD)(MAKEWORD(0,SS_NO_ADAPTERS));
|
||
|
else
|
||
|
retVal = (DWORD)(MAKEWORD(NtScsiDrives.numAdapters,SS_COMP));
|
||
|
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Needs to call the appropriate function for the lpsrb->SRB_Cmd specified.
|
||
|
* Valid types are SC_HA_INQUIRY, SC_GET_DEV_TYPE, SC_EXEC_SCSI_CMD,
|
||
|
* and SC_RESET_DEV.
|
||
|
*/
|
||
|
DWORD NtScsiSendASPI32Command( LPSRB lpsrb )
|
||
|
{
|
||
|
if ( !lpsrb )
|
||
|
return SS_ERR;
|
||
|
|
||
|
switch( lpsrb->SRB_Cmd )
|
||
|
{
|
||
|
case SC_HA_INQUIRY:
|
||
|
return NtScsiHandleHaInquiry( (LPSRB_HAINQUIRY)lpsrb );
|
||
|
break;
|
||
|
|
||
|
case SC_GET_DEV_TYPE:
|
||
|
return NtScsiGetDeviceType( (LPSRB_GDEVBLOCK)lpsrb );
|
||
|
break;
|
||
|
|
||
|
case SC_EXEC_SCSI_CMD:
|
||
|
return NtScsiExecSCSICommand( (LPSRB_EXECSCSICMD)lpsrb, FALSE );
|
||
|
break;
|
||
|
|
||
|
case SC_RESET_DEV:
|
||
|
default:
|
||
|
lpsrb->SRB_Status = SS_ERR;
|
||
|
return SS_ERR;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return SS_ERR; // should never get to here...
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Universal function to get a file handle to the CD device. Since
|
||
|
* NT 4.0 wants just the GENERIC_READ flag, and Win2K wants both
|
||
|
* GENERIC_READ and GENERIC_WRITE (why a read-only CD device needs
|
||
|
* GENERIC_WRITE access is beyond me...), the easist workaround is to just
|
||
|
* try them both.
|
||
|
*/
|
||
|
static HANDLE GetFileHandle( BYTE i )
|
||
|
{
|
||
|
wchar_t buf[12] = {0};
|
||
|
HANDLE fh = NULL;
|
||
|
OSVERSIONINFO osver;
|
||
|
DWORD dwFlags;
|
||
|
|
||
|
memset( &osver, 0x00, sizeof(osver) );
|
||
|
osver.dwOSVersionInfoSize = sizeof(osver);
|
||
|
GetVersionEx( &osver );
|
||
|
|
||
|
// if Win2K or greater, add GENERIC_WRITE
|
||
|
dwFlags = GENERIC_READ;
|
||
|
|
||
|
if ( (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osver.dwMajorVersion > 4) )
|
||
|
{
|
||
|
dwFlags |= GENERIC_WRITE;
|
||
|
}
|
||
|
|
||
|
wsprintf( buf, L"\\\\.\\%c:", (wchar_t)('A'+i) );
|
||
|
fh = CreateFile( buf, dwFlags, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING, 0, NULL );
|
||
|
|
||
|
if ( fh == INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
// it went foobar somewhere, so try it with the GENERIC_WRITE bit flipped
|
||
|
dwFlags ^= GENERIC_WRITE;
|
||
|
fh = CreateFile( buf, dwFlags, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
|
||
|
}
|
||
|
|
||
|
if ( fh == INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
}
|
||
|
|
||
|
return fh;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* fills in a pDrive structure with information from a SCSI_INQUIRY
|
||
|
* and obtains the ha:tgt:lun values via IOCTL_SCSI_GET_ADDRESS
|
||
|
*/
|
||
|
void GetDriveInformation( BYTE i, NTSCSIDRIVE *pDrive )
|
||
|
{
|
||
|
HANDLE fh;
|
||
|
char buf[2048] = {0};
|
||
|
BOOL status;
|
||
|
PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER pswb;
|
||
|
PSCSI_ADDRESS pscsiAddr;
|
||
|
ULONG length, returned;
|
||
|
BYTE inqData[100] = {0};
|
||
|
|
||
|
fh = GetFileHandle( i );
|
||
|
|
||
|
if ( fh == INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the drive inquiry data
|
||
|
*/
|
||
|
ZeroMemory( &buf, 2048 );
|
||
|
ZeroMemory( inqData, 100 );
|
||
|
pswb = (PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER)buf;
|
||
|
pswb->spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
|
||
|
pswb->spt.CdbLength = 6;
|
||
|
pswb->spt.SenseInfoLength = 24;
|
||
|
pswb->spt.DataIn = SCSI_IOCTL_DATA_IN;
|
||
|
pswb->spt.DataTransferLength = 100;
|
||
|
pswb->spt.TimeOutValue = 2;
|
||
|
pswb->spt.DataBuffer = inqData;
|
||
|
pswb->spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER,ucSenseBuf );
|
||
|
pswb->spt.Cdb[0] = 0x12;
|
||
|
pswb->spt.Cdb[4] = 100;
|
||
|
|
||
|
length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
|
||
|
status = DeviceIoControl( fh,
|
||
|
IOCTL_SCSI_PASS_THROUGH_DIRECT,
|
||
|
pswb,
|
||
|
length,
|
||
|
pswb,
|
||
|
length,
|
||
|
&returned,
|
||
|
NULL );
|
||
|
|
||
|
if ( !status )
|
||
|
{
|
||
|
CloseHandle( fh );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
memcpy( pDrive->inqData, inqData, 36 );
|
||
|
|
||
|
/*
|
||
|
* get the address (path/tgt/lun) of the drive via IOCTL_SCSI_GET_ADDRESS
|
||
|
*/
|
||
|
ZeroMemory( &buf, 2048 );
|
||
|
pscsiAddr = (PSCSI_ADDRESS)buf;
|
||
|
pscsiAddr->Length = sizeof(SCSI_ADDRESS);
|
||
|
if ( DeviceIoControl( fh, IOCTL_SCSI_GET_ADDRESS, NULL, 0,
|
||
|
pscsiAddr, sizeof(buf), &returned,
|
||
|
NULL ) )
|
||
|
{
|
||
|
pDrive->bUsed = TRUE;
|
||
|
pDrive->ha = pscsiAddr->PortNumber;
|
||
|
pDrive->tgt = pscsiAddr->TargetId;
|
||
|
pDrive->lun = pscsiAddr->Lun;
|
||
|
pDrive->driveLetter = i;
|
||
|
pDrive->hDevice = INVALID_HANDLE_VALUE;
|
||
|
}
|
||
|
else if (50 == GetLastError()) // usb/firewire
|
||
|
{
|
||
|
pDrive->bUsed = TRUE;
|
||
|
pDrive->ha = i;
|
||
|
pDrive->tgt = 0;
|
||
|
pDrive->lun = 0;
|
||
|
pDrive->driveLetter = i;
|
||
|
pDrive->hDevice = INVALID_HANDLE_VALUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pDrive->bUsed = FALSE;
|
||
|
}
|
||
|
|
||
|
CloseHandle( fh );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
DWORD NtScsiHandleHaInquiry( LPSRB_HAINQUIRY lpsrb )
|
||
|
{
|
||
|
DWORD *pMTL;
|
||
|
|
||
|
lpsrb->HA_Count = NtScsiDrives.numAdapters;
|
||
|
|
||
|
if ( lpsrb->SRB_HaId >= NtScsiDrives.numAdapters )
|
||
|
{
|
||
|
lpsrb->SRB_Status = SS_INVALID_HA;
|
||
|
return SS_INVALID_HA;
|
||
|
}
|
||
|
lpsrb->HA_SCSI_ID = 7; // who cares... we're not really an ASPI manager
|
||
|
memcpy( lpsrb->HA_ManagerId, "blahblahblahblah", 16 );
|
||
|
memcpy( lpsrb->HA_Identifier, "blahblahblahblah", 16 );
|
||
|
lpsrb->HA_Identifier[13] = (char)('0'+lpsrb->SRB_HaId);
|
||
|
ZeroMemory( lpsrb->HA_Unique, 16 );
|
||
|
lpsrb->HA_Unique[3] = 8;
|
||
|
pMTL = (LPDWORD)&lpsrb->HA_Unique[4];
|
||
|
*pMTL = 64 * 1024;
|
||
|
|
||
|
lpsrb->SRB_Status = SS_COMP;
|
||
|
return SS_COMP;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Scans through the drive array and returns DTYPE_CDROM type for all items
|
||
|
* found, and DTYPE_UNKNOWN for all others.
|
||
|
*/
|
||
|
DWORD NtScsiGetDeviceType( LPSRB_GDEVBLOCK lpsrb )
|
||
|
{
|
||
|
lpsrb->SRB_Status = SS_NO_DEVICE;
|
||
|
if ( NtScsiGetDeviceIndex( lpsrb->SRB_HaId, lpsrb->SRB_Target, lpsrb->SRB_Lun ) )
|
||
|
lpsrb->SRB_Status = SS_COMP;
|
||
|
|
||
|
if ( lpsrb->SRB_Status == SS_COMP )
|
||
|
lpsrb->SRB_DeviceType = DTC_CDROM;
|
||
|
else
|
||
|
lpsrb->SRB_DeviceType = DTC_UNKNOWN;
|
||
|
|
||
|
return lpsrb->SRB_Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Looks up the index in the drive array for a given ha:tgt:lun triple
|
||
|
*/
|
||
|
BYTE NtScsiGetDeviceIndex( BYTE ha, BYTE tgt, BYTE lun )
|
||
|
{
|
||
|
BYTE i;
|
||
|
|
||
|
for( i = 2; i < 26; i++ )
|
||
|
{
|
||
|
if ( NtScsiDrives.drive[i].bUsed )
|
||
|
{
|
||
|
NTSCSIDRIVE *lpd;
|
||
|
lpd = &NtScsiDrives.drive[i];
|
||
|
if ( (lpd->ha == ha) && (lpd->tgt == tgt) && (lpd->lun == lun) )
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Converts ASPI-style SRB to SCSI Pass Through IOCTL
|
||
|
*/
|
||
|
DWORD NtScsiExecSCSICommand( LPSRB_EXECSCSICMD lpsrb, BOOL bBeenHereBefore )
|
||
|
{
|
||
|
BOOL status;
|
||
|
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
|
||
|
ULONG length, returned;
|
||
|
BYTE idx;
|
||
|
|
||
|
idx = NtScsiGetDeviceIndex( lpsrb->SRB_HaId, lpsrb->SRB_Target, lpsrb->SRB_Lun );
|
||
|
|
||
|
if ( idx == 0 )
|
||
|
{
|
||
|
lpsrb->SRB_Status = SS_ERR;
|
||
|
return SS_ERR;
|
||
|
}
|
||
|
|
||
|
if ( lpsrb->CDBByte[0] == 0x12 ) // is it an INQUIRY?
|
||
|
{
|
||
|
lpsrb->SRB_Status = SS_COMP;
|
||
|
memcpy( lpsrb->SRB_BufPointer, NtScsiDrives.drive[idx].inqData, 36 );
|
||
|
return SS_COMP;
|
||
|
}
|
||
|
|
||
|
if ( NtScsiDrives.drive[idx].hDevice == INVALID_HANDLE_VALUE )
|
||
|
NtScsiDrives.drive[idx].hDevice = GetFileHandle( NtScsiDrives.drive[idx].driveLetter );
|
||
|
|
||
|
ZeroMemory( &swb, sizeof(swb) );
|
||
|
swb.spt.Length = sizeof(SCSI_PASS_THROUGH);
|
||
|
swb.spt.CdbLength = lpsrb->SRB_CDBLen;
|
||
|
if ( lpsrb->SRB_Flags & SRB_DIR_IN )
|
||
|
swb.spt.DataIn = SCSI_IOCTL_DATA_IN;
|
||
|
else if ( lpsrb->SRB_Flags & SRB_DIR_OUT )
|
||
|
swb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
|
||
|
else
|
||
|
swb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
|
||
|
swb.spt.DataTransferLength = lpsrb->SRB_BufLen;
|
||
|
swb.spt.TimeOutValue = 5;
|
||
|
swb.spt.DataBuffer = lpsrb->SRB_BufPointer;
|
||
|
swb.spt.SenseInfoOffset =
|
||
|
offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf );
|
||
|
memcpy( swb.spt.Cdb, lpsrb->CDBByte, lpsrb->SRB_CDBLen );
|
||
|
length = sizeof(swb);
|
||
|
|
||
|
status = DeviceIoControl( NtScsiDrives.drive[idx].hDevice,
|
||
|
IOCTL_SCSI_PASS_THROUGH_DIRECT,
|
||
|
&swb,
|
||
|
length,
|
||
|
&swb,
|
||
|
length,
|
||
|
&returned,
|
||
|
NULL );
|
||
|
|
||
|
if ( status )
|
||
|
{
|
||
|
lpsrb->SRB_Status = SS_COMP;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DWORD dwErrCode;
|
||
|
|
||
|
lpsrb->SRB_Status = SS_ERR;
|
||
|
lpsrb->SRB_TargStat = 0x0004;
|
||
|
dwErrCode = GetLastError();
|
||
|
/*
|
||
|
* KLUDGE ALERT! KLUDGE ALERT! KLUDGE ALERT!
|
||
|
* Whenever a disk changer switches disks, it may render the device
|
||
|
* handle invalid. We try to catch these errors here and recover
|
||
|
* from them.
|
||
|
*/
|
||
|
if ( !bBeenHereBefore &&
|
||
|
((dwErrCode == ERROR_MEDIA_CHANGED) || (dwErrCode == ERROR_INVALID_HANDLE)) )
|
||
|
{
|
||
|
if ( dwErrCode != ERROR_INVALID_HANDLE )
|
||
|
CloseHandle( NtScsiDrives.drive[idx].hDevice );
|
||
|
GetDriveInformation( idx, &NtScsiDrives.drive[idx] );
|
||
|
|
||
|
return NtScsiExecSCSICommand( lpsrb, TRUE );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return lpsrb->SRB_Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
BOOL UsingSCSIPT( void )
|
||
|
{
|
||
|
return bUseNtScsi;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Calls GetFileHandle for the CD refered to by ha:tgt:lun to open it for
|
||
|
* use
|
||
|
*/
|
||
|
void NtScsiOpenCDHandle( BYTE ha, BYTE tgt, BYTE lun )
|
||
|
{
|
||
|
BYTE idx;
|
||
|
|
||
|
idx = NtScsiGetDeviceIndex( ha, tgt, lun );
|
||
|
|
||
|
if ( idx && NtScsiDrives.drive[idx].hDevice == INVALID_HANDLE_VALUE )
|
||
|
NtScsiDrives.drive[idx].hDevice = GetFileHandle( NtScsiDrives.drive[idx].driveLetter );
|
||
|
}
|