TCDBASIC.PAS
资源名称:tcdrom.zip [点击查看]
上传用户:jzxjwgb
上传日期:2007-01-06
资源大小:64k
文件大小:76k
源码类别:
SCSI/ASPI
开发平台:
Delphi
- unit TCDBASIC;
- // -----------------------------------------------------------------------------
- // Project: CD-ROM and CD-audio Interface Components
- // Component Names: TCdRom
- // Module: TCdBasic
- // Description: Implements ASPI/SCSI primitive interface
- // to CD-ROM and CD-Audio devices.
- // Version: 0.2 early beta
- // Date: 25-MAY-1999
- // Target: Win32, Delphi3
- // Authors: Sergey "KSER" Kabikov, iamkser@hotmail.com
- // Copyright (c) 1999 Sergey Kabikov
- // -----------------------------------------------------------------------------
- // -----------------------------------------------------------------------------
- // Acknowledgements:
- // 1. Thanks to Jay A. Key (scsiprog@geocities.com) for his "akrip" program,
- // which sources (http://www.geocities.com/SiliconValley/Byte/7125/)
- // helps to solve many problems.
- // -----------------------------------------------------------------------------
- { 1. Full list of mandatory SCSI-2 commands ('+' is release mark)
- Comment line is brief description if released or reasons why don't.
- + 00h TEST UNIT READY ; checks if the LUN (logical unit) is ready.
- + 03h REQUEST SENSE ; requests info about results of last SCSI operation.
- + 12h INQUIRY ; requests some parameters of the target.
- + 16h RESERVE ; reserves the entire LUN for the exclusive use.
- + 17h RELEASE ; cancels RESERVE command effects.
- + 1Dh SelfTest ; requests the target to perform self-test.
- + 25h READ CAPACITY ; returns logical block address (LBA) and block length
- ; of the last valid block for seek operations.
- + 28h READ(10) ; requests data from the target.
- 2. Full list of optional SCSI-2 commands ('+' is release mark)
- + 01h REZERO UNIT ; positions the actuator at cylinder 0
- - 08h READ(6) ; REPLACED BY READ(10)
- - 0Bh SEEK(6) ; REPLACED BY SEEK(10)
- - 15h MODE SELECT(6) ; REPLACED BY MODE SELECT(10)
- - 18h COPY ; RARE NEED/RARE EXISTS
- - 1Ah MODE SENSE(6) ; REPLACED BY MODE SENSE(10)
- + 1Bh START STOP UNIT ; controls for media access. May also eject CD.
- - 1Ch RECEIVE DIAGNOSTIC RESULTS ; The only non-vendor-specific diagnostic
- ; page is a list-of-supported-pages.
- - 1Dh SEND DIAGNOSTIC ; Partial release - see SelfTest above.
- + 1Eh PREVENT/ALLOW MEDIUM REMOVAL
- - A8h READ(12) ; We don't need to read >128MB in one piece...
- + 2Bh SEEK(10) ; requests LUN seek to the specified LBA.
- - 2Fh VERIFY(10) ; RARE NEED/RARE EXISTS
- - Afh VERIFY(12) ; RARE NEED/RARE EXISTS
- - 30h/31h/32h SEARCH DATA HIGH/EQUAL/LOW(10) ; RARE NEED/RARE EXISTS
- - B0h/B1h/B2h SEARCH DATA HIGH/EQUAL/LOW(12) ; RARE NEED/RARE EXISTS
- - 33h SET LIMITS(10) ; FOR LINKED COMMANDS ONLY
- - B3h SET LIMITS(12) ; FOR LINKED COMMANDS ONLY
- + 34h PRE-FETCH ; target must transfer the LBs to the cache memory.
- + 35h SYNCHRONIZE CACHE ; forces all delayed writes from cache to medium.
- + 36h LOCK/UNLOCK CACHE ; target disallows/allows LBs within the specified
- ; range to be removed from the cache memory.
- - 39h COMPARE ; RARE NEED/RARE EXISTS
- - 3Ah COPY AND VERIFY ; RARE NEED/RARE EXISTS
- - 3Bh WRITE BUFFER ; The device's buffer I/O without medium access.
- - 3Ch READ BUFFER ;/ Not-so-useful ?
- + 3Eh READ LONG ; Reads full sector, including ECC bytes
- - 40h CHANGE DEFINITION ; RARE NEED/RARE EXISTS
- + 42h READ SUB-CHANNEL ; Read special info from current sector [partial]
- + 43h READ TOC ; Reads CD's Table Of Content (tracks list)
- + 44h READ HEADER ; Reads LB's header: abs.address and CD-ROM mode
- - 4Ch LOG SELECT ; Statistics request commands (not for audio)
- - 4Dh LOG SENSE ;/ Not-so-useful ?
- + 55h MODE SELECT(10) ; Changes CD-ROM mode parameters.
- + 5Ah MODE SENSE(10) ; A means for a CD-ROM to report its parameters.
- }
- interface
- uses Windows, TASPIdev;
- type
- { =============== CD SUB-CHANNEL Q DATA DEFINITIONS =============== }
- TAudioStatus = (AudioNone, AudioPlay, AudioPause, AudioComplete,
- AudioError, AudioStop, AudioInvalid);
- {Status| Description |
- |------+------------------------------------------------|
- | 0 | Audio status byte not supported or not valid |
- | 1 | Audio play operation in progress |
- | 2 | Audio play operation paused |
- | 3 | Audio play operation successfully completed |
- | 4 | Audio play operation stopped due to error |
- | 5 | No current audio status to return }
- { Audio status values 3 and 4 return information on previous
- audio operations; they are returned only once after the
- condition has occurred. If another audio play operation is
- not requested, the audio status returned for subsequent
- READ SUB-CHANNEL commands is 5. }
- var
- TAudioStatusName : array[TAudioStatus] of string = ('None', 'Play',
- 'Pause', 'Complete', 'Error', 'Stop', 'Invalid');
- type
- TsubchannelADRinfo = (ADRnone, ADRposition, ADRcatalogue,
- ADRISRC, ADRreserved);
- {ADR code| Sub-channel Q encoded information |
- |--------+---------------------------------------|
- | 0h | Mode information not supplied |
- | 1h | Current position data ( track, index, |
- | | absolute and relative addresses) |
- | 2h | Media catalogue number |
- | 3h | ISRC |
- | 4h-Fh | Reserved }
- var
- TsubchannelADRname : array[TsubchannelADRinfo] of string = ('None',
- 'Position', 'Catalogue', 'ISRC', 'Reserved');
- type
- TCdRomSubQinfo = packed record
- AudioStatus : TAudioStatus;
- ADR : TsubchannelADRinfo;
- PreEmphasis : BOOLEAN; // TRUE=Audio track with pre-emphasis
- CopyPermit : BOOLEAN; // TRUE=Digital copy permitted
- // FALSE=Digital copy prohibited
- DataTrack : BOOLEAN; // TRUE=Data track, FALSE=Audio track
- QuadAudio : BOOLEAN; // TRUE=Four-channel audio
- // FALSE=Two-channel audio
- TrackNumber : byte; // Number (0..99d) of current track
- IndexNumber : byte; // Index (0..99d) of part of the track
- UPC : string[16]; // identifying number of this media
- // according to the uniform product code
- // values (UPC/EAN bar coding) in ASCII.
- ISRC : string[16]; // identifying number of this media
- // according to the ISRC standards
- // (DIN-31-621) expressed in ASCII.
- case BOOLEAN of // Address of current location: absolute
- // and relative to current track beginning
- TRUE : (AbsAddress, // in LBA format
- RelAddress : DWORD);
- FALSE: (AbsAddressF, // in MSF format : Frames (0..74d),
- AbsAddressS, // Seconds (0..59d) and
- AbsAddressM, // Minutes (0..74d)
- AbsAddressR, // And reserved byte field in MSB
- RelAddressF,
- RelAddressS,
- RelAddressM,
- RelAddressR : BYTE);
- end;
- { =============== CD TABLE-OF-CONTENT DEFINITION =============== }
- TCdRomTrackDescriptor = packed record
- TrackNumber : byte; // Track no 0AAh is lead-out area
- ADR : TsubchannelADRinfo;
- PreEmphasis : BOOLEAN; // TRUE=Audio track with pre-emphasis
- CopyPermit : BOOLEAN; // TRUE=Digital copy permitted
- // FALSE=Digital copy prohibited
- DataTrack : BOOLEAN; // TRUE=Data track, FALSE=Audio track
- QuadAudio : BOOLEAN; // TRUE=Four-channel audio
- // FALSE=Two-channel audio
- case BOOLEAN of // Absolute linear address of track
- TRUE : (Address : DWORD); // in LBA format
- FALSE: (AddressF, // in MSF format : Frames (0..74d),
- AddressS, // Seconds (0..59d) and
- AddressM, // Minutes (0..74d)
- AddressR : BYTE); // And reserved byte field in MSB
- end;
- TCdRomToc = packed record
- Track : array[0..99] of TCdRomTrackDescriptor;
- FirstTrack : integer;
- LastTrack : integer;
- TrackCount : integer; // Total tracks amount
- InMSF : BOOLEAN; // Track[i].Address format:
- // TRUE=MSF, FALSE=LBA
- end;
- { ========== CD-ROM MODE SELECT/SENSE DEFINITIONS =========== }
- TCdRomModePageType = (MPTcurrent, MPTchangeable, MPTdefault, MPTsaved);
- var
- TCdRomModePageTypeName : array[TCdRomModePageType] of string =
- ('MPTcurrent', 'MPTchangeable', 'MPTdefault', 'MPTsaved');
- type
- TCdRomMediumType = (CDTdefault, CDTdata, CDTaudio, CDTmixed,
- CDTreserved, CDTdata80, CDTaudio80, CDTmixed80, CDTnone);
- { 'CDTmixed' means that medium may contain data and audio tracks,
- 'CDTdefault' - that device supports only one medium type (??),
- suffix '80' - that media is a 80 mm diameter disk instead the
- default 120 mm. }
- var
- TCdRomMediumName : array[TCdRomMediumType] of string = ('Default',
- 'CD-ROM 5"', 'CD-Audio 5"', 'CD-XA 5"', 'Unknown',
- 'CD-ROM 3"', 'CD-Audio 3"', 'CD-XA 3"', 'None');
- type
- TCdRomBlockDescriptor = record
- Density : BYTE; // type of physical sectors on CD (see below)
- BlkCount : DWORD; // amount of sectors with declared size
- BlkSize : DWORD; // physical sector size in bytes
- end;
- { CD-ROM density codes:
- 00h = Default density code (the only density supported by target)
- 01h = CD-ROM mode 1 - User data only (2048 bytes per physical sector)
- 02h = CD-ROM mode 2 - User data plus auxiliary data field (2336 bytes)
- 03h = 4-byte tag field, user data plus auxiliary data (2340 bytes)
- 04h = CD-Audio mode - 1/75th of a second per logical block
- values 05h-7Fh are reserved, 80h-FFh - vendor-specific. }
- TCdRomModeHeader = record
- Medium : TCdRomMediumType; // see descriptions above
- DPOFUA : BOOLEAN; // DPO/FUA (see SCSIread10) valid if TRUE
- BDlength : BYTE; // real number of records in BD field
- BD : array[0..99] of TCdRomBlockDescriptor;
- end;
- TCdRomModePageAudio = record
- PSAV, // TRUE means parameter saving capability
- IMM, // TRUE enables background audio playback
- SOTC, // TRUE enables multi-track audio playback
- APRV : BOOLEAN; // LBAformat and LBSaudio are valid if TRUE
- LBAformat : BYTE; // LBSaudio fractional coeff.index (1 or 1/256)
- LBSaudio : WORD; // LBAs per second in audio mode
- Volume, // volume for indexed target's audio output
- // port. 00h is silence, FFh is maximum.
- Channel : // set of disc's audio channels, connected to
- array[0..3] of BYTE; // indexed output port (ORed bitset 00h..0Fh)
- end;
- { ================ CD-ROM CLASS DEFINITION ================= }
- TCdRom = class(TASPIDevice)
- private
- fLocked : BOOLEAN;
- protected
- procedure SetLockUnlock(Value : boolean);
- procedure ChangeDevice(Value : TdeviceID); override;
- public
- constructor Create(AOwner : TObject);
- destructor Destroy; override;
- {============== MANDATORY SCSI-II COMMANDS ============}
- function SCSItestReady : BOOLEAN; // True if READY
- function SCSIrequestSense(Pbuf : pointer; BufLen : BYTE) : BOOLEAN;
- function SCSIinquiry(Vital : BOOLEAN; CodePage : BYTE;
- Pbuf : pointer; BufLen : BYTE) : BOOLEAN;
- function SCSIreserve : BOOLEAN;
- function SCSIrelease : BOOLEAN;
- function SCSIreserve3rdParty(TrdPartyDeviceID: BYTE) : BOOLEAN;
- function SCSIrelease3rdParty(TrdPartyDeviceID: BYTE) : BOOLEAN;
- function SCSIselfTest(DOFF, UOFF : BOOLEAN) : BOOLEAN;
- function SCSIreadCapacity(var BlkCount, BlkSize : DWORD) : BOOLEAN;
- function SCSIreadCapacityPM(Partition : WORD; GLBA : DWORD;
- var BlkCount, BlkSize : DWORD) : BOOLEAN;
- function SCSIread10(DPO,FUA: boolean; GLBA: DWORD; Sectors: WORD;
- Buf : pointer; BufLen : DWORD) : BOOLEAN;
- {============== OPTIONAL SCSI-II COMMANDS =============}
- function SCSIrezeroUnit : BOOLEAN;
- function SCSIstartStopUnit(STRT, LOEJ, IMM : boolean) : BOOLEAN;
- function SCSIseek10(GLBA : DWORD) : BOOLEAN;
- function SCSIpreFetch(IMM : BOOLEAN;
- GLBA : DWORD; Sectors : WORD) : BOOLEAN;
- function SCSIsynchronizeCache(IMM : BOOLEAN;
- GLBA : DWORD; Sectors : WORD) : BOOLEAN;
- function SCSIlockUnlockCache(LOK : boolean;
- GLBA : DWORD; Sectors : WORD) : BOOLEAN;
- function SCSIreadLong(CORR : BOOLEAN; GLBA : DWORD;
- Buf : pointer; BufLen : DWORD) : BOOLEAN;
- function SCSIreadSubchannel(MSFform : BOOLEAN;
- var Info : TCdRomSubQinfo) : BOOLEAN;
- function SCSIreadToc(MSFform : BOOLEAN; Start : BYTE;
- var Toc : TCdRomToc) : BOOLEAN;
- function SCSIreadHeaderLBA(GLBA : DWORD;
- var CDmode : BYTE; var Address : DWORD) : BOOLEAN;
- function SCSIreadHeaderMSF(GLBA : DWORD;
- var CDmode, AddressM, AddressS, AddressF : BYTE) : BOOLEAN;
- function SCSImodeSelectEX(PF, SP : BOOLEAN;
- Buf : pointer; BufLen : DWORD) : BOOLEAN;
- function SCSImodeSelect(Buf : pointer; BufLen : DWORD) : BOOLEAN;
- function SCSImodeSense(DBD : BOOLEAN; PCTL : TCdRomModePageType;
- PAGE : BYTE; Buf : pointer; BufLen : DWORD) : BOOLEAN;
- // The function set below have to describe all possible MODE SELECT
- // and MODE SENSE implementations for various mode pages. Almost
- // each function is in two forms - full-featured with 'EX' suffix
- // in name, and shortened-form (without any suffixes).
- // For now, the only MODE SENSE is implemented. It is necessary to
- // manipulate with mode parameters highly carefull. I'm not ready
- // yet. That's why.
- // Unimplemented mode pages are:
- // 02h Disconnect-reconnect page
- // 09h Peripheral device page > unuseful with ATAPI devices
- // 0Ah Control mode page /
- // 07h Verify error recovery page - verify command are not used
- // 08h Caching page - do you need to control CD-ROM's cache?
- function SCSImodeSenseHeader(var sh : TCdRomModeHeader) : BOOLEAN;
- function SCSImodeSenseRecoverEX(PCTL: TCdRomModePageType;
- var PSAV : BOOLEAN; var RLEV, RETR : BYTE) : BOOLEAN;
- function SCSImodeSenseRecover(var RLEV, RETR : BYTE) : BOOLEAN;
- function SCSImodeSenseMediumEX(PCTL : TCdRomModePageType;
- var PSAV : BOOLEAN;
- var Med1,Med2,Med3,Med4 : TCdRomMediumType) : BOOLEAN;
- function SCSImodeSenseDeviceEX(PCTL : TCdRomModePageType;
- var PSAV : BOOLEAN; var ITimer : BYTE;
- var SperMunits, FperSunits : WORD) : BOOLEAN;
- function SCSImodeSenseDevice(var ITimer : BYTE) : BOOLEAN;
- function SCSImodeSenseAudioEX(PCTL : TCdRomModePageType;
- var sh : TCdRomModePageAudio) : BOOLEAN;
- function SCSImodeSenseAudio(var sh: TCdRomModePageAudio) : BOOLEAN;
- {============== OPTIONAL AUDIO PLAYBACK COMMANDS =============}
- function SCSIplayAudio10(GLBA : DWORD; Sectors : WORD) : BOOLEAN;
- function SCSIplayAudio12(GLBA, Sectors : DWORD) : BOOLEAN;
- function SCSIplayAudioMSF(StartM, StartS, StartF,
- StopM, StopS, StopF : BYTE ) : BOOLEAN;
- function SCSIplayAudioTI(StartTrack, StartIndex,
- StopTrack, StopIndex : BYTE ) : BOOLEAN;
- function SCSIplayAudioR10(TRLBA : DWORD;
- StartTrack : BYTE; Sectors : WORD) : BOOLEAN;
- function SCSIplayAudioR12(TRLBA : DWORD;
- StartTrack : BYTE; Sectors : DWORD) : BOOLEAN;
- function SCSIpauseAudio : BOOLEAN;
- function SCSIresumeAudio : BOOLEAN;
- property Locked : BOOLEAN read fLocked write SetLockUnlock;
- end;
- implementation
- constructor TCdRom.Create;
- begin
- inherited Create(AOwner);
- fLocked := FALSE;
- end;
- destructor TCdRom.Destroy;
- begin
- SetLockUnlock(FALSE); // Unlock the medium
- SCSIlockUnlockCache(FALSE, 0, 0); // Unlock entire cache
- inherited Destroy;
- end;
- procedure TCdRom.ChangeDevice(Value : TdeviceID);
- begin
- SetLockUnlock(FALSE); // Unlock the medium
- SCSIlockUnlockCache(FALSE, 0, 0); // Unlock entire cache
- inherited ChangeDevice(Value);
- end;
- {======================= MANDATORY SCSI-II COMMANDS ====================}
- { TEST UNIT READY command provides a means to check if the logical unit
- is ready. This is not a request for a self-test. It is especially
- useful to check cartridge status of logical units with removable media.
- ===================== Possible LastError codes: =========================
- |Status|Sense key| ASC and ASCQ |LastError code(Err_..)
- |------+---------+------------------------------------+---------------------|
- | GOOD | --- | --- | None |
- | BUSY | --- | --- | TargetBusy |
- | RESERVATION CONFLICT --- | ReservationConflict |
- |------+---------+------------------------------------+---------------------|
- | CHECK| ILLEGAL | LOGICAL UNIT NOT SUPPORTED | SenseIllegalRequest |
- |CONDI-| REQUEST | | |
- | TION ----------+------------------------------------+---------------------|
- | | NOT | LUN DOES NOT RESPOND TO SELECTION | SenseNotReady |
- | | READY | MEDIUM NOT PRESENT | |
- | | | LUN IS IN PROCESS OF BECOMING READY| |
- | | | INIT COMMAND REQUIRED | |
- | | | MANUAL INTERVENTION REQUIRED | |
- | | | FORMAT IS IN PROGRESS | |
- | | | LUN NOT READY,CAUSE NOT REPORTABLE | |
- |------+---------+------------------------------------+---------------------|}
- function TCdRom.SCSItestReady : BOOLEAN; //True if READY
- begin result := ASPIsend6(0, 0, 0, NIL, 0, SRB_NODIR, ShortTimeout); end;
- { REQUEST SENSE command (code 03h) is fully implemented in ASPI layer.
- If still need it, you got it below. }
- function TCdRom.SCSIrequestSense(Pbuf : pointer; BufLen : BYTE) : BOOLEAN;
- var I : DWORD;
- begin
- I := DWORD(BufLen) AND $FF;
- result := ASPIsend6(3, 0, BufLen, PBuf, I, SRB_NODIR, ShortTimeout);
- end;
- { INQUIRY command requests some parameters of the target and its attached
- peripheral device(s). In the common case it seems like below.
- More practical styled call is GetDeviceInfo included into TASPIdevice. }
- function TCdRom.SCSIinquiry(Vital : BOOLEAN; CodePage : BYTE;
- Pbuf : pointer; BufLen : BYTE) : BOOLEAN;
- var I, J : DWORD;
- begin
- J := DWORD(CodePage) AND $FF;
- if Vital then J := (J OR $100) SHL 8 else J := 0;
- I := DWORD(BufLen) AND $FF;
- result := ASPIsend6($12, J, BufLen, PBuf, I, SRB_NODIR, ShortTimeout);
- end;
- { RESERVE and RELEASE commands define how different types of
- restricted access may be achieved, and to whom the access is restricted.
- Use reservations to gain a level of exclusivity in access to all or part
- of the medium. It is expected that the reservation will be retained until
- released. Partial medium reservation is excluded from this implementation
- because it's not-so-useful and optional in SCSI-2 terms.
- RESERVE requests that the entire logical unit be reserved for the
- exclusive use until released by a RELEASE command or by a hard/soft RESET
- condition. A reservation is not granted if the logical unit is reserved
- by another task. If, after honouring the reservation, any other initiator
- attempts to perform any command on the reserved LUN other than INQUIRY or
- REQUEST SENSE, it shall be rejected with Err_SenseReservationConflict
- status. }
- function TCdRom.SCSIreserve : BOOLEAN;
- begin result := ASPIsend6($16, 0,0, NIL,0, SRB_NODIR, ShortTimeout); end;
- function TCdRom.SCSIrelease : BOOLEAN;
- begin result := ASPIsend6($17, 0,0, NIL,0, SRB_NODIR, ShortTimeout); end;
- { The third-party reservation for RESERVE command allows to reserve
- the logical unit for another SCSI device specified in the third-party
- DeviceID field. This is intended for use in multiple-initiator systems
- that use the COPY command. }
- function TCdRom.SCSIreserve3rdParty(TrdPartyDeviceID: BYTE): BOOLEAN;
- var I : DWORD;
- begin
- I := DWORD(TrdPartyDeviceID) AND 7; I := (I SHL 17) OR $100000;
- result := ASPIsend6($16, I, 0, NIL, 0, SRB_NODIR, ShortTimeout);
- end;
- function TCdRom.SCSIrelease3rdParty(TrdPartyDeviceID: BYTE): BOOLEAN;
- var I : DWORD;
- begin
- I := DWORD(TrdPartyDeviceID) AND 7; I := (I SHL 17) OR $100000;
- result := ASPIsend6($17, I, 0, NIL, 0, SRB_NODIR, ShortTimeout);
- end;
- { SelfSets is the mandatory subset implementation of SEND DIAGNOSTIC
- command. SEND DIAGNOSTIC requests the target to perform diagnostic
- operations on itself. Here is only mandatory implementation of this
- command: self-test feature with the parameter list length of zero.
- Full command implementation (with page structure) is OPTIONAL.
- DOFF=TRUE (Device Off-line) and UOFF=TRUE (Unit Off-line) flags grant
- permission to perform vendor-specific diagnostic operations on the
- target that may be visible to attached initiators, e.g. writings
- to the user accessible medium, or repositioning of the medium on
- sequential access devices. Setting this flags FALSE prohibits all
- diagnostic operations that may be detected by other I/O processes.}
- function TCdRom.SCSIselfTest(DOFF, UOFF : BOOLEAN) : BOOLEAN;
- var I : DWORD;
- begin
- I := $40000;
- if DOFF then I := I OR $20000;
- if UOFF then I := I OR $10000;
- result := ASPIsend6($1D, I, 0, NIL, 0, SRB_NODIR, ShortTimeout);
- end;
- { READ CAPACITY command returns logical block address and block length
- (in bytes) of the last valid logical block of the logical unit for seek
- operations. BlkCount may be greater (up to +75) than or equal to the
- last readable or playable block. If greater, this address may be in a
- transition area beyond the last valid logical block for read or audio
- play operations because the CD-ROM TOC (table of contents) lead-out
- track location has a +/- 75 sector tolerance when the lead-out track
- is encoded as an audio track. }
- function TCdRom.SCSIreadCapacity(var BlkCount, BlkSize : DWORD) : BOOLEAN;
- var cd : array[0..1] of DWORD;
- begin
- FillChar(cd, sizeof(cd), 0);
- result := ASPIsend10($25, 0,0,0,0, @cd,8, SRB_DIR_IN, ShortTimeout);
- BlkCount := BigEndianD(cd[0]);
- BlkSize := BigEndianD(cd[1]);
- end;
- { READ CAPACITY command with enabled partial medium feature returns
- logical block address and block length (in bytes) of the last logical
- block address after which a substantial delay in data transfer will be
- encountered. This logical block address shall be greater than or equal
- to the logical block address specified in GLBA parameter.
- On CD-ROM media, this is interpreted as being the last readable or
- playable logical block beyond the specified GLBA.
- NOTE: This option may take several seconds to complete. }
- function TCdRom.SCSIreadCapacityPM(Partition : WORD; GLBA : DWORD;
- var BlkCount, BlkSize : DWORD) : BOOLEAN;
- var cd : array[0..1] of DWORD;
- begin
- FillChar(cd, sizeof(cd), 0);
- result := ASPIsend10($25,0,GLBA,0,((Partition AND $7F) SHL 1) OR 1,
- @cd, 8, SRB_DIR_IN, LongTimeout);
- BlkCount := BigEndianD(cd[0]);
- BlkSize := BigEndianD(cd[1]);
- end;
- { READ(10) command requests data from the target.
- DPO (Disable Page Out) flag is used to control replacement of logical
- blocks in the device's cache memory when the host has information
- on the future usage of the logical blocks.
- DPO = TRUE indicates that the target shall assign the logical
- blocks accessed the lowest caching priority. It overrides any
- retention priority specified in the cache page. If the DPO = TRUE,
- the host knows the logical blocks are not likely to be accessed
- again in the near future and should not be put in the cache
- memory nor retained by the cache memory.
- DPO = FALSE means that the host expects that logical blocks
- accessed by this command are likely to be accessed again in the
- near future and the priority shall be determined by the retention
- priority fields in the cache page.
- FUA (Force Unit Access) flag controls cache using too.
- FUA = TRUE indicates that the target shall access the specified
- logical blocks from the media (i.e. the data is not directly
- retrieved from the cache).
- FUA = FALSE means that any logical blocks that are contained in
- the cache memory may be transferred directly from the cache memory.
- GLBA is a number of first logical block to transfer.
- Sectors is a number of contiguous logical blocks of data that shall be
- transferred. A transfer length of zero indicates that no logical
- blocks shall be transferred (this shall not be considered an error).
- BufLen is the length of data buffer "Buf" in bytes. }
- function TCdRom.SCSIread10(DPO,FUA: boolean; GLBA: DWORD; Sectors: WORD;
- Buf : pointer; BufLen : DWORD) : BOOLEAN;
- var Arg1 : byte;
- begin
- FillChar(Buf^, BufLen, 0);
- Arg1 := 0; if DPO then Arg1:=$10; if FUA then Arg1:=Arg1 OR 8;
- result := ASPIsend10($28, Arg1, GLBA, 0, Sectors,
- Buf, BufLen, SRB_DIR_IN, MediumTimeout);
- end;
- {======================= OPTIONAL SCSI-II COMMANDS ====================}
- { REZERO UNIT command is used in some devices to position the actuator at
- cylinder zero. Some devices return GOOD status without attempting any
- action. }
- function TCdRom.SCSIrezeroUnit : BOOLEAN;
- begin result := ASPIsend6(1,0,0,NIL,0, SRB_NODIR, ShortTimeout); end;
- { START STOP UNIT command requests that the target enable or disable the
- logical unit for media access operations. Targets that contain cache
- memory shall implicitly perform a SYNCHRONIZE CACHE command for the
- entire medium prior to executing the STOP UNIT command.
- STRT=TRUE requests the LUN be made ready for use, STRT=FALSE - be stopped
- (media cannot be accessed by the initiator).
- LOEJ=TRUE enables medium to be loaded (STRT=TRUE) or ejected (STRT=FALSE)
- LOEJ=FALSE requests that no medium action be taken.
- IMM=TRUE indicates that status shall be returned as soon as the CDB has
- been validated, else status shall be returned after the operation
- is completed. }
- function TCdRom.SCSIstartStopUnit(STRT, LOEJ, IMM : boolean) : BOOLEAN;
- var
- Arg1 : DWORD;
- Arg2 : byte;
- begin
- Arg1 := 0; if IMM then Arg1 := $10000;
- Arg2 := 0; if LOEJ then Arg2:=2; if STRT then Arg2:=Arg2 OR 1;
- result := ASPIsend6($1B, Arg1, Arg2, NIL, 0,
- SRB_NODIR, MediumTimeout);
- end;
- { PREVENT ALLOW MEDIUM REMOVAL enables or disables the removal of the
- medium in the unit. While prevention of medium removal condition is in
- effect the target shall inhibit mechanisms that normally allow removal
- of the medium by an operator. This mechanism is independent of device
- reservations and the target shall not allow medium removal if any
- initiator currently has medium removal prevented.
- The prevention of medium removal shall terminate:
- a) after all initiators that have medium removal prevented issue this
- commands with a prevent bit of zero, and the target has successfully
- performed a synchronize cache operation;
- b) upon the receipt of a BUS DEVICE RESET message from any initiator;
- c) upon a hard RESET condition.
- NOTE: We will use it only as property's SET procedure. }
- procedure TCdRom.SetLockUnlock(Value : boolean);
- begin
- if Value = fLocked then exit;
- if Value then ASPIsend6($1E,0,1,NIL,0,SRB_NODIR, ShortTimeout)
- else ASPIsend6($1E,0,0,NIL,0,SRB_NODIR, ShortTimeout);
- fLocked := Value;
- end;
- { SEEK(10) requests LUN seek to the specified logical block address. }
- function TCdRom.SCSIseek10(GLBA : DWORD) : BOOLEAN;
- begin
- result := ASPIsend10($2B,0,GLBA,0,0,NIL,0,SRB_NODIR, MediumTimeout);
- end;
- { PRE-FETCH requests that the target transfer the specified logical blocks
- to the cache memory. No data shall be transferred to the initiator.
- The target shall transfer to cache memory as many logical blocks as
- will fit and may elect to not transfer logical blocks that already are
- contained in the cache memory.
- IMM=TRUE indicates that status shall be returned as soon as the CDB has
- been validated, else status shall be returned after the operation
- is completed.
- GLBA and Sectors specifies the block range. Sectors=0 indicates that
- all remaining logical blocks shall be within the range. }
- function TCdRom.SCSIpreFetch(IMM : BOOLEAN;
- GLBA : DWORD; Sectors : WORD) : BOOLEAN;
- begin
- if IMM then result := ASPIsend10($34, 2, GLBA, 0, Sectors,
- NIL, 0, SRB_NODIR, ShortTimeout)
- else result := ASPIsend10($34, 0, GLBA, 0, Sectors,
- NIL, 0, SRB_NODIR, MediumTimeout);
- end;
- { SYNCHRONIZE CACHE ensures that logical blocks in the cache memory,
- within the specified range, have their most recent data value recorded
- on the physical medium. If a more recent data value exists in the cache
- memory than on the physical medium, then all data from the cache memory
- shall be written to the physical medium. Logical blocks are not
- necessarily removed from the cache memory as a result of the operation.
- IMM=TRUE indicates that status shall be returned as soon as the CDB has
- been validated, else status shall be returned after the operation
- is completed. If IMM=TRUE and the target does not support it then
- the command shall terminate with Err_SenseIllegalRequest status.
- GLBA and Sectors specifies the block range. Sectors=0 indicates that
- all remaining logical blocks shall be within the range. }
- function TCdRom.SCSIsynchronizeCache(IMM : BOOLEAN;
- GLBA : DWORD; Sectors : WORD) : BOOLEAN;
- begin
- if IMM then result := ASPIsend10($35, 2, GLBA, 0, Sectors, NIL, 0,
- SRB_NODIR, ShortTimeout)
- else result := ASPIsend10($35, 0, GLBA, 0, Sectors, NIL, 0,
- SRB_NODIR, MediumTimeout);
- end;
- { LOCK UNLOCK CACHE requests that the target disallow or allow logical
- blocks within the specified range to be removed from the cache memory
- by the target's cache replacement algorithm. Locked logical blocks may
- be written to the medium when modified, but a copy of the modified
- logical block shall remain in the cache memory. Multiple locks may be
- in effect from more than one initiator. Locks from different initiators
- may overlap. An unlock of an overlapped area does not release the lock
- of another initiator.
- LOK=TRUE means that any logical block in the specified range that is
- currently present in the cache memory shall be locked into cache
- memory. Only logical blocks that are already present in the cache
- memory are actually locked. LOK=FALSE indicates that all logical
- blocks in the specified range that are currently locked into the
- cache memory shall be unlocked, but not necessarily removed.
- GLBA and Sectors specifies the block range. Sectors=0 indicates that
- all remaining logical blocks shall be within the range. }
- function TCdRom.SCSIlockUnlockCache(LOK : boolean;
- GLBA : DWORD; Sectors : WORD) : BOOLEAN;
- var Arg1 : byte;
- begin
- if LOK then Arg1 := 2 else Arg1 := 0;
- result := ASPIsend10($36, Arg1, GLBA, 0, Sectors, NIL, 0,
- SRB_NODIR, ShortTimeout);
- end;
- { READ LONG requests to transfer data to the initiator. This data is
- vendor-specific, but shall include the data bytes and the ECC bytes
- recorded on the medium. Any other bytes that can be corrected by ECC
- should be included (e.g. data synchronization mark within the area
- covered by ECC). All tranferred bytes should be in the same order as
- they are on the media.
- CORR=TRUE (Corrected) causes the data to be corrected by ECC before
- being transferred to the initiator, CORR=FALSE causes a LB to be
- read without any correction made by the target.
- GLBA is a number of first logical block to transfer.
- BufLen specifies the number of bytes of data that are available for
- transfer. If it does not exactly match the available data length,
- the target shall terminate the command with Err_SenseIllegalRequest
- status. BufLen=0 indicates that no bytes shall be transferred and
- shall not be considered an error. }
- function TCdRom.SCSIreadLong(CORR : BOOLEAN; GLBA : DWORD;
- Buf : pointer; BufLen : DWORD) : BOOLEAN;
- var Arg1 : byte;
- begin
- FillChar(Buf^, BufLen, 0);
- if CORR then Arg1 := 2 else Arg1 := 0;
- result := ASPIsend10($3E, Arg1, GLBA, 0, BufLen,
- Buf, BufLen, SRB_DIR_IN, MediumTimeout);
- end;
- { READ SUB-CHANNEL requests the sub-channel data plus the state of audio
- play operations. Sub-channel data returned by this command may be from
- the last appropriate sector encountered by a current or previous media
- accessing operation. When there is no current audio play operation, the
- target may access the media to read the sub-channel data. The target is
- responsible that the data returned are current and consistent.
- MSFform=TRUE requests that MSF format be used for address fields, FALSE
- requests to LBA format. }
- function TCdRom.SCSIreadSubchannel(MSFform : BOOLEAN;
- var Info : TCdRomSubQinfo) : BOOLEAN;
- type SCSI_SubQinfo = packed record
- Reserved : byte;
- AudioStatus : byte;
- DataLength : word; // byte-reversed
- FormatCode : byte; // no matter
- ControlAndADR : byte;
- TrackNumber : byte;
- IndexNumber : byte;
- AbsoluteAddr : DWORD; // byte-reversed
- RelativeAddr : DWORD; // byte-reversed
- MCVal : byte; // if bit7=1 then UPC is valid
- UPC : array[0..14] of char; // Media catalogue number(UPC/Bar code)
- TCVal : byte; // if bit7=1 then ISRC is valid
- ISRC : array[0..14] of char; // Track international standard
- end; // recording code (ISRC)
- var
- Arg1, B : byte;
- cd : SCSI_SubQinfo;
- Len : WORD;
- i : integer;
- begin
- FillChar(cd, sizeof(cd), 0);
- if MSFform then Arg1 := 2 else Arg1 := 0;
- result := ASPIsend10($42, Arg1, $40000000, 0, sizeof(cd),
- @cd, sizeof(cd), SRB_DIR_IN, MediumTimeout);
- Len := BigEndianW(cd.DataLength);
- case cd.AudioStatus of
- 0 : Info.AudioStatus := AudioNone;
- $11 : Info.AudioStatus := AudioPlay;
- $12 : Info.AudioStatus := AudioPause;
- $13 : Info.AudioStatus := AudioComplete;
- $14 : Info.AudioStatus := AudioError;
- $15 : Info.AudioStatus := AudioStop
- else Info.AudioStatus := AudioInvalid;
- end;
- B := (cd.ControlAndADR SHR 4) AND $0F;
- if B >= 4 then Info.ADR := ADRreserved
- else Info.ADR := TsubchannelADRinfo(B);
- Info.PreEmphasis := (cd.ControlAndADR AND 1) <> 0;
- Info.CopyPermit := (cd.ControlAndADR AND 2) <> 0;
- Info.DataTrack := (cd.ControlAndADR AND 4) <> 0;
- Info.QuadAudio := (cd.ControlAndADR AND 8) <> 0;
- Info.TrackNumber := cd.TrackNumber;
- Info.IndexNumber := cd.IndexNumber;
- Info.AbsAddress := BigEndianD(cd.AbsoluteAddr);
- Info.RelAddress := BigEndianD(cd.RelativeAddr);
- Info.UPC := ''; i := 0;
- if (cd.MCVal AND $80) <> 0 then
- while (i <= 14) AND (Len >= (i + 14)) do
- begin Info.UPC := Info.UPC + cd.UPC[i]; i := i + 1; end;
- Info.ISRC := ''; i := 0;
- if (cd.TCVal AND $80) <> 0 then
- while (i <= 14) AND (Len >= (i + 30)) do
- begin Info.ISRC := Info.ISRC + cd.ISRC[i]; i := i + 1; end;
- if Len < 12 then begin result := FALSE; exit; end;
- end;
- { READ TOC requests that the target transfers the table of contents.
- NOTE: The maximum TOC data length possible on ISO-9660 CD-ROM media
- is 804 bytes, or 100 TOC track descriptors. Track number 0 is CD lead-in
- area, track number 0AAh is CD lead-out area, and they are not accessible
- through SCSI.
- MSFform=TRUE requests that MSF format be used for LBA fields, i.e. least
- significant byte should be the Frame number (0..74d), next byte -
- Second number (0..59d), next byte - Minute number (0..74bcd), and
- the MSB of DWORD is reserved. In certain transition areas, the
- relative MSF are decreasing positive values. The absolute MSF
- addresses are always increasing positive values.
- MSF=FALSE requests that the logical block address format be used
- for the CD-ROM absolute address field or for the offset from the
- beginning of the current track expressed as a number of LBs in a
- CD-ROM track relative address field. This track relative logical
- block address (TRLBA) value is reported as a negative value in
- twos-complement notation for transition areas that have decreasing
- MSF encoded relative addresses.
- Start field specifies the starting track number for which the data
- shall be returned. The data are returned in contiguous ascending
- track number order. If the starting track field is not valid for
- the currently installed medium, the command shall be terminated
- with Err_SenseIllegalRequest status. Start=0 means entire table of
- contents request and it's the only way we recommend to use this
- function.
- The returned data format shown below in SCSI_Toc record structure.
- The TOC data block contains a 4-byte header followed by zero or more
- TOC track descriptors. The first track number is not required to be one.
- A disc may start at any valid track number. The track numbers between
- the first and the last track number are required to be in contiguous
- ascending order, except for the lead-out track (always numbered 0AAh).
- NOTE: The starting logical block address value recovered from the TOC
- has a tolerance of zero for data tracks and plus or minus 75 CD sectors
- for audio tracks. }
- function TCdRom.SCSIreadToc(MSFform : BOOLEAN; Start : BYTE;
- var Toc: TCdRomToc) : BOOLEAN;
- type
- SCSI_TrackDescriptor = packed record
- Reserved1 : byte;
- ControlAndADR: byte; //bitset: D7..D4=ADR, D3..D0=Control
- TrackNumber : byte;
- Reserved2 : byte;
- AbsAddress : DWORD; //byte-reversed!
- end;
- SCSI_Toc = packed record
- Length : WORD; //byte-reversed!
- FirstTrack: byte;
- LastTrack : byte;
- Tracks : array[0..99] of SCSI_TrackDescriptor;
- end;
- var
- cd : SCSI_Toc;
- cdt : SCSI_TrackDescriptor;
- Arg1, B : byte;
- i : integer;
- begin
- FillChar(cd, sizeof(cd), 0);
- if MSFform then Arg1 := 2 else Arg1 := 0;
- result := ASPIsend10($43, Arg1, 0, Start, sizeof(cd),
- @cd, sizeof(cd), SRB_DIR_IN, ShortTimeout);
- if NOT result then begin
- Toc.FirstTrack := 0; Toc.LastTrack := 0;
- Toc.TrackCount := 0; exit;
- end;
- Toc.FirstTrack := integer(cd.FirstTrack) AND $00FF;
- Toc.LastTrack := integer(cd.LastTrack) AND $00FF;
- Toc.TrackCount := (integer(BigEndianW(cd.Length))-2) DIV sizeof(cdt);
- Toc.InMSF := MSFform;
- for i := 0 to Toc.TrackCount-1 do begin
- cdt := cd.Tracks[i];
- with Toc.Track[i] do begin
- Address := BigEndianD(cdt.AbsAddress);
- TrackNumber := cdt.TrackNumber;
- B := (cdt.ControlAndADR SHR 4) AND $0F;
- if B >= 4 then ADR := ADRreserved
- else ADR := TsubchannelADRinfo(B);
- PreEmphasis := (cdt.ControlAndADR AND 1) <> 0;
- CopyPermit := (cdt.ControlAndADR AND 2) <> 0;
- DataTrack := (cdt.ControlAndADR AND 4) <> 0;
- QuadAudio := (cdt.ControlAndADR AND 8) <> 0;
- end; end; end;
- { READ HEADER requests the CD-ROM data block address header of the
- requested logical block.
- CDmode field specifies the CD-ROM data mode of the logical blocks:
- +======================================+
- |CD-ROM|User data field|Auxiliary field|
- | mode | (2 048 bytes) | (288 bytes) |
- |------+---------------+---------------|
- | 00h | All zeroes | All zeroes |
- | 01h | User data | L-EC symbols |
- | 02h | User data | User data |
- |03-FFh| Reserved | Reserved |
- +======================================+
- Address field gives the address of the first logical block in the
- physical sector where the data for the requested GLBA is found. }
- function TCdRom.SCSIreadHeaderLBA(GLBA : DWORD;
- var CDmode : BYTE; var Address : DWORD) : BOOLEAN;
- type SCSI_HeaderInfo = packed record
- Mode : BYTE;
- Resv1,Resv2,Resv3 : BYTE;
- Addr : DWORD;
- end;
- var cd : SCSI_HeaderInfo;
- begin
- FillChar(cd, sizeof(cd), 0);
- result := ASPIsend10($44, 0, GLBA, 0, sizeof(cd),
- @cd, sizeof(cd), SRB_DIR_IN, MediumTimeout);
- CDmode := cd.Mode;
- Address := BigEndianD(cd.Addr);
- end;
- { Second implementation of READ HEADER requests that MSF format be used
- for Address field. }
- function TCdRom.SCSIreadHeaderMSF(GLBA : DWORD;
- var CDmode, AddressM, AddressS, AddressF : BYTE) : BOOLEAN;
- var cd : array[0..7] of BYTE;
- begin
- FillChar(cd, sizeof(cd), 0);
- result := ASPIsend10($44, 2, GLBA, 0, sizeof(cd),
- @cd, sizeof(cd), SRB_DIR_IN, MediumTimeout);
- CDmode := cd[0];
- AddressM := cd[5];
- AddressS := cd[6];
- AddressF := cd[7];
- end;
- { MODE SELECT(10) is a means to specify medium or device parameters to
- the target. The target may provide for independent sets of parameters
- for each attached logical unit.
- PF (Page Form)=TRUE indicates that parameters following the header and
- block descriptor(s) are structured as pages as specified in SCSI-2,
- PF=FALSE means that all parameters after the block descriptors are
- vendor-specific according to SCSI-1.
- SP (Save Pages)=TRUE indicates that the target (after performing the
- specified MODE SELECT operation) shall save to a non-volatile
- vendor-specific location all the savable pages. The SP flag is
- optional, even when mode pages are supported by the target.
- If the target does not implement saved pages and the SP flag is
- set to TRUE, the command shall be terminated with
- Err_SenseIllegalRequest status.
- BufLen is the length in bytes of the mode parameter list that shall be
- transferred to the target. BufLen=0 indicates that no data shall be
- transferred. This condition shall not be considered as an error.
- Command shall be terminated with Err_SenseIllegalRequest status and all
- mode parameters shall be remains unchanged if:
- a) It sets any field that is reported by the target as not changeable
- to a value other than its current value.
- b) It sets any field in the mode parameter header, block descriptor(s)
- or mode parameter to an unsupported value.
- c) It sends a mode page with a length not equal to the page length
- returned by the MODE SENSE command for that page.
- d) It sets any reserved field in the parameter list to a non-zero value.
- e) BufLen results in the truncation of any mode parameter header, mode
- parameter block descriptor(s), or mode page.
- A target may alter any mode parameter in any mode page (even reported
- as non-changeable) as a result of changes to other mode parameters.
- NOTE: MODE SENSE command should be issued prior to each MODE SELECT to
- determine supported mode pages, page lengths, and other parameters.
- NOTE: The initiator should issue a MODE SENSE command with the PCTL=1
- and PAGE=3Fh to determine which mode pages are supported, which mode
- parameters within the pages are changeable, and the supported length
- of each mode page prior to issuing any MODE SELECT commands.
- NOTE: MODE SELECT does not support page code of 3Fh (all pages). }
- function TCdRom.SCSImodeSelectEX(PF, SP : BOOLEAN;
- Buf : pointer; BufLen : DWORD) : BOOLEAN;
- var Arg1 : byte;
- begin
- if PF then Arg1 := $10 else Arg1 := 0;
- if SP then Arg1 := Arg1 OR 1;
- result := ASPIsend10($55, Arg1, 0, 0, BufLen,
- Buf, BufLen, SRB_DIR_OUT, ShortTimeout);
- end;
- function TCdRom.SCSImodeSelect(Buf : pointer; BufLen : DWORD) : BOOLEAN;
- begin result := SCSImodeSelectEX(TRUE, FALSE, Buf, BufLen); end;
- { MODE SENSE(10) provides a means for a target to report parameters to
- the initiator. It is a complementary command to the MODE SELECT(10).
- DBD (Disable Block Descriptors) = TRUE specifies that the target shall
- not return any block descriptors in the returned MODE SENSE data.
- DBD=FALSE means that the target may return zero or more block
- descriptors there, at the target's discretion.
- PCTL (Page Control) field defines the type of mode parameter values to
- be returned in the mode pages: 00b=Current Values, 01b=Changeable
- Values, 10b=Default Values and 11b=Saved Values (see below).
- NOTE: PCTL field only affects the mode parameters within the mode
- pages, however the PS bit, page code and page length fields should
- return current values since they have no meaning when used with
- other types. The mode parameter header and mode parameter block
- descriptor should return current values.
- The current values (PCTL=0) are:
- a) the values established by last successful MODE SELECT command;
- b) the saved values if a MODE SELECT command has not successfully
- completed since the last power-on, hard or soft RESET;
- c) the default values, if saved values are not available or not
- supported.
- If the changeable values (PCTL=1) are selected, then all fields of
- the mode parameters that are changeable shall be set to all one bits
- and the fields of the mode parameters that are non-changeable (i.e.
- defined by the target) shall be set to all zero bits.
- NOTE: An attempt to change a non-changeable mode parameter (via MODE
- SELECT) results in an error condition.
- The default values (PCTL=2) are accessible even if the device is not
- ready. Parameters not supported by the target shall be set to zero.
- Implementation of saved (PCTL=3) page parameters is optional.
- Mode parameters not supported by the target shall be set to zero.
- If saved values are not implemented, the command shall return
- Err_SenseIllegalRequest status.
- PAGE specifies which mode page(s) to return. If requested page is not
- implemented by the target, it shall return Err_SenseIllegalRequest
- status.
- A page code of 3Fh indicates that all pages implemented by the
- target shall be returned. Pages should be returned in ascending
- page code order except for mode page 00h. If implemented, it shall
- be returned after all other mode pages.
- NOTE: If the PCTL field and PAGE field are both set to zero, the
- target should return a mode parameter header and block descriptor
- (if applicable). This is for compatibility with SCSI-1 initiators.
- +====================================================+
- |Page code| Description |
- |---------+------------------------------------------+
- | 01h | Read error recovery page |
- | 02h | Disconnect-reconnect page |
- | 07h | Verify error recovery page |
- | 08h | Caching page |
- | 09h | Peripheral device page |
- | 0Ah | Control mode page |
- | 0Bh | Medium types supported page |
- | 0Dh | CD-ROM page |
- | 0Eh | CD-ROM audio control page |
- | 20h-3Eh |Vendor-specific (page format required) |
- | 00h |Vendor-specific (page format not required)|
- +====================================================+ }
- function TCdRom.SCSImodeSense(DBD : BOOLEAN; PCTL : TCdRomModePageType;
- PAGE : BYTE; Buf : pointer; BufLen : DWORD) : BOOLEAN;
- var
- Arg1 : byte;
- Arg2 : DWORD;
- begin
- FillChar(Buf^, BufLen, 0);
- if DBD then Arg1 := 8 else Arg1 := 0;
- Arg2 := ((Ord(PCTL) AND 3) SHL 6) OR (PAGE AND $3F);
- result := ASPIsend10($5A, Arg1, Arg2 SHL 24, 0, BufLen,
- Buf, BufLen, SRB_DIR_IN, ShortTimeout);
- end;
- {============= MODE SELECT/MODE SENSE - Mode list header. =============}
- { The mode parameter list used by MODE SELECT/MODE SENSE contains a header,
- followed by zero or more block descriptors, followed by zero or more
- variable-length pages. Here are procedures for header and descriptors
- reading/modifying.
- The list header consist of two useful parameters:
- a) The medium-type code field defined by TCdRomMediumType type.
- 'mixed' means that medium may contain data and audio tracks,
- 'default' - that device supports only one medium type (??),
- suffix '80' - that media is a 80 mm diameter disk instead the
- default 120 mm.
- b) The DPOFUA flag (not supported for MODE SELECT). If MODE SENSE
- returns this flag TRUE, then target supports DPO and FUA flags
- using (see SCSIread10 function description).
- Each block descriptor contains three fields:
- a) Density - Enumerated CD-ROM density codes:
- 00h = Default density code (the only density supported by target)
- 01h = CD-ROM mode 1 - User data only (2048 bytes per physical sector)
- 02h = CD-ROM mode 2 - User data plus auxiliary data field (2336 bytes)
- 03h = 4-byte tag field, user data plus auxiliary data (2340 bytes)
- 04h = CD-Audio mode - 1/75th of a second per logical block
- values 05h-7Fh are reserved, 80h-FFh - vendor-specific.
- NOTE: The number of bytes per sector specified here is used with the
- block length to map CD-ROM sectors to logical block addresses.
- b) BlkCount - The number of logical blocks on the medium to which the
- density code and block length fields apply. A zero value indicates
- that all of the remaining logical blocks of the logical unit shall
- have the medium characteristics specified.
- c) BlkSize - The length in bytes of each logical block described by
- the block descriptor. }
- function TCdRom.SCSImodeSenseHeader(var sh : TCdRomModeHeader) : BOOLEAN;
- type SCSI_ModeHeader = packed record
- ModeDataLength : WORD; // full record length except itself (SENSE only)
- MediumType : BYTE;
- DeviceSpecific : BYTE; // $10 bit is DPOFUA flag
- Reserved1 : WORD;
- DescriptorLength : WORD; // equal to 8*Number of Block Descriptors
- // must be zero when DBD=TRUE
- BDP : array[0..511] of DWORD; // intuitional choice :)
- end;
- var
- cd : SCSI_ModeHeader;
- i : integer;
- begin
- FillChar(sh, sizeof(sh), 0);
- result := SCSImodeSense(FALSE, MPTcurrent, $3F, @cd, sizeof(cd));
- if result then begin
- if cd.MediumType > 7 then sh.Medium := CDTreserved
- else sh.Medium := TCdRomMediumType(cd.MediumType);
- sh.DPOFUA := (cd.DeviceSpecific AND $10) <> 0;
- sh.BDlength := cd.DescriptorLength DIV 8;
- for i := 0 to sh.BDlength - 1 do with sh.BD[i] do begin
- Density := BYTE($0FF AND cd.BDP[i SHL 1]);
- BlkCount := $0FFFFFF AND BigEndianD(cd.BDP[i SHL 1]);
- BlkSize := $0FFFFFF AND BigEndianD(cd.BDP[(i SHL 1)+1]);
- end; end; end;
- {======== MODE SELECT/MODE SENSE - Error recovery parameters page. ========}
- { This page specifies the error recovery parameters the target shall use
- during any command that performs a data read operation to the media
- (e.g. READ, READ TOC, etc.).
- PSAV (Parameters SAVable) = TRUE indicates that the target is capable
- of saving the page in a non-volatile vendor-specific location.
- This bit is only used with the MODE SENSE command.
- RLEV (Recovery Level) is the set of five bits:
- $20 = Transfer block bit. If set, data block that is not recovered
- shall be transferred to data buffer anyway. This bit does not
- affect the action taken for recovered data and does not
- suppress the Err_SenseMediumError status of read commands.
- $10 = Read continuous bit. If set, indicates the target shall
- transfer the entire requested length of data without adding
- delays to perform error recovery procedures.
- This implies that the target may send data that is erroneous
- or fabricated in order to maintain a continuous flow of data.
- This bit is typically used in audio or video applications.
- $04 = Post error bit. If set, target shall report recovered errors.
- $02 = Disable transfer on error bit. Ones indicates that the target
- shall terminate the data phase upon detection of a recovered
- error.
- $01 = Disable correction bit. If set, error correction codes shall
- not be used for data error recovery. This is applicable to
- layered error correction (L-EC) feature only. The retries and
- CIRC (Cross Interleaved Reed-solomon Code,the error detection
- and correction technique used within small (24 bytes) frames
- of audio or data) are performed anyway.
- The interpretation of these bit settings for CD-ROM devices and
- valid RLEV values are given below. If RLEV is set to any other
- value, the command shall be terminated with Err_SenseInvalidRequest
- status.
- 00h/01h If an uncorrectable error occurs, transfer is terminated
- with Err_SenseMediumError status. The block with the error is not
- transferred. Recovered errors are not reported.
- 04h/05h Recovered errors are reported by Err_SenseRecoveredError,
- but data transfer is not terminated. Uncorrectable error terminates
- data transfer with Err_SenseMediumError status and the block with
- the error is not transferred.
- 06h/07h Recovered data error terminates data transfer with
- Err_SenseRecoveredError status. The block with the recovered error is
- not transferred. Uncorrectable error terminates data transfer with
- Err_SenseMediumError status and errata block is not transferred.
- 10h/11h If an error occurs which is uncorrectable in time to maintain
- data transfer, the data transfer is not terminated. However, when the
- data transfer has completed, Err_SenseMediumError status is reported.
- Recovered errors are not reported.
- 14h/15h Data transfer is never terminated. Recovered errors are
- reported at completion by Err_SenseRecoveredError status,
- uncorrectable errors - by Err_SenseMediumError. Reporting
- unrecovered errors takes precedence over reporting recovered errors.
- 20h/21h/24h/25h/26h/27h are the same as 00h/01h/04h/05h/06h/07h, but
- the block with the error is always transferred.
- 30h/31h/34h/35h are the same as 10h/11h/14h/15h.
- A CIRC recovered data error is defined as a block for which the
- CIRC based error correction algorithm was unsuccessful for a read
- attempt, but on a subsequent read operation no error was reported.
- The number of subsequent read operations is limited to the read
- retry count. Layered error correction was not used.
- An L-EC recovered data error is defined as a block for which the
- CIRC based error correction was unsuccessful, but the layered error
- correction was able to correct the block within the read retry count.
- RETR (Read Retry Count) specifies the number of times that the controller
- shall attempt its read recovery algorithm. }
- function TCdRom.SCSImodeSenseRecoverEX(PCTL : TCdRomModePageType;
- var PSAV : BOOLEAN; var RLEV, RETR : BYTE) : BOOLEAN;
- type SCSI_ModeRecovery = packed record
- ModeDataLength : WORD; // full record length except itself
- MediumType : BYTE;
- DeviceSpecific : BYTE;
- Reserved1 : WORD;
- DescriptorLength : WORD; // equal to 8*Number of Block Descriptors
- // must be zero when DBD=TRUE
- PageCode : BYTE; // six LSBs of page code (01h) plus
- // MSbit is PSAV flag (MODE SENSE only)
- ParamLength : BYTE; // must be = 6
- RecoveryLevel : BYTE; // returns as RLEV
- MaxRetryCount : BYTE;
- Reserved2 : DWORD;
- end;
- var cd : SCSI_ModeRecovery;
- begin
- result := SCSImodeSense(TRUE, PCTL, 1, @cd, sizeof(cd));
- PSAV := (cd.PageCode AND $80) <> 0;
- RLEV := cd.RecoveryLevel;
- RETR := cd.MaxRetryCount;
- end;
- function TCdRom.SCSImodeSenseRecover(var RLEV, RETR : BYTE) : BOOLEAN;
- var PSAVtemp : BOOLEAN;
- begin
- result := SCSImodeSenseRecoverEX(MPTcurrent, PSAVtemp, RLEV, RETR);
- end;
- {======== MODE SELECT/MODE SENSE - Supported medium page. ========}
- { This page contains a list of the medium types implemented by the target.
- The code values (up to four) are reported in ascending order.
- PSAV=TRUE indicates that the target is capable of saving the page
- in a non-volatile location. Used with the MODE SENSE only. }
- function TCdRom.SCSImodeSenseMediumEX(PCTL : TCdRomModePageType;
- var PSAV: BOOLEAN; var Med1,Med2,Med3,Med4: TCdRomMediumType): BOOLEAN;
- type SCSI_ModeMedium = packed record
- ModeDataLength : WORD; // full record length except itself
- MediumType : BYTE;
- DeviceSpecific : BYTE;
- Reserved1 : WORD;
- DescriptorLength : WORD;
- PageCode : BYTE; // six LSBs of page code (0Bh) plus
- // MSbit is PSAV flag (MODE SENSE only)
- ParamLength : BYTE; // must be = 6
- Reserved2 : WORD;
- Medium : array[0..3] of BYTE;
- end;
- var
- cd : SCSI_ModeMedium;
- i : integer;
- begin
- result := SCSImodeSense(TRUE, PCTL, $0B, @cd, sizeof(cd));
- PSAV := (cd.PageCode AND $80) <> 0;
- with cd do begin
- for i := 0 to 3 do begin
- if Medium[i] > 7 then Medium[i] := 4; // CDTreserved
- if (Medium[i]=0) AND (i > 0)
- then Medium[i] := 8; // CDTnone
- end;
- Med1 := TCdRomMediumType(Medium[0]);
- Med2 := TCdRomMediumType(Medium[1]);
- Med3 := TCdRomMediumType(Medium[2]);
- Med4 := TCdRomMediumType(Medium[3]);
- end; end;
- {======== MODE SELECT/MODE SENSE - CD-ROM device parameters page. ========}
- { This page specifies parameters that affect all CD-ROM data types.
- PSAV=TRUE indicates that the target is capable of saving the page in a
- non-volatile vendor-specific location. Used with the MODE SENSE only.
- ITimer (Inactivity Timer) specifies the time that the drive shall remain
- in the hold track state after completion of a seek or read operation.
- Value: 1 2 3 4 5 6 7 8 9 Ah Bh Ch Dh Eh Fh
- Time: 0.125s 0.25s 0.5s 1s 2s 4s 8s 16s 32s 1m 2m 4m 8m 16m 32m
- where s=seconds, m=minutes. Value of zero means vendor-specific time.
- WARNING: High values in this parameter may have an adverse effect on
- the drive MTBF.
- SperMunits and FperSunits fields gives the ratio of these MSF address
- values. For media conforming to the CD-ROM and CD-DA standard, this
- value is 60 and 75 respectively. }
- function TCdRom.SCSImodeSenseDeviceEX(PCTL : TCdRomModePageType;
- var PSAV : BOOLEAN; var ITimer : BYTE;
- var SperMunits, FperSunits : WORD) : BOOLEAN;
- type SCSI_ModeDevice = packed record
- ModeDataLength : WORD; // full record length except itself
- MediumType : BYTE;
- DeviceSpecific : BYTE;
- Reserved1 : WORD;
- DescriptorLength : WORD;
- PageCode : BYTE; // six LSBs of page code (0Dh) plus
- // MSbit is PSAV flag (MODE SENSE only)
- ParamLength : BYTE; // must be = 6
- Reserved2 : BYTE;
- InactivityTimer : BYTE; // returns as ITimer
- SunitsPerMunits : WORD; // returns as SperMunits. Usually=60d
- FunitsPerSunits : WORD; // returns as FperSunits. Usually=75d
- end;
- var cd : SCSI_ModeDevice;
- begin
- result := SCSImodeSense(TRUE, PCTL, $0D, @cd, sizeof(cd));
- PSAV := (cd.PageCode AND $80) <> 0;
- ITimer := $0F AND cd.InactivityTimer;
- SperMunits := BigEndianW(cd.SunitsPerMunits);
- FperSunits := BigEndianW(cd.FunitsPerSunits);
- end;
- function TCdRom.SCSImodeSenseDevice(var ITimer : BYTE) : BOOLEAN;
- var
- PSAVtemp : BOOLEAN;
- SPM, FPS : WORD;
- begin
- result := SCSImodeSenseDeviceEX(MPTcurrent,
- PSAVtemp, ITimer, SPM, FPS);
- end;
- {======== MODE SELECT/MODE SENSE - CD-ROM audio control page. ========}
- { This page sets the playback modes and output controls for subsequent
- PLAY AUDIO commands and any current audio playback operation.
- PSAV (Parameters SAVable) = TRUE indicates that the target is capable
- of saving the page in a non-volatile vendor-specific location.
- This bit is only used with the MODE SENSE command.
- IMM=TRUE indicates that status of any audio playback operation shall be
- returned as soon as the CDB has been validated, else status shall
- be returned after the playback has been completed or terminated.
- It is recommended to RESERVE a LUN prior to starting audio play
- operations with IMM=TRUE in any multitasking environment.
- SOTC (Stop On Track Crossing)=TRUE indicates the target shall terminate
- the audio playback operation when the beginning of a following track
- is encountered. SOTC=FALSE indicates the target shall do it when the
- transfer length is satisfied, even if multiple tracks will be played
- (including the audio pause/silence between the tracks).
- APRV (Audio Playback Rate Valid) = TRUE indicates that the format of
- logical blocks per second field and the logical blocks per second of
- audio playback field are valid.
- LBAformat field is provided as a means to return fractional (i.e. non-
- integral) values in the logical block addresses per second of audio
- playback. This shall occur when logical block sizes that are not
- even multiples or divisions of the physical block size are used.
- It gives the multiplier to be used with the logical blocks per
- second of audio playback: LBAformat=0 means 1 LBA/sec, LBAformat=8
- - 1/256 LBA/sec. All other values of this field are reserved.
- LBSaudio (Logical Blocks per Second of audio playback) field gives the
- relationship between time and the duration of play per logical block
- address. The value in this field is to be multiplied by the value in
- format of LBAs per second field.
- NOTE: Both LBAformat and LBSaudio may not be supported by CD-ROM
- devices as a modifiable mode select parameter.
- Volume[] is the relative volume level for this audio output port.
- Zero indicates the output is muted, and a value of FFh indicates
- maximum volume level. If volume controls are implemented, the
- default volume level should be no more than 25 % of the maximum.
- Channel[] specifies the audio channels from the disc to which this
- output port should be connected. More than one output port may be
- connected to an audio channel and vice versa. The four-bit value
- is a bitset of connected channels: 0=no connect (port is muted),
- 1=channel0 only, 2=channel1 only, 3=channel0 plus channel1, etc. }
- function TCdRom.SCSImodeSenseAudioEX(PCTL : TCdRomModePageType;
- var sh : TCdRomModePageAudio) : BOOLEAN;
- type SCSI_ModeAudio = packed record
- ModeDataLength : WORD; // full record length except itself
- MediumType : BYTE;
- DeviceSpecific : BYTE;
- Reserved1 : WORD;
- DescriptorLength : WORD;
- PageCode : BYTE; // six LSBs of page code (0Eh) plus
- // MSbit is PSAV flag (MODE SENSE only)
- ParamLength : BYTE; // must be = 14d
- IMMandSOTC : BYTE;
- Reserved2 : WORD;
- APRVandLBAF : BYTE;
- LBSaudioW : WORD;
- ChVolume : array[0..3] of WORD;
- end;
- var
- cd : SCSI_ModeAudio;
- i : integer;
- begin
- FillChar(sh, sizeof(sh), 0);
- result := SCSImodeSense(TRUE, PCTL, $0E, @cd, sizeof(cd));
- if result then with sh do begin
- PSAV := (cd.PageCode AND $80) <> 0;
- IMM := (cd.IMMandSOTC AND 4) <> 0;
- SOTC := (cd.IMMandSOTC AND 2) <> 0;
- APRV := (cd.APRVandLBAF AND $80) <> 0;
- LBAformat := cd.APRVandLBAF AND $0F;
- LBSaudio := BigEndianW(cd.LBSaudioW);
- for i := 0 to 3 do begin
- Volume[i] := $0FF AND BigEndianW(cd.ChVolume[i]);
- Channel[i] := $0F AND cd.ChVolume[i];
- end; end; end;
- function TCdRom.SCSImodeSenseAudio(var sh : TCdRomModePageAudio) : BOOLEAN;
- begin result := SCSImodeSenseAudioEX(MPTcurrent, sh); end;
- {======================= PLAY AUDIO SCSI-II COMMANDS ====================}
- { If any of the PLAY AUDIO commands are implemented, the PLAY AUDIO
- command shall be implemented by the target. A target responding
- to a PLAY AUDIO command that has a transfer length of zero with
- Err_SenseIllegalRequest status does not support audio play operations.
- + 45h PLAY AUDIO(10) ; begins an audio playback operation
- + A5h PLAY AUDIO(12) ; same for long-time (>14 min) operations
- + 47h PLAY AUDIO MSF ; same with MSF parameter format
- + 48h PLAY AUDIO TRACK/INDEX ; same with track/index parameter format
- + 49h PLAY TRACK RELATIVE(10) ; same with track+RelLBA parameter format
- + A9h PLAY TRACK RELATIVE(12) ; same for long-time (>14 min) operations
- + 4Bh PAUSE/RESUME ; Pauses/resumes paused audio play operation
- }
- { PLAY AUDIO(10) and PLAY AUDIO(12) requests the target to begin an
- audio playback operation. The command function (IMM and SOTC flags)
- and the mapping/volume of audio signals are specified by the settings
- of the mode parameters (see SCSImodeSenseAudio function).
- GLBA is the logical block at which the playback operation shall begin.
- Sectors is a number of contiguous logical blocks of data that shall be
- played. A transfer length of zero indicates that no audio operation
- shall occur (this shall not be considered an error).
- If the logical block length is not equal to the sector size, the target
- may adjust the starting LBA and the transfer length. In such case,
- target starts the audio play operation with the beginning of a sector
- whenever the starting logical address falls within, and may continue
- the playback through the end of a sector whenever the ending logical
- address falls within.
- If the starting address is not found, if the address is not within an
- audio track, or if a not ready condition exists, the command shall be
- terminated with Err_CheckCondition status.
- If the logical block address requested is not within an audio track,
- or the CD-ROM information type (data vs. audio) changes, the command
- shall be terminated with Err_SenseBlankCheck status. }
- function TCdRom.SCSIplayAudio10(GLBA : DWORD; Sectors : WORD) : BOOLEAN;
- begin
- result := ASPIsend10($45, 0, GLBA, 0, Sectors,
- NIL, 0, SRB_NODIR, AudioTimeout);
- end;
- function TCdRom.SCSIplayAudio12(GLBA, Sectors : DWORD) : BOOLEAN;
- begin
- result := ASPIsend12($A5, 0, GLBA, Sectors, 0,
- NIL, 0, SRB_NODIR, AudioTimeout);
- end;
- { PLAY AUDIO MSF requests the target to begin an audio playback operation.
- It is full equivalent to the PLAY AUDIO command except the CD addresses
- style - start and stop points in Minute-Second-Frame form.
- A starting MSF address equal to an ending MSF address causes no audio
- play operation to occur. This shall not be considered an error. If
- the starting MSF address is less than the ending MSF address, the
- command shall be terminated with Err_SenseIlleglRequest status. }
- function TCdRom.SCSIplayAudioMSF(StartM, StartS, StartF,
- StopM, StopS, StopF : BYTE ) : BOOLEAN;
- begin
- result := ASPIsend10($47, 0, GatherDWORD(0, StartM, StartS, StartF),
- StopM, GatherWORD(StopS, StopF), NIL, 0, SRB_NODIR, AudioTimeout);
- end;
- { PLAY AUDIO TRACK INDEX requests the target to begin an audio playback
- operation. It is full equivalent to the PLAY AUDIO command except the
- CD addresses style - start and stop points in Track-Index form.
- The audio play operation shall begin at the first block with specified
- starting track/index and terminate at the last block with specified
- ending track/index. All contiguous audio sectors between the starting
- and the ending address shall be played.
- Valid values for the track and index fields are 1 to 99. The first
- audio sector of the track following the (optional) pause has Index=1.
- Index=99 specifies that playback continues through the last sector of
- the track.
- All these conditions shall not be considered errors:
- a) If the ending track is greater than the last information track on the
- media, the playback shall continue until the last track is complete.
- b) If the ending index is greater than the largest index value on the
- ending track, the playback shall continue until this track is complete.
- c) If the starting index is greater than the largest index value on the
- starting track, and the SOTC flag of the audio control mode page (see
- SCSImodeSenseAudio function) is FALSE, the playback operation shall
- start at the beginning of the next track.
- The command shall be terminated with Err_SenseIlleglRequest status if
- the starting index is greater than the largest index value on the
- starting track and SOTC=TRUE, so that playback shall not begin.
- NOTE: The SOTC=TRUE allows the user to determine the largest index on
- the track. The SOTC=FALSE allows the user to set up play operations
- without complete knowledge of the media layout. }
- function TCdRom.SCSIplayAudioTI(StartTrack, StartIndex,
- StopTrack, StopIndex : BYTE ) : BOOLEAN;
- begin
- result := ASPIsend10($48, 0, GatherDWORD(0,0,StartTrack,StartIndex),
- 0, GatherWORD(StopTrack,StopIndex), NIL,0,SRB_NODIR,AudioTimeout);
- end;
- { PLAY AUDIO TRACK RELATIVE(10) and PLAY AUDIO TRACK RELATIVE(12)
- requests the target to begin an audio playback operation. It is full
- equivalent to the PLAY AUDIO(10)/(12) commands except the CD start
- address which is specified as a track relative logical block address
- (TRLBA, two's complement) within the specified starting track.
- Negative TRLBA values indicate a starting location within the audio
- pause area at the beginning of the requested track. }
- function TCdRom.SCSIplayAudioR10(TRLBA : DWORD;
- StartTrack : BYTE; Sectors : WORD) : BOOLEAN;
- begin
- result := ASPIsend10($49, 0, TRLBA, StartTrack, Sectors,
- NIL, 0, SRB_NODIR, AudioTimeout);
- end;
- function TCdRom.SCSIplayAudioR12(TRLBA : DWORD;
- StartTrack : BYTE; Sectors : DWORD) : BOOLEAN;
- begin
- result := ASPIsend12($A9, 0, TRLBA, Sectors, StartTrack,
- NIL, 0, SRB_NODIR, AudioTimeout);
- end;
- { PAUSE RESUME command stops or starts an audio play operation.
- This command is used with PLAY AUDIO commands.
- PAUSE causes the drive to enter the hold track state with the audio
- output muted after the current block is played. If no audio play
- operation is in progress, or the requested audio play operation has
- been completed, the command is terminated with Err_CheckCondition
- status. It shall not be considered an error to request a pause when
- a pause is already in effect. }
- function TCdRom.SCSIpauseAudio : BOOLEAN;
- begin result:=ASPIsend10($4B,0,0,0,0,NIL,0,SRB_NODIR,ShortTimeout); end;
- { RESUME causes the drive to release the pause and begin play at the
- block following the last block played. If an audio play operation
- cannot be resumed, the command is terminated with Err_CheckCondition
- status. It shall not be considered an error to request a resume when
- a play operation is in progress. }
- function TCdRom.SCSIresumeAudio : BOOLEAN;
- begin result:=ASPIsend10($4B,0,0,0,1,NIL,0,SRB_NODIR,ShortTimeout); end;
- end.