wincd.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. {
  2. }
  3. unit wincd;
  4. {$mode objfpc}
  5. {$h+}
  6. interface
  7. uses Windows,SysUtils;
  8. Type
  9. TCDAccessMethod = (camNone,camASPI,camSPTI,camIOCTL);
  10. {$packrecords c}
  11. TTOCTrack = packed record
  12. rsvd,
  13. ADR,
  14. trackNumber,
  15. rsvd2 : Byte;
  16. addr : Array[0..3] of byte;
  17. end;
  18. TTOC = packed Record
  19. toclen : word;
  20. firsttrack,
  21. lastTrack : byte;
  22. toctrack: Array[0..99] of TTocTrack;
  23. end;
  24. Const
  25. AccessMethodNames : Array[TCDAccessMethod] of string
  26. = ('None','ASPI','SPTI','IOCTL');
  27. Function GetCDAccessMethod : TCDAccessMethod;
  28. Procedure SetCDAccessMethod (Value : TCDAccessMethod);
  29. Function ReadTOC(Device : String; Var TOC : TTOc) : Integer;
  30. Function EnumCDDrives(Var Drives : Array of String) : Integer;
  31. Function GetNumDrives : Integer;
  32. implementation
  33. uses cdromioctl,wnaspi32,scsidefs;
  34. Var
  35. CurrentAccessMethod : TCDAccessMethod;
  36. CDOSVer : Integer;
  37. { ---------------------------------------------------------------------
  38. SPTI Defines.
  39. ---------------------------------------------------------------------}
  40. Type
  41. {$packrecords C}
  42. SCSI_PASS_THROUGH = record
  43. Length : USHORT;
  44. ScsiStatus : UCHAR;
  45. PathId : UCHAR;
  46. TargetId : UCHAR;
  47. Lun : UCHAR;
  48. CdbLength : UCHAR;
  49. SenseInfoLength : UCHAR;
  50. DataIn : UCHAR;
  51. DataTransferLength : ULONG;
  52. TimeOutValue : ULONG;
  53. DataBufferOffset : ULONG;
  54. SenseInfoOffset : ULONG;
  55. Cdb : array[0..15] of UCHAR;
  56. end;
  57. TSCSI_PASS_THROUGH = SCSI_PASS_THROUGH;
  58. PSCSI_PASS_THROUGH = ^TSCSI_PASS_THROUGH;
  59. SCSI_PASS_THROUGH_DIRECT = record
  60. Length : USHORT;
  61. ScsiStatus : UCHAR;
  62. PathId : UCHAR;
  63. TargetId : UCHAR;
  64. Lun : UCHAR;
  65. CdbLength : UCHAR;
  66. SenseInfoLength : UCHAR;
  67. DataIn : UCHAR;
  68. DataTransferLength : ULONG;
  69. TimeOutValue : ULONG;
  70. DataBuffer : PVOID;
  71. SenseInfoOffset : ULONG;
  72. Cdb : array[0..15] of UCHAR;
  73. end;
  74. TSCSI_PASS_THROUGH_DIRECT = SCSI_PASS_THROUGH_DIRECT;
  75. PSCSI_PASS_THROUGH_DIRECT = ^SCSI_PASS_THROUGH_DIRECT;
  76. SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER = record
  77. spt : SCSI_PASS_THROUGH_DIRECT;
  78. Filler : ULONG;
  79. ucSenseBuf : array[0..31] of UCHAR;
  80. end;
  81. TSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER = SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
  82. PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER = ^SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
  83. const
  84. METHOD_BUFFERED = 0;
  85. METHOD_IN_DIRECT = 1;
  86. METHOD_OUT_DIRECT = 2;
  87. METHOD_NEITHER = 3;
  88. FILE_ANY_ACCESS = 0;
  89. FILE_READ_ACCESS = $0001;
  90. FILE_WRITE_ACCESS = $0002;
  91. IOCTL_CDROM_BASE = $00000002;
  92. IOCTL_SCSI_BASE = $00000004;
  93. SCSI_IOCTL_DATA_OUT = 0;
  94. SCSI_IOCTL_DATA_IN = 1;
  95. SCSI_IOCTL_DATA_UNSPECIFIED = 2;
  96. { ---------------------------------------------------------------------
  97. Initialization code.
  98. ---------------------------------------------------------------------}
  99. procedure InitWinCD;
  100. Var
  101. TheCDOSVER : TOSVersionInfo;
  102. begin
  103. TheCDOSVer.dwOSVersionInfoSize:=SizeOf(TheCDOSver);
  104. GetVersionEx(TheCDOSVer);
  105. CDOSVer:=TheCDOSVer.dwMajorVersion;
  106. If AspiLoaded then
  107. CurrentAccessMethod := camASPI
  108. else
  109. begin
  110. if (CDOSver<1) then
  111. CurrentAccessMethod := camNone
  112. else
  113. {
  114. It is better to use SPTI on windows, but the problem with that
  115. is that administrative priviledges are needed. A detection
  116. algorithm for these priviledges here would be nice.
  117. }
  118. CurrentAccessMethod := camSPTI;
  119. end;
  120. end;
  121. { ---------------------------------------------------------------------
  122. Actual reading of table of contents.
  123. ---------------------------------------------------------------------}
  124. { ---------------------------------------------------------------------
  125. 1. SPTI
  126. ---------------------------------------------------------------------}
  127. Function sptiReadTOC(Device : String; var TOC: TToC) : Integer;
  128. Var
  129. DriveHandle : THandle;
  130. len : Cardinal;
  131. buf : Array[0..31] of char;
  132. ID,retVal : Integer;
  133. Returned,Flags : Cardinal;
  134. swb : TSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
  135. begin
  136. Flags := Cardinal(GENERIC_READ);
  137. if (CDOSVer>4) then
  138. Flags:=Flags or Cardinal(GENERIC_WRITE);
  139. Device:=Upcase('\\.\'+Device);
  140. DriveHandle:=CreateFile(pchar(Device),Flags,FILE_SHARE_READ,
  141. nil,OPEN_EXISTING, 0, 0 );
  142. if (DriveHandle=INVALID_HANDLE_VALUE) then
  143. begin
  144. Result:=-1;
  145. Exit;
  146. end;
  147. Try
  148. Returned:= 0;
  149. len:= sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
  150. FillChar(swb, len ,0);
  151. With swb.spt do
  152. begin
  153. Length := sizeof(swb.spt); // SCSI_PASS_THROUGH);
  154. CdbLength := 10;
  155. DataIn := SCSI_IOCTL_DATA_IN;
  156. DataTransferLength := SizeOf(tOC);
  157. TimeOutValue := 5;
  158. DataBuffer := @TOC;
  159. SenseInfoOffset := @swb.ucSenseBuf[0]-pbyte(@swb.spt);
  160. Cdb[0] := $43; // read TOC
  161. Cdb[1] := $02; // MSF mode
  162. Cdb[7] := $03;
  163. Cdb[8] := $24;
  164. end;
  165. if (Not DeviceIoControl(DriveHandle,
  166. IOCTL_SCSI_PASS_THROUGH_DIRECT,
  167. @swb,
  168. len,
  169. @swb,
  170. len,
  171. @Returned,
  172. Nil)) then
  173. begin
  174. Result:=-1;
  175. Exit;
  176. end;
  177. With TOC do
  178. Result:=LastTrack-FirstTrack+1;
  179. finally
  180. CloseHandle(DriveHandle);
  181. end;
  182. end;
  183. { ---------------------------------------------------------------------
  184. 2. ASPI
  185. ---------------------------------------------------------------------}
  186. Function AspiGetNumAdapters : Integer;
  187. Var
  188. D : DWORD;
  189. Count, Status : Byte;
  190. begin
  191. d:= GetASPI32SupportInfo();
  192. Count:=D and $FF;
  193. Status:=(D shr 8) and $ff;
  194. if (Status<>SS_COMP) and (Status<>SS_NO_ADAPTERS) then
  195. Result:=-1
  196. else
  197. Result:=Count;
  198. end;
  199. Function DriveToSCSIParm (Device : String; Var HID,TGT,LUN : Byte) : Boolean;
  200. Var
  201. Code : Integer;
  202. begin
  203. Result:=False;
  204. Code:=Pos('[',Device);
  205. if Code<>0 then
  206. begin
  207. Delete(Device,1,Code);
  208. Code:=Pos(';',Device);
  209. HID:=StrToIntDef(Copy(Device,1,Code-1),-1);
  210. Result:=HID<>-1;
  211. If result then
  212. begin
  213. Delete(DEvice,1,Code);
  214. Code:=Pos(';',Device);
  215. Tgt:=StrToIntDef(Copy(Device,1,Code-1),-1);
  216. Result:=tgt<>-1;
  217. If result then
  218. begin
  219. Delete(DEvice,1,Code);
  220. Code:=Pos(']',Device);
  221. Lun:=StrToIntDef(Copy(Device,1,Code-1),-1);
  222. Result:=Lun<>-1;
  223. end;
  224. end;
  225. end;
  226. end;
  227. Var
  228. Atoc : TTOc;
  229. Function AspiReadTOC(Device : String; Var TOC : TTOC) : Integer;
  230. Var
  231. HAID,TGT,LUN : Byte;
  232. Status : DWord;
  233. S,T : SRB_ExecSCSICmd;
  234. HEvent : THANDLE;
  235. begin
  236. If Not DriveToSCSIParm(Device,HAID,TGT,lun) then
  237. begin
  238. Result:=-1;
  239. Exit;
  240. end;
  241. Writeln('About to read toc from ',haid,' ',tgt,' ',lun);
  242. hEvent:=CreateEvent( nil, TRUE, FALSE, nil );
  243. Writeln('Resetting event');
  244. ResetEvent(hEvent);
  245. Writeln('Reset event');
  246. Try
  247. FillChar(S,sizeof(s),0);
  248. s.SRB_Cmd := SC_EXEC_SCSI_CMD;
  249. s.SRB_HaID := HaID;
  250. s.SRB_Target := Tgt;
  251. s.SRB_Lun := lun;
  252. s.SRB_Flags := SRB_DIR_IN or SRB_EVENT_NOTIFY;
  253. s.SRB_BufLen := SizeOf(Toc);
  254. s.SRB_BufPointer := @TOC;
  255. s.SRB_SenseLen := SENSE_LEN;
  256. s.SRB_CDBLen := $0A;
  257. s.SRB_PostProc := LPVOID(hEvent);
  258. s.CDBByte[0] := SCSI_READ_TOC; // read TOC command
  259. s.CDBByte[1] := $02; // MSF mode
  260. s.CDBByte[7] := HiByte(Word(S.SRB_BufLen)); // high-order byte of buffer len
  261. s.CDBByte[8] := LoByte(Word(S.SRB_BUFLEN)); // low-order byte of buffer len
  262. Writeln('Sending Command');
  263. SendASPI32Command(LPSRB(@s));
  264. Writeln('Sent Command');
  265. Status:=S.SRB_STATUS;
  266. Writeln('Command status,',Status);
  267. if (Status=SS_PENDING ) then
  268. begin
  269. Writeln('Waiting for object');
  270. WaitForSingleObject( hEvent, 10000 ); // wait up to 10 secs
  271. Writeln('Waiting ended');
  272. end;
  273. Finally
  274. CloseHandle( hEvent );
  275. end;
  276. if (S.SRB_Status<>SS_COMP ) then
  277. begin
  278. Result:=-1;
  279. Exit;
  280. end;
  281. Writeln('Command completed');
  282. With TOC do
  283. Result:=LastTrack-FirstTrack+1;
  284. end;
  285. { ---------------------------------------------------------------------
  286. 3. IOCTL
  287. ---------------------------------------------------------------------}
  288. Function ioctlReadTOC(Device : String; Var TOC : TTOC) : Integer;
  289. Var
  290. DriveHandle : Thandle;
  291. Retval : Longint;
  292. Returned : DWord;
  293. Flags : Cardinal;
  294. begin
  295. Flags:=Cardinal(GENERIC_READ);
  296. device:=Upcase('\\.\'+device);
  297. DriveHandle:=CreateFile(PChar(Device), Flags,
  298. FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0 );
  299. if (DriveHandle = INVALID_HANDLE_VALUE) then
  300. begin
  301. result:=-1;
  302. exit;
  303. end;
  304. Try
  305. Returned := 0;
  306. FillChar(Toc, sizeof(TOC),0 );
  307. if Not DeviceIoControl(DriveHandle,
  308. IOCTL_CDROM_READ_TOC,
  309. Nil,
  310. 0,
  311. @TOC,
  312. sizeof(TTOC),
  313. @Returned,
  314. NiL) then
  315. begin
  316. Result:=-1;
  317. exit;
  318. end;
  319. With TOC do
  320. Result:=LastTrack-FirstTrack+1;
  321. Finally
  322. CloseHandle(DriveHandle);
  323. end;
  324. end;
  325. Function NtDriveInfo(CopyDrives : Boolean;Var CDDrives : Array of string): Integer;
  326. var
  327. I : Integer;
  328. Drives : Array[0..105] of char;
  329. P : PChar;
  330. begin
  331. FillChar(Drives,SizeOf(Drives),0);
  332. GetLogicalDriveStrings(105,Drives);
  333. P:=@Drives[0];
  334. Result:=0;
  335. While P[0]<>#0 do
  336. begin
  337. If GetDriveType(p)=DRIVE_CDROM then
  338. begin
  339. If CopyDrives and (Result<High(CDDrives)) then
  340. CDDrives[Result]:=Upcase(P[0])+':';
  341. Inc(Result);
  342. end;
  343. P:=P+Strlen(P)+1;
  344. end;
  345. end;
  346. Function NTGetNumDrives: Integer;
  347. Var A : Array[1..1] of string;
  348. begin
  349. Result:=NTDriveInfo(False,A);
  350. end;
  351. Function ioctlEnumDrives(Var Drives : Array of string) : Integer;
  352. begin
  353. result:=NTDriveInfo(True,Drives);
  354. end;
  355. { ---------------------------------------------------------------------
  356. 3. Generic
  357. ---------------------------------------------------------------------}
  358. Function ReadTOC(Device : String; Var TOC : TTOc) : Integer;
  359. begin
  360. Case CurrentAccessMethod of
  361. camNone : Result:=-1;
  362. camASPI : Result:=AspiReadTOC(Device,TOC);
  363. camSPTI : Result:=SptiReadTOC(Device,TOC);
  364. camIOCTL : Result:=IOCTLReadTOC(Device,TOC);
  365. end;
  366. end;
  367. Function GetCDAccessMethod : TCDAccessMethod;
  368. begin
  369. Result:=CurrentAccessMethod;
  370. end;
  371. Procedure SetCDAccessMethod (Value : TCDAccessMethod);
  372. begin
  373. CurrentAccessMethod:=Value;
  374. end;
  375. Function ASPIDriveInfo(CopyInfo : Boolean; Var Drives : Array of string) : Integer;
  376. var
  377. sh : SRB_HAInquiry;
  378. sd : SRB_GDEVBlock;
  379. numAdapters, maxTgt : Byte;
  380. i, j, k : byte;
  381. idx : Integer;
  382. begin
  383. Result:=0;
  384. numAdapters := AspiGetNumAdapters;
  385. if (numAdapters=0) then
  386. exit;
  387. For I:=0 to NumAdapters-1 do
  388. begin
  389. FillChar(sh,sizeof(sh),0);
  390. sh.SRB_Cmd := SC_HA_INQUIRY;
  391. sh.SRB_HaID := i;
  392. SendASPI32Command(LPSRB(@sh));
  393. if (sh.SRB_Status=SS_COMP) then
  394. begin
  395. maxTgt:=sh.HA_Unique[3];
  396. if (maxTgt=0) then
  397. maxTgt:=MAXTARG;
  398. For J:=0 to Maxtgt-1 do
  399. For k:=0 to MAXLUN-1 do
  400. begin
  401. FillChar(sd,sizeof(sd),0);
  402. sd.SRB_Cmd := SC_GET_DEV_TYPE;
  403. sd.SRB_HaID := i;
  404. sd.SRB_Target := j;
  405. sd.SRB_Lun := k;
  406. SendASPI32Command(LPSRB(@sd));
  407. if (sd.SRB_Status=SS_COMP) and
  408. (sd.SRB_DeviceType=DTYPE_CDROM) then
  409. begin
  410. If CopyInfo and (Result<High(Drives)) then
  411. Drives[Result]:=Format('ASPI[%d;%d;%d]',[I,J,K]);
  412. Inc(Result);
  413. end;
  414. end;
  415. end;
  416. end;
  417. end;
  418. Function ASPIGetNumDrives: Integer;
  419. Var
  420. A : Array[1..1] of string;
  421. begin
  422. Result:=AspiDriveInfo(False,A);
  423. end;
  424. Function GetNumDrives : Integer;
  425. begin
  426. If CurrenTAccessMethod=camASPI then
  427. Result:=AspiGetNumDrives
  428. else
  429. Result:=NTGetNumDrives;
  430. end;
  431. Function EnumCDDrives(Var Drives : Array of String) : Integer;
  432. begin
  433. If CurrenTAccessMethod=camASPI then
  434. Result:=AspiDriveInfo(True,Drives)
  435. else
  436. Result:=ioctlEnumDrives(Drives);
  437. end;
  438. Initialization
  439. InitWinCD;
  440. end.