wincd.pp 12 KB

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