1
0

NTScsi.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. #include <stdio.h>
  2. #include <stddef.h>
  3. #include "NTScsi.h"
  4. typedef struct {
  5. BYTE ha;
  6. BYTE tgt;
  7. BYTE lun;
  8. BYTE driveLetter;
  9. BOOL bUsed;
  10. HANDLE hDevice;
  11. BYTE inqData[36];
  12. } NTSCSIDRIVE;
  13. typedef struct
  14. {
  15. BYTE numAdapters;
  16. NTSCSIDRIVE drive[26];
  17. } NTSCSIDRIVES;
  18. void GetDriveInformation( BYTE i, NTSCSIDRIVE *pDrive );
  19. static HANDLE GetFileHandle( BYTE i );
  20. static BOOL bNtScsiAvailable = FALSE;
  21. static NTSCSIDRIVES NtScsiDrives;
  22. static BOOL bUseNtScsi = FALSE;
  23. /*
  24. * Initialization of SCSI Pass Through Interface code. Responsible for
  25. * setting up the array of SCSI devices. This code will be a little
  26. * different from the normal code -- it will query each drive letter from
  27. * C: through Z: to see if it is a CD. When we identify a CD, we then
  28. * send CDB with the INQUIRY command to it -- NT will automagically fill in
  29. * the PathId, TargetId, and Lun for us.
  30. */
  31. int NtScsiInit( void )
  32. {
  33. BYTE i;
  34. wchar_t buf[4] = {0};
  35. UINT uDriveType;
  36. int retVal = 0;
  37. if ( bNtScsiAvailable )
  38. {
  39. for( i = 2; i < 26; i++ ) if ( NtScsiDrives.drive[i].bUsed ) retVal++;
  40. bUseNtScsi = (retVal > 0 );
  41. return retVal;
  42. }
  43. memset( &NtScsiDrives, 0x00, sizeof(NtScsiDrives) );
  44. for( i = 0; i < 26; i++ )
  45. {
  46. NtScsiDrives.drive[i].hDevice = INVALID_HANDLE_VALUE;
  47. }
  48. for( i = 2; i < 26; i++ )
  49. {
  50. wsprintf( buf, L"%c:\\", (wchar_t)('A'+i) );
  51. uDriveType = GetDriveType( buf );
  52. /* check if this is a CDROM drive */
  53. if ( uDriveType == DRIVE_CDROM )
  54. {
  55. GetDriveInformation( i, &NtScsiDrives.drive[i] );
  56. if ( NtScsiDrives.drive[i].bUsed )
  57. retVal++;
  58. }
  59. }
  60. NtScsiDrives.numAdapters = NtScsiGetNumAdapters( );
  61. bNtScsiAvailable = TRUE;
  62. if ( retVal > 0 )
  63. {
  64. bUseNtScsi = TRUE;
  65. }
  66. return retVal;
  67. }
  68. int NtScsiDeInit( void )
  69. {
  70. BYTE i;
  71. if ( !bNtScsiAvailable )
  72. return 0;
  73. for( i = 2; i < 26; i++ )
  74. {
  75. if ( NtScsiDrives.drive[i].bUsed )
  76. {
  77. CloseHandle( NtScsiDrives.drive[i].hDevice );
  78. }
  79. }
  80. NtScsiDrives.numAdapters = NtScsiGetNumAdapters( );
  81. ZeroMemory( &NtScsiDrives, sizeof(NtScsiDrives) );
  82. bNtScsiAvailable = FALSE;
  83. return -1;
  84. }
  85. /*
  86. * Returns the number of "adapters" present.
  87. */
  88. BYTE NtScsiGetNumAdapters( void )
  89. {
  90. BYTE buf[256] = {0};
  91. WORD i;
  92. BYTE numAdapters = 0;
  93. // PortNumber 0 should exist, so pre-mark it. This avoids problems
  94. // when the primary IDE drives are on PortNumber 0, but can't be opened
  95. // because of insufficient privelege (ie. non-admin).
  96. buf[0] = 1;
  97. for( i = 0; i < 26; i++ )
  98. {
  99. if ( NtScsiDrives.drive[i].bUsed )
  100. buf[NtScsiDrives.drive[i].ha] = 1;
  101. }
  102. for( i = 0; i <= 255; i++ )
  103. {
  104. if ( buf[i] )
  105. numAdapters++;
  106. }
  107. return numAdapters;
  108. }
  109. /*
  110. * Replacement for GetASPI32SupportInfo from wnaspi32.dll
  111. */
  112. DWORD NtScsiGetASPI32SupportInfo( void )
  113. {
  114. DWORD retVal;
  115. if ( !NtScsiDrives.numAdapters )
  116. retVal = (DWORD)(MAKEWORD(0,SS_NO_ADAPTERS));
  117. else
  118. retVal = (DWORD)(MAKEWORD(NtScsiDrives.numAdapters,SS_COMP));
  119. return retVal;
  120. }
  121. /*
  122. * Needs to call the appropriate function for the lpsrb->SRB_Cmd specified.
  123. * Valid types are SC_HA_INQUIRY, SC_GET_DEV_TYPE, SC_EXEC_SCSI_CMD,
  124. * and SC_RESET_DEV.
  125. */
  126. DWORD NtScsiSendASPI32Command( LPSRB lpsrb )
  127. {
  128. if ( !lpsrb )
  129. return SS_ERR;
  130. switch( lpsrb->SRB_Cmd )
  131. {
  132. case SC_HA_INQUIRY:
  133. return NtScsiHandleHaInquiry( (LPSRB_HAINQUIRY)lpsrb );
  134. break;
  135. case SC_GET_DEV_TYPE:
  136. return NtScsiGetDeviceType( (LPSRB_GDEVBLOCK)lpsrb );
  137. break;
  138. case SC_EXEC_SCSI_CMD:
  139. return NtScsiExecSCSICommand( (LPSRB_EXECSCSICMD)lpsrb, FALSE );
  140. break;
  141. case SC_RESET_DEV:
  142. default:
  143. lpsrb->SRB_Status = SS_ERR;
  144. return SS_ERR;
  145. break;
  146. }
  147. return SS_ERR; // should never get to here...
  148. }
  149. /*
  150. * Universal function to get a file handle to the CD device. Since
  151. * NT 4.0 wants just the GENERIC_READ flag, and Win2K wants both
  152. * GENERIC_READ and GENERIC_WRITE (why a read-only CD device needs
  153. * GENERIC_WRITE access is beyond me...), the easist workaround is to just
  154. * try them both.
  155. */
  156. static HANDLE GetFileHandle( BYTE i )
  157. {
  158. wchar_t buf[12] = {0};
  159. HANDLE fh = NULL;
  160. OSVERSIONINFO osver;
  161. DWORD dwFlags;
  162. memset( &osver, 0x00, sizeof(osver) );
  163. osver.dwOSVersionInfoSize = sizeof(osver);
  164. GetVersionEx( &osver );
  165. // if Win2K or greater, add GENERIC_WRITE
  166. dwFlags = GENERIC_READ;
  167. if ( (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osver.dwMajorVersion > 4) )
  168. {
  169. dwFlags |= GENERIC_WRITE;
  170. }
  171. wsprintf( buf, L"\\\\.\\%c:", (wchar_t)('A'+i) );
  172. fh = CreateFile( buf, dwFlags, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,OPEN_EXISTING, 0, NULL );
  173. if ( fh == INVALID_HANDLE_VALUE )
  174. {
  175. // it went foobar somewhere, so try it with the GENERIC_WRITE bit flipped
  176. dwFlags ^= GENERIC_WRITE;
  177. fh = CreateFile( buf, dwFlags, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
  178. }
  179. if ( fh == INVALID_HANDLE_VALUE )
  180. {
  181. }
  182. else
  183. {
  184. }
  185. return fh;
  186. }
  187. /*
  188. * fills in a pDrive structure with information from a SCSI_INQUIRY
  189. * and obtains the ha:tgt:lun values via IOCTL_SCSI_GET_ADDRESS
  190. */
  191. void GetDriveInformation( BYTE i, NTSCSIDRIVE *pDrive )
  192. {
  193. HANDLE fh;
  194. char buf[2048] = {0};
  195. BOOL status;
  196. PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER pswb;
  197. PSCSI_ADDRESS pscsiAddr;
  198. ULONG length, returned;
  199. BYTE inqData[100] = {0};
  200. fh = GetFileHandle( i );
  201. if ( fh == INVALID_HANDLE_VALUE )
  202. {
  203. return;
  204. }
  205. /*
  206. * Get the drive inquiry data
  207. */
  208. ZeroMemory( &buf, 2048 );
  209. ZeroMemory( inqData, 100 );
  210. pswb = (PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER)buf;
  211. pswb->spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
  212. pswb->spt.CdbLength = 6;
  213. pswb->spt.SenseInfoLength = 24;
  214. pswb->spt.DataIn = SCSI_IOCTL_DATA_IN;
  215. pswb->spt.DataTransferLength = 100;
  216. pswb->spt.TimeOutValue = 2;
  217. pswb->spt.DataBuffer = inqData;
  218. pswb->spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER,ucSenseBuf );
  219. pswb->spt.Cdb[0] = 0x12;
  220. pswb->spt.Cdb[4] = 100;
  221. length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
  222. status = DeviceIoControl( fh,
  223. IOCTL_SCSI_PASS_THROUGH_DIRECT,
  224. pswb,
  225. length,
  226. pswb,
  227. length,
  228. &returned,
  229. NULL );
  230. if ( !status )
  231. {
  232. CloseHandle( fh );
  233. return;
  234. }
  235. memcpy( pDrive->inqData, inqData, 36 );
  236. /*
  237. * get the address (path/tgt/lun) of the drive via IOCTL_SCSI_GET_ADDRESS
  238. */
  239. ZeroMemory( &buf, 2048 );
  240. pscsiAddr = (PSCSI_ADDRESS)buf;
  241. pscsiAddr->Length = sizeof(SCSI_ADDRESS);
  242. if ( DeviceIoControl( fh, IOCTL_SCSI_GET_ADDRESS, NULL, 0,
  243. pscsiAddr, sizeof(buf), &returned,
  244. NULL ) )
  245. {
  246. pDrive->bUsed = TRUE;
  247. pDrive->ha = pscsiAddr->PortNumber;
  248. pDrive->tgt = pscsiAddr->TargetId;
  249. pDrive->lun = pscsiAddr->Lun;
  250. pDrive->driveLetter = i;
  251. pDrive->hDevice = INVALID_HANDLE_VALUE;
  252. }
  253. else if (50 == GetLastError()) // usb/firewire
  254. {
  255. pDrive->bUsed = TRUE;
  256. pDrive->ha = i;
  257. pDrive->tgt = 0;
  258. pDrive->lun = 0;
  259. pDrive->driveLetter = i;
  260. pDrive->hDevice = INVALID_HANDLE_VALUE;
  261. }
  262. else
  263. {
  264. pDrive->bUsed = FALSE;
  265. }
  266. CloseHandle( fh );
  267. }
  268. DWORD NtScsiHandleHaInquiry( LPSRB_HAINQUIRY lpsrb )
  269. {
  270. DWORD *pMTL;
  271. lpsrb->HA_Count = NtScsiDrives.numAdapters;
  272. if ( lpsrb->SRB_HaId >= NtScsiDrives.numAdapters )
  273. {
  274. lpsrb->SRB_Status = SS_INVALID_HA;
  275. return SS_INVALID_HA;
  276. }
  277. lpsrb->HA_SCSI_ID = 7; // who cares... we're not really an ASPI manager
  278. memcpy( lpsrb->HA_ManagerId, "blahblahblahblah", 16 );
  279. memcpy( lpsrb->HA_Identifier, "blahblahblahblah", 16 );
  280. lpsrb->HA_Identifier[13] = (char)('0'+lpsrb->SRB_HaId);
  281. ZeroMemory( lpsrb->HA_Unique, 16 );
  282. lpsrb->HA_Unique[3] = 8;
  283. pMTL = (LPDWORD)&lpsrb->HA_Unique[4];
  284. *pMTL = 64 * 1024;
  285. lpsrb->SRB_Status = SS_COMP;
  286. return SS_COMP;
  287. }
  288. /*
  289. * Scans through the drive array and returns DTYPE_CDROM type for all items
  290. * found, and DTYPE_UNKNOWN for all others.
  291. */
  292. DWORD NtScsiGetDeviceType( LPSRB_GDEVBLOCK lpsrb )
  293. {
  294. lpsrb->SRB_Status = SS_NO_DEVICE;
  295. if ( NtScsiGetDeviceIndex( lpsrb->SRB_HaId, lpsrb->SRB_Target, lpsrb->SRB_Lun ) )
  296. lpsrb->SRB_Status = SS_COMP;
  297. if ( lpsrb->SRB_Status == SS_COMP )
  298. lpsrb->SRB_DeviceType = DTC_CDROM;
  299. else
  300. lpsrb->SRB_DeviceType = DTC_UNKNOWN;
  301. return lpsrb->SRB_Status;
  302. }
  303. /*
  304. * Looks up the index in the drive array for a given ha:tgt:lun triple
  305. */
  306. BYTE NtScsiGetDeviceIndex( BYTE ha, BYTE tgt, BYTE lun )
  307. {
  308. BYTE i;
  309. for( i = 2; i < 26; i++ )
  310. {
  311. if ( NtScsiDrives.drive[i].bUsed )
  312. {
  313. NTSCSIDRIVE *lpd;
  314. lpd = &NtScsiDrives.drive[i];
  315. if ( (lpd->ha == ha) && (lpd->tgt == tgt) && (lpd->lun == lun) )
  316. return i;
  317. }
  318. }
  319. return 0;
  320. }
  321. /*
  322. * Converts ASPI-style SRB to SCSI Pass Through IOCTL
  323. */
  324. DWORD NtScsiExecSCSICommand( LPSRB_EXECSCSICMD lpsrb, BOOL bBeenHereBefore )
  325. {
  326. BOOL status;
  327. SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
  328. ULONG length, returned;
  329. BYTE idx;
  330. idx = NtScsiGetDeviceIndex( lpsrb->SRB_HaId, lpsrb->SRB_Target, lpsrb->SRB_Lun );
  331. if ( idx == 0 )
  332. {
  333. lpsrb->SRB_Status = SS_ERR;
  334. return SS_ERR;
  335. }
  336. if ( lpsrb->CDBByte[0] == 0x12 ) // is it an INQUIRY?
  337. {
  338. lpsrb->SRB_Status = SS_COMP;
  339. memcpy( lpsrb->SRB_BufPointer, NtScsiDrives.drive[idx].inqData, 36 );
  340. return SS_COMP;
  341. }
  342. if ( NtScsiDrives.drive[idx].hDevice == INVALID_HANDLE_VALUE )
  343. NtScsiDrives.drive[idx].hDevice = GetFileHandle( NtScsiDrives.drive[idx].driveLetter );
  344. ZeroMemory( &swb, sizeof(swb) );
  345. swb.spt.Length = sizeof(SCSI_PASS_THROUGH);
  346. swb.spt.CdbLength = lpsrb->SRB_CDBLen;
  347. if ( lpsrb->SRB_Flags & SRB_DIR_IN )
  348. swb.spt.DataIn = SCSI_IOCTL_DATA_IN;
  349. else if ( lpsrb->SRB_Flags & SRB_DIR_OUT )
  350. swb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
  351. else
  352. swb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
  353. swb.spt.DataTransferLength = lpsrb->SRB_BufLen;
  354. swb.spt.TimeOutValue = 5;
  355. swb.spt.DataBuffer = lpsrb->SRB_BufPointer;
  356. swb.spt.SenseInfoOffset =
  357. offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf );
  358. memcpy( swb.spt.Cdb, lpsrb->CDBByte, lpsrb->SRB_CDBLen );
  359. length = sizeof(swb);
  360. status = DeviceIoControl( NtScsiDrives.drive[idx].hDevice,
  361. IOCTL_SCSI_PASS_THROUGH_DIRECT,
  362. &swb,
  363. length,
  364. &swb,
  365. length,
  366. &returned,
  367. NULL );
  368. if ( status )
  369. {
  370. lpsrb->SRB_Status = SS_COMP;
  371. }
  372. else
  373. {
  374. DWORD dwErrCode;
  375. lpsrb->SRB_Status = SS_ERR;
  376. lpsrb->SRB_TargStat = 0x0004;
  377. dwErrCode = GetLastError();
  378. /*
  379. * KLUDGE ALERT! KLUDGE ALERT! KLUDGE ALERT!
  380. * Whenever a disk changer switches disks, it may render the device
  381. * handle invalid. We try to catch these errors here and recover
  382. * from them.
  383. */
  384. if ( !bBeenHereBefore &&
  385. ((dwErrCode == ERROR_MEDIA_CHANGED) || (dwErrCode == ERROR_INVALID_HANDLE)) )
  386. {
  387. if ( dwErrCode != ERROR_INVALID_HANDLE )
  388. CloseHandle( NtScsiDrives.drive[idx].hDevice );
  389. GetDriveInformation( idx, &NtScsiDrives.drive[idx] );
  390. return NtScsiExecSCSICommand( lpsrb, TRUE );
  391. }
  392. }
  393. return lpsrb->SRB_Status;
  394. }
  395. BOOL UsingSCSIPT( void )
  396. {
  397. return bUseNtScsi;
  398. }
  399. /*
  400. * Calls GetFileHandle for the CD refered to by ha:tgt:lun to open it for
  401. * use
  402. */
  403. void NtScsiOpenCDHandle( BYTE ha, BYTE tgt, BYTE lun )
  404. {
  405. BYTE idx;
  406. idx = NtScsiGetDeviceIndex( ha, tgt, lun );
  407. if ( idx && NtScsiDrives.drive[idx].hDevice == INVALID_HANDLE_VALUE )
  408. NtScsiDrives.drive[idx].hDevice = GetFileHandle( NtScsiDrives.drive[idx].driveLetter );
  409. }