123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 |
- {
- }
- unit wincd;
- {$mode objfpc}
- {$h+}
- interface
- uses Windows,SysUtils;
- Type
- TCDAccessMethod = (camNone,camASPI,camSPTI,camIOCTL);
- {$packrecords c}
- TTOCTrack = packed record
- rsvd,
- ADR,
- trackNumber,
- rsvd2 : Byte;
- addr : Array[0..3] of byte;
- end;
- TTOC = packed Record
- toclen : word;
- firsttrack,
- lastTrack : byte;
- toctrack: Array[0..99] of TTocTrack;
- end;
- Const
- AccessMethodNames : Array[TCDAccessMethod] of string
- = ('None','ASPI','SPTI','IOCTL');
- Function GetCDAccessMethod : TCDAccessMethod;
- Procedure SetCDAccessMethod (Value : TCDAccessMethod);
- Function ReadTOC(Device : String; Var TOC : TTOc) : Integer;
- Function EnumCDDrives(Var Drives : Array of String) : Integer;
- Function GetNumDrives : Integer;
- implementation
- uses cdromioctl,wnaspi32,scsidefs;
- Var
- CurrentAccessMethod : TCDAccessMethod;
- CDOSVer : Integer;
- { ---------------------------------------------------------------------
- SPTI Defines.
- ---------------------------------------------------------------------}
- Type
- {$packrecords C}
- SCSI_PASS_THROUGH = record
- Length : USHORT;
- ScsiStatus : UCHAR;
- PathId : UCHAR;
- TargetId : UCHAR;
- Lun : UCHAR;
- CdbLength : UCHAR;
- SenseInfoLength : UCHAR;
- DataIn : UCHAR;
- DataTransferLength : ULONG;
- TimeOutValue : ULONG;
- DataBufferOffset : ULONG;
- SenseInfoOffset : ULONG;
- Cdb : array[0..15] of UCHAR;
- end;
- TSCSI_PASS_THROUGH = SCSI_PASS_THROUGH;
- PSCSI_PASS_THROUGH = ^TSCSI_PASS_THROUGH;
- SCSI_PASS_THROUGH_DIRECT = record
- Length : USHORT;
- ScsiStatus : UCHAR;
- PathId : UCHAR;
- TargetId : UCHAR;
- Lun : UCHAR;
- CdbLength : UCHAR;
- SenseInfoLength : UCHAR;
- DataIn : UCHAR;
- DataTransferLength : ULONG;
- TimeOutValue : ULONG;
- DataBuffer : PVOID;
- SenseInfoOffset : ULONG;
- Cdb : array[0..15] of UCHAR;
- end;
- TSCSI_PASS_THROUGH_DIRECT = SCSI_PASS_THROUGH_DIRECT;
- PSCSI_PASS_THROUGH_DIRECT = ^SCSI_PASS_THROUGH_DIRECT;
- SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER = record
- spt : SCSI_PASS_THROUGH_DIRECT;
- Filler : ULONG;
- ucSenseBuf : array[0..31] of UCHAR;
- end;
- TSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER = SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
- PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER = ^SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
- const
- METHOD_BUFFERED = 0;
- METHOD_IN_DIRECT = 1;
- METHOD_OUT_DIRECT = 2;
- METHOD_NEITHER = 3;
- FILE_ANY_ACCESS = 0;
- FILE_READ_ACCESS = $0001;
- FILE_WRITE_ACCESS = $0002;
- IOCTL_CDROM_BASE = $00000002;
- IOCTL_SCSI_BASE = $00000004;
- SCSI_IOCTL_DATA_OUT = 0;
- SCSI_IOCTL_DATA_IN = 1;
- SCSI_IOCTL_DATA_UNSPECIFIED = 2;
- { ---------------------------------------------------------------------
- Initialization code.
- ---------------------------------------------------------------------}
- procedure InitWinCD;
- Var
- TheCDOSVER : TOSVersionInfo;
- begin
- TheCDOSVer.dwOSVersionInfoSize:=SizeOf(TheCDOSver);
- GetVersionEx(TheCDOSVer);
- CDOSVer:=TheCDOSVer.dwMajorVersion;
- If AspiLoaded then
- CurrentAccessMethod := camASPI
- else
- begin
- if (CDOSver<1) then
- CurrentAccessMethod := camNone
- else
- {
- It is better to use SPTI on windows, but the problem with that
- is that administrative priviledges are needed. A detection
- algorithm for these priviledges here would be nice.
- }
- CurrentAccessMethod := camSPTI;
- end;
- end;
- { ---------------------------------------------------------------------
- Actual reading of table of contents.
- ---------------------------------------------------------------------}
- { ---------------------------------------------------------------------
- 1. SPTI
- ---------------------------------------------------------------------}
- Function sptiReadTOC(Device : String; var TOC: TToC) : Integer;
- Var
- DriveHandle : THandle;
- len : Cardinal;
- buf : Array[0..31] of char;
- ID,retVal : Integer;
- Returned,Flags : Cardinal;
- swb : TSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
- begin
- Flags := Cardinal(GENERIC_READ);
- if (CDOSVer>4) then
- Flags:=Flags or Cardinal(GENERIC_WRITE);
- Device:=Upcase('\\.\'+Device);
- DriveHandle:=CreateFile(pchar(Device),Flags,FILE_SHARE_READ,
- nil,OPEN_EXISTING, 0, 0 );
- if (DriveHandle=INVALID_HANDLE_VALUE) then
- begin
- Result:=-1;
- Exit;
- end;
- Try
- Returned:= 0;
- len:= sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
- FillChar(swb, len ,0);
- With swb.spt do
- begin
- Length := sizeof(swb.spt); // SCSI_PASS_THROUGH);
- CdbLength := 10;
- DataIn := SCSI_IOCTL_DATA_IN;
- DataTransferLength := SizeOf(tOC);
- TimeOutValue := 5;
- DataBuffer := @TOC;
- SenseInfoOffset := @swb.ucSenseBuf[0]-pbyte(@swb.spt);
- Cdb[0] := $43; // read TOC
- Cdb[1] := $02; // MSF mode
- Cdb[7] := $03;
- Cdb[8] := $24;
- end;
- if (Not DeviceIoControl(DriveHandle,
- IOCTL_SCSI_PASS_THROUGH_DIRECT,
- @swb,
- len,
- @swb,
- len,
- @Returned,
- Nil)) then
- begin
- Result:=-1;
- Exit;
- end;
- With TOC do
- Result:=LastTrack-FirstTrack+1;
- finally
- CloseHandle(DriveHandle);
- end;
- end;
- { ---------------------------------------------------------------------
- 2. ASPI
- ---------------------------------------------------------------------}
- Function AspiGetNumAdapters : Integer;
- Var
- D : DWORD;
- Count, Status : Byte;
- begin
- d:= GetASPI32SupportInfo();
- Count:=D and $FF;
- Status:=(D shr 8) and $ff;
- if (Status<>SS_COMP) and (Status<>SS_NO_ADAPTERS) then
- Result:=-1
- else
- Result:=Count;
- end;
- Function DriveToSCSIParm (Device : String; Var HID,TGT,LUN : Byte) : Boolean;
- Var
- Code : Integer;
- begin
- Result:=False;
- Code:=Pos('[',Device);
- if Code<>0 then
- begin
- Delete(Device,1,Code);
- Code:=Pos(';',Device);
- HID:=StrToIntDef(Copy(Device,1,Code-1),-1);
- Result:=HID<>-1;
- If result then
- begin
- Delete(DEvice,1,Code);
- Code:=Pos(';',Device);
- Tgt:=StrToIntDef(Copy(Device,1,Code-1),-1);
- Result:=tgt<>-1;
- If result then
- begin
- Delete(DEvice,1,Code);
- Code:=Pos(']',Device);
- Lun:=StrToIntDef(Copy(Device,1,Code-1),-1);
- Result:=Lun<>-1;
- end;
- end;
- end;
- end;
- Var
- Atoc : TTOc;
- Function AspiReadTOC(Device : String; Var TOC : TTOC) : Integer;
- Var
- HAID,TGT,LUN : Byte;
- Status : DWord;
- S,T : SRB_ExecSCSICmd;
- HEvent : THANDLE;
- begin
- If Not DriveToSCSIParm(Device,HAID,TGT,lun) then
- begin
- Result:=-1;
- Exit;
- end;
- Writeln('About to read toc from ',haid,' ',tgt,' ',lun);
- hEvent:=CreateEvent( nil, TRUE, FALSE, nil );
- Writeln('Resetting event');
- ResetEvent(hEvent);
- Writeln('Reset event');
- Try
- FillChar(S,sizeof(s),0);
- s.SRB_Cmd := SC_EXEC_SCSI_CMD;
- s.SRB_HaID := HaID;
- s.SRB_Target := Tgt;
- s.SRB_Lun := lun;
- s.SRB_Flags := SRB_DIR_IN or SRB_EVENT_NOTIFY;
- s.SRB_BufLen := SizeOf(Toc);
- s.SRB_BufPointer := @TOC;
- s.SRB_SenseLen := SENSE_LEN;
- s.SRB_CDBLen := $0A;
- s.SRB_PostProc := LPVOID(hEvent);
- s.CDBByte[0] := SCSI_READ_TOC; // read TOC command
- s.CDBByte[1] := $02; // MSF mode
- s.CDBByte[7] := HiByte(Word(S.SRB_BufLen)); // high-order byte of buffer len
- s.CDBByte[8] := LoByte(Word(S.SRB_BUFLEN)); // low-order byte of buffer len
- Writeln('Sending Command');
- SendASPI32Command(LPSRB(@s));
- Writeln('Sent Command');
- Status:=S.SRB_STATUS;
- Writeln('Command status,',Status);
- if (Status=SS_PENDING ) then
- begin
- Writeln('Waiting for object');
- WaitForSingleObject( hEvent, 10000 ); // wait up to 10 secs
- Writeln('Waiting ended');
- end;
- Finally
- CloseHandle( hEvent );
- end;
- if (S.SRB_Status<>SS_COMP ) then
- begin
- Result:=-1;
- Exit;
- end;
- Writeln('Command completed');
- With TOC do
- Result:=LastTrack-FirstTrack+1;
- end;
- { ---------------------------------------------------------------------
- 3. IOCTL
- ---------------------------------------------------------------------}
- Function ioctlReadTOC(Device : String; Var TOC : TTOC) : Integer;
- Var
- DriveHandle : Thandle;
- Retval : Longint;
- Returned : DWord;
- Flags : Cardinal;
- begin
- Flags:=Cardinal(GENERIC_READ);
- device:=Upcase('\\.\'+device);
- DriveHandle:=CreateFile(PChar(Device), Flags,
- FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0 );
- if (DriveHandle = INVALID_HANDLE_VALUE) then
- begin
- result:=-1;
- exit;
- end;
- Try
- Returned := 0;
- FillChar(Toc, sizeof(TOC),0 );
- if Not DeviceIoControl(DriveHandle,
- IOCTL_CDROM_READ_TOC,
- Nil,
- 0,
- @TOC,
- sizeof(TTOC),
- @Returned,
- NiL) then
- begin
- Result:=-1;
- exit;
- end;
- With TOC do
- Result:=LastTrack-FirstTrack+1;
- Finally
- CloseHandle(DriveHandle);
- end;
- end;
- Function NtDriveInfo(CopyDrives : Boolean;Var CDDrives : Array of string): Integer;
- var
- I : Integer;
- Drives : Array[0..105] of char;
- P : PChar;
- begin
- FillChar(Drives,SizeOf(Drives),0);
- GetLogicalDriveStrings(105,Drives);
- P:=@Drives[0];
- Result:=0;
- While P[0]<>#0 do
- begin
- If GetDriveType(p)=DRIVE_CDROM then
- begin
- If CopyDrives and (Result<High(CDDrives)) then
- CDDrives[Result]:=Upcase(P[0])+':';
- Inc(Result);
- end;
- P:=P+Strlen(P)+1;
- end;
- end;
- Function NTGetNumDrives: Integer;
- Var A : Array[1..1] of string;
- begin
- Result:=NTDriveInfo(False,A);
- end;
- Function ioctlEnumDrives(Var Drives : Array of string) : Integer;
- begin
- result:=NTDriveInfo(True,Drives);
- end;
- { ---------------------------------------------------------------------
- 3. Generic
- ---------------------------------------------------------------------}
- Function ReadTOC(Device : String; Var TOC : TTOc) : Integer;
- begin
- Case CurrentAccessMethod of
- camNone : Result:=-1;
- camASPI : Result:=AspiReadTOC(Device,TOC);
- camSPTI : Result:=SptiReadTOC(Device,TOC);
- camIOCTL : Result:=IOCTLReadTOC(Device,TOC);
- end;
- end;
- Function GetCDAccessMethod : TCDAccessMethod;
- begin
- Result:=CurrentAccessMethod;
- end;
- Procedure SetCDAccessMethod (Value : TCDAccessMethod);
- begin
- CurrentAccessMethod:=Value;
- end;
- Function ASPIDriveInfo(CopyInfo : Boolean; Var Drives : Array of string) : Integer;
- var
- sh : SRB_HAInquiry;
- sd : SRB_GDEVBlock;
- numAdapters, maxTgt : Byte;
- i, j, k : byte;
- idx : Integer;
- begin
- Result:=0;
- numAdapters := AspiGetNumAdapters;
- if (numAdapters=0) then
- exit;
- For I:=0 to NumAdapters-1 do
- begin
- FillChar(sh,sizeof(sh),0);
- sh.SRB_Cmd := SC_HA_INQUIRY;
- sh.SRB_HaID := i;
- SendASPI32Command(LPSRB(@sh));
- if (sh.SRB_Status=SS_COMP) then
- begin
- maxTgt:=sh.HA_Unique[3];
- if (maxTgt=0) then
- maxTgt:=MAXTARG;
- For J:=0 to Maxtgt-1 do
- For k:=0 to MAXLUN-1 do
- begin
- FillChar(sd,sizeof(sd),0);
- sd.SRB_Cmd := SC_GET_DEV_TYPE;
- sd.SRB_HaID := i;
- sd.SRB_Target := j;
- sd.SRB_Lun := k;
- SendASPI32Command(LPSRB(@sd));
- if (sd.SRB_Status=SS_COMP) and
- (sd.SRB_DeviceType=DTYPE_CDROM) then
- begin
- If CopyInfo and (Result<High(Drives)) then
- Drives[Result]:=Format('ASPI[%d;%d;%d]',[I,J,K]);
- Inc(Result);
- end;
- end;
- end;
- end;
- end;
- Function ASPIGetNumDrives: Integer;
- Var
- A : Array[1..1] of string;
- begin
- Result:=AspiDriveInfo(False,A);
- end;
- Function GetNumDrives : Integer;
- begin
- If CurrenTAccessMethod=camASPI then
- Result:=AspiGetNumDrives
- else
- Result:=NTGetNumDrives;
- end;
- Function EnumCDDrives(Var Drives : Array of String) : Integer;
- begin
- If CurrenTAccessMethod=camASPI then
- Result:=AspiDriveInfo(True,Drives)
- else
- Result:=ioctlEnumDrives(Drives);
- end;
- Initialization
- InitWinCD;
- end.
|