⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scsipt.c

📁 读取音乐光盘磁道为磁盘文件的DLL源码
💻 C
字号:
/*
 * scsipt.c - Copyright (C) 1999 Jay A. Key
 *
 * Native NT support functions via the SCSI Pass Through interface instead
 * of ASPI.  Although based on information from the NT 4.0 DDK from 
 * Microsoft, the information has been sufficiently distilled to allow
 * compilation w/o having the DDK installed.
 *
 * Implements the following functions:
 *   DWORD SPTIGetASPI32SupportInfo(void);
 *   DWORD SPTISendASPI32Command(LPSRB);
 * which are equivalents to their ASPI counterparts.  Additionally implements
 *   int InitSCSIPT( void );
 *   int DeinitSCSIPT( void );
 *
 **********************************************************************
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser 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
 *
 **********************************************************************
 *
 * $Id: scsipt.c,v 1.2 2000/02/25 10:47:37 akey Exp $
 * $Date: &
 * $Locker:  $
 * $Log: scsipt.c,v $
 * Revision 1.2  2000/02/25 10:47:37  akey
 * sync'ed with akrip32.dll v0.94
 *
 * Revision 1.6  2000/02/25 10:13:26  akey
 * Added SPTIOpenCDHandle for scsi pass through
 *
 * Revision 1.5  2000/02/25 07:50:38  akey
 * CreateFile now tries both with/without the GENERIC_WRITE flag
 *
 * Revision 1.4  2000/02/14 09:56:25  akey
 * cleaned up #ifdef _DEBUG code considerably
 *
 * Revision 1.3  2000/01/31 15:35:40  akey
 * v0.93: added CDDBGetServerList and fixed problem with Win2000 scsi pass through code
 *
 * Revision 1.2  2000/01/17 11:11:29  akey
 * Put debug code in #ifdef
 *
 * Revision 1.1  2000/01/12 10:06:05  akey
 * Minimal SCSI passthrough IOCTL support
 *
 *
 */

#include <windows.h>
#include <stdio.h>
#include <stddef.h>
#include "myaspi32.h"
#include "scsidefs.h"
#include "aspilib.h"
#include "akrip32.h"
#include "scsipt.h"


typedef struct {
  BYTE ha;
  BYTE tgt;
  BYTE lun;
  BYTE driveLetter;
  BOOL bUsed;
  HANDLE hDevice;
  BYTE inqData[36];
} DRIVE;

typedef struct {
  BYTE numAdapters;
  DRIVE drive[26];
} SPTIGLOBAL;

void GetDriveInformation( BYTE i, DRIVE *pDrive );
BYTE SPTIGetNumAdapters( void );
BYTE SPTIGetDeviceIndex( BYTE ha, BYTE tgt, BYTE lun );
DWORD SPTIHandleHaInquiry( LPSRB_HAInquiry lpsrb );
DWORD SPTIGetDeviceType( LPSRB_GDEVBlock lpsrb );
DWORD SPTIExecSCSICommand( LPSRB_ExecSCSICmd lpsrb, BOOL bBeenHereBefore );
HANDLE GetFileHandle( BYTE i );

static BOOL bSCSIPTInit = FALSE;
static SPTIGLOBAL sptiglobal;
static BOOL bUsingSCSIPT = 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 InitSCSIPT( void )
{
  BYTE i;
  char buf[4];
  UINT uDriveType;
  int retVal = 0;

  if ( bSCSIPTInit )
    return 0;

  ZeroMemory( &sptiglobal, sizeof(SPTIGLOBAL) );
  for( i = 0; i < 26; i++ )
    sptiglobal.drive[i].hDevice = INVALID_HANDLE_VALUE;
  for( i = 2; i < 26; i++ )
    {
      wsprintf( buf, "%c:\\", (char)('A'+i) );
      uDriveType = GetDriveType( buf );
      if ( uDriveType == DRIVE_CDROM )
	{
	  GetDriveInformation( i, &sptiglobal.drive[i] );
	  if ( sptiglobal.drive[i].bUsed )
	    retVal++;
	}
    }

  sptiglobal.numAdapters = SPTIGetNumAdapters( );

  bSCSIPTInit = TRUE;
  if ( retVal > 0 )
    bUsingSCSIPT = TRUE;
  return retVal;
}


int DeinitSCSIPT( void )
{
  BYTE i;

  if ( !bSCSIPTInit )
    return 0;

  for( i = 2; i < 26; i++ )
    {
      if ( sptiglobal.drive[i].bUsed )
	{
	  CloseHandle( sptiglobal.drive[i].hDevice );
	}
    }

  sptiglobal.numAdapters = SPTIGetNumAdapters( );

  ZeroMemory( &sptiglobal, sizeof(SPTIGLOBAL) );
  bSCSIPTInit = TRUE;
  return -1;
}



BYTE SPTIGetNumAdapters( void )
{
  BYTE buf[256];
  WORD i;
  BYTE numAdapters = 0;

  ZeroMemory( buf, 256 );

  for( i = 0; i < 26; i++ )
    {
      if ( sptiglobal.drive[i].bUsed )
	buf[sptiglobal.drive[i].ha] = 1;
    }

  for( i = 0; i <= 255; i++ )
    if ( buf[i] )
      numAdapters++;

  return numAdapters;
}


DWORD SPTIGetASPI32SupportInfo( void )
{
  DWORD retVal;

#ifdef _DEBUG_SCSIPT
  dbprintf( "AKRip32: SPTIGetASPI32SupportInfo" );
#endif
  if ( !sptiglobal.numAdapters )
    retVal = (DWORD)(MAKEWORD(0,SS_NO_ADAPTERS));
  else
    retVal = (DWORD)(MAKEWORD(sptiglobal.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 SPTISendASPI32Command( LPSRB lpsrb )
{
  if ( !lpsrb )
    return SS_ERR;

  switch( lpsrb->SRB_Cmd )
    {
    case SC_HA_INQUIRY:
      return SPTIHandleHaInquiry( (LPSRB_HAInquiry)lpsrb );

    case SC_GET_DEV_TYPE:
      return SPTIGetDeviceType( (LPSRB_GDEVBlock)lpsrb );

    case SC_EXEC_SCSI_CMD:
      return SPTIExecSCSICommand( (LPSRB_ExecSCSICmd)lpsrb, FALSE );

    case SC_RESET_DEV:
    default:
      lpsrb->SRB_Status = SS_ERR;
      return SS_ERR;
    }

  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.
 */
HANDLE GetFileHandle( BYTE i )
{
  char buf[12];
  HANDLE fh;
  OSVERSIONINFO osver;
  DWORD dwFlags;

  ZeroMemory( &osver, 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;
#ifdef _DEBUG_SCSIPT
      dbprintf( "AKRip32: SCSIPT: GetFileHandle(): Setting for Win2K" );
#endif
    }

  wsprintf( buf, "\\\\.\\%c:", (char)('A'+i) );
  fh = CreateFile( buf, dwFlags, FILE_SHARE_READ, NULL,
		   OPEN_EXISTING, 0, NULL );

  if ( fh == INVALID_HANDLE_VALUE )
    {
      dwFlags ^= GENERIC_WRITE;
      fh = CreateFile( buf, dwFlags, FILE_SHARE_READ, NULL,
		       OPEN_EXISTING, 0, NULL );
    }


#ifdef _DEBUG_SCSIPT
  dbprintf( "akrip32: scsipt: CreateFile() failed! -> %d", GetLastError() );
#endif

  return fh;
}



void GetDriveInformation( BYTE i, DRIVE *pDrive )
{
  HANDLE fh;
  //char buf[12];
  BOOLEAN status;
  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
  ULONG length, returned;
  BYTE inqData[100];

#ifdef _DEBUG_SCSIPT
  dbprintf( "AKRip32: SCSIPT: Checking drive %c:", 'A'+i );
#endif

  fh = GetFileHandle( i );

  if ( fh == INVALID_HANDLE_VALUE )
    {
#ifdef _DEBUG_SCSIPT
      dbprintf( "       : fh == INVALID_HANDLE_VALUE" );
#endif
      return;
    }

#ifdef _DEBUG_SCSIPT
  dbprintf( "       : Index %d: fh == %08X", i, fh );
#endif

  ZeroMemory( &swb, sizeof(swb) );
  ZeroMemory( inqData, 100 );

  swb.spt.Length          = sizeof(SCSI_PASS_THROUGH);
  swb.spt.CdbLength       = 6;
  swb.spt.SenseInfoLength = 24;
  swb.spt.DataIn          = SCSI_IOCTL_DATA_IN;
  swb.spt.DataTransferLength = 100;
  swb.spt.TimeOutValue    = 2;
  swb.spt.DataBuffer      = inqData;
  swb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER,ucSenseBuf );
  swb.spt.Cdb[0]          = 0x12;
  swb.spt.Cdb[4]          = 100;

  length = sizeof(swb);
#ifdef _DEBUG_SCSIPT
  dbprintf( "AKRip32: GetDriveInformation: Calling DeviceIoControl" );
#endif
  status = DeviceIoControl( fh,
			    IOCTL_SCSI_PASS_THROUGH_DIRECT,
			    &swb,
			    length,
			    &swb,
			    length,
			    &returned,
			    NULL );

  if ( !status )
    {
      CloseHandle( fh );
#ifdef _DEBUG_SCSIPT
      dbprintf( "AKRip32: SCSIPT: Error DeviceIoControl() -> %d",
		GetLastError() );
#endif
      return;
    }

#ifdef _DEBUG_SCSIPT
  dbprintf( "AKRip32: SPTI: Adding drive %c: (%d:%d:%d)", 'A'+i,
	    swb.spt.PathId, swb.spt.TargetId, swb.spt.Lun );
#endif
  pDrive->bUsed     = TRUE;
  pDrive->ha        = swb.spt.PathId;
  pDrive->tgt       = swb.spt.TargetId;
  pDrive->lun       = swb.spt.Lun;
  pDrive->driveLetter = i;
  pDrive->hDevice   = INVALID_HANDLE_VALUE;
  memcpy( pDrive->inqData, inqData, 36 );

  CloseHandle( fh );
}



DWORD SPTIHandleHaInquiry( LPSRB_HAInquiry lpsrb )
{
  DWORD *pMTL;

  lpsrb->HA_Count    = sptiglobal.numAdapters;
  if ( lpsrb->SRB_HaID >= sptiglobal.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,  "AKASPI v0.000001", 16 );
  memcpy( lpsrb->HA_Identifier, "SCSI Adapter    ", 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 SPTIGetDeviceType( LPSRB_GDEVBlock lpsrb )
{
#ifdef _DEBUG_SCSIPT
  dbprintf( "AKRip32: SPTIGetDeviceType( %d:%d:%d )",lpsrb->SRB_HaID, lpsrb->SRB_Target, lpsrb->SRB_Lun );
#endif

  lpsrb->SRB_Status = SS_NO_DEVICE;
  if ( SPTIGetDeviceIndex( lpsrb->SRB_HaID, lpsrb->SRB_Target, lpsrb->SRB_Lun ) )
    lpsrb->SRB_Status = SS_COMP;

  if ( lpsrb->SRB_Status == SS_COMP )
    lpsrb->SRB_DeviceType = DTYPE_CDROM;
  else
    lpsrb->SRB_DeviceType = DTYPE_UNKNOWN;

  return lpsrb->SRB_Status;
}


BYTE SPTIGetDeviceIndex( BYTE ha, BYTE tgt, BYTE lun )
{
  BYTE i;

#ifdef _DEBUG_SCSIPT
  dbprintf( "AKRip32: SPTIGetDeviceIndex" );
#endif

  for( i = 2; i < 26; i++ )
    {
      if ( sptiglobal.drive[i].bUsed )
	{
	  DRIVE *lpd;
	  lpd = &sptiglobal.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 SPTIExecSCSICommand( LPSRB_ExecSCSICmd lpsrb, BOOL bBeenHereBefore )
{
  BOOLEAN status;
  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
  ULONG length, returned;
  //BYTE i;
  BYTE idx;

  idx = SPTIGetDeviceIndex( 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, sptiglobal.drive[idx].inqData, 36 );
      return SS_COMP;
    }

  if ( sptiglobal.drive[idx].hDevice == INVALID_HANDLE_VALUE )
    sptiglobal.drive[idx].hDevice = GetFileHandle( sptiglobal.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);

#ifdef _DEBUG_SCSIPT
  dbprintf( "AKRip32: SPTIExecSCSICmd: calling DeviceIoControl()" );
  dbprintf( "       : cmd == 0x%02X", swb.spt.Cdb[0] );
#endif
  status = DeviceIoControl( sptiglobal.drive[idx].hDevice,
			    IOCTL_SCSI_PASS_THROUGH_DIRECT,
			    &swb,
			    length,
			    &swb,
			    length,
			    &returned,
			    NULL );

  if ( status )
    {
      lpsrb->SRB_Status = SS_COMP;
      OutputDebugString( "       : SRB_Status == SS_COMP" );
    }
  else
    {
      DWORD dwErrCode;

      lpsrb->SRB_Status = SS_ERR;
      lpsrb->SRB_TargStat = 0x0004;
      dwErrCode = GetLastError();
#ifdef _DEBUG_SCSIPT
      dbprintf( "       : error == %d   handle == %08X", dwErrCode, sptiglobal.drive[idx].hDevice );
#endif
      /*
       * 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( sptiglobal.drive[idx].hDevice );
	  GetDriveInformation( idx, &sptiglobal.drive[idx] );
#ifdef _DEBUG_SCSIPT
	  dbprintf( "AKRip32: SPTIExecSCSICommand: Retrying after ERROR_MEDIA_CHANGED" );
#endif
	  return SPTIExecSCSICommand( lpsrb, TRUE );
	}
    }

  return lpsrb->SRB_Status;
}



BOOL UsingSCSIPT( void )
{
  return bUsingSCSIPT;
}



/*
 * Calls GetFileHandle for the CD refered to by ha:tgt:lun to open it for
 * use
 */
void SPTIOpenCDHandle( BYTE ha, BYTE tgt, BYTE lun )
{
  BYTE idx;

  idx = SPTIGetDeviceIndex( ha, tgt, lun );

  if ( idx && sptiglobal.drive[idx].hDevice == INVALID_HANDLE_VALUE )
    sptiglobal.drive[idx].hDevice = GetFileHandle( sptiglobal.drive[idx].driveLetter );  
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -