TCDBASIC.PAS
上传用户:jzxjwgb
上传日期:2007-01-06
资源大小:64k
文件大小:76k
源码类别:

SCSI/ASPI

开发平台:

Delphi

  1. unit TCDBASIC;
  2. // -----------------------------------------------------------------------------
  3. // Project:         CD-ROM and CD-audio Interface Components
  4. // Component Names: TCdRom
  5. // Module:          TCdBasic
  6. // Description:     Implements ASPI/SCSI primitive interface
  7. //                  to CD-ROM and CD-Audio devices.
  8. // Version:         0.2 early beta
  9. // Date:            25-MAY-1999
  10. // Target:          Win32, Delphi3
  11. // Authors:         Sergey "KSER" Kabikov,   iamkser@hotmail.com
  12. // Copyright        (c) 1999 Sergey Kabikov
  13. // -----------------------------------------------------------------------------
  14. // -----------------------------------------------------------------------------
  15. // Acknowledgements:
  16. // 1. Thanks to Jay A. Key (scsiprog@geocities.com) for his "akrip" program,
  17. //    which sources (http://www.geocities.com/SiliconValley/Byte/7125/)
  18. //    helps to solve many problems.
  19. // -----------------------------------------------------------------------------
  20.    { 1. Full list of mandatory SCSI-2 commands ('+' is release mark)
  21.         Comment line is brief description if released or reasons why don't.
  22. + 00h TEST UNIT READY        ; checks if the LUN (logical unit) is ready.
  23. + 03h REQUEST SENSE  ; requests info about results of last SCSI operation.
  24. + 12h INQUIRY        ; requests some parameters of the target.
  25. + 16h RESERVE        ; reserves the entire LUN for the exclusive use.
  26. + 17h RELEASE        ; cancels RESERVE command effects.
  27. + 1Dh SelfTest       ; requests the target to perform self-test.
  28. + 25h READ CAPACITY  ; returns logical block address (LBA) and block length
  29.                      ; of the last valid block for seek operations.
  30. + 28h READ(10)       ; requests data from the target.
  31.      2. Full list of optional SCSI-2 commands ('+' is release mark)
  32. + 01h REZERO UNIT            ; positions the actuator at cylinder 0
  33. - 08h READ(6)                ; REPLACED BY READ(10)
  34. - 0Bh SEEK(6)                ; REPLACED BY SEEK(10)
  35. - 15h MODE SELECT(6)         ; REPLACED BY MODE SELECT(10)
  36. - 18h COPY                   ; RARE NEED/RARE EXISTS
  37. - 1Ah MODE SENSE(6)          ; REPLACED BY MODE SENSE(10)
  38. + 1Bh START STOP UNIT        ; controls for media access. May also eject CD.
  39. - 1Ch RECEIVE DIAGNOSTIC RESULTS ; The only non-vendor-specific diagnostic
  40.                                  ; page is a list-of-supported-pages.
  41. - 1Dh SEND DIAGNOSTIC            ; Partial release - see SelfTest above.
  42. + 1Eh PREVENT/ALLOW MEDIUM REMOVAL
  43. - A8h READ(12)               ; We don't need to read >128MB in one piece...
  44. + 2Bh SEEK(10)               ; requests LUN seek to the specified LBA.
  45. - 2Fh VERIFY(10)                             ; RARE NEED/RARE EXISTS
  46. - Afh VERIFY(12)                             ; RARE NEED/RARE EXISTS
  47. - 30h/31h/32h SEARCH DATA HIGH/EQUAL/LOW(10) ; RARE NEED/RARE EXISTS
  48. - B0h/B1h/B2h SEARCH DATA HIGH/EQUAL/LOW(12) ; RARE NEED/RARE EXISTS
  49. - 33h SET LIMITS(10)                         ; FOR LINKED COMMANDS ONLY
  50. - B3h SET LIMITS(12)                         ; FOR LINKED COMMANDS ONLY
  51. + 34h PRE-FETCH          ; target must transfer the LBs to the cache memory.
  52. + 35h SYNCHRONIZE CACHE  ; forces all delayed writes from cache to medium.
  53. + 36h LOCK/UNLOCK CACHE  ; target disallows/allows LBs within the specified
  54.                          ; range to be removed from the cache memory.
  55. - 39h COMPARE            ; RARE NEED/RARE EXISTS
  56. - 3Ah COPY AND VERIFY    ; RARE NEED/RARE EXISTS
  57. - 3Bh WRITE BUFFER       ; The device's buffer I/O without medium access.
  58. - 3Ch READ BUFFER        ;/ Not-so-useful ?
  59. + 3Eh READ LONG          ; Reads full sector, including ECC bytes
  60. - 40h CHANGE DEFINITION  ; RARE NEED/RARE EXISTS
  61. + 42h READ SUB-CHANNEL   ; Read special info from current sector [partial]
  62. + 43h READ TOC           ; Reads CD's Table Of Content (tracks list)
  63. + 44h READ HEADER        ; Reads LB's header: abs.address and CD-ROM mode
  64. - 4Ch LOG SELECT         ; Statistics request commands (not for audio)
  65. - 4Dh LOG SENSE          ;/ Not-so-useful ?
  66. + 55h MODE SELECT(10)    ; Changes CD-ROM mode parameters.
  67. + 5Ah MODE SENSE(10)     ; A means for a CD-ROM to report its parameters.
  68. }
  69. interface
  70. uses  Windows, TASPIdev;
  71. type
  72. { =============== CD SUB-CHANNEL Q DATA DEFINITIONS =============== }
  73.    TAudioStatus = (AudioNone,  AudioPlay, AudioPause, AudioComplete,
  74.                    AudioError, AudioStop, AudioInvalid);
  75.            {Status|  Description                                   |
  76.            |------+------------------------------------------------|
  77.            |   0  |  Audio status byte not supported or not valid  |
  78.            |   1  |  Audio play operation in progress              |
  79.            |   2  |  Audio play operation paused                   |
  80.            |   3  |  Audio play operation successfully completed   |
  81.            |   4  |  Audio play operation stopped due to error     |
  82.            |   5  |  No current audio status to return             }
  83.          { Audio status values 3 and 4 return information on previous
  84.            audio operations; they are returned only once after the
  85.            condition has occurred. If another audio play operation is
  86.            not requested, the audio status returned for subsequent
  87.            READ SUB-CHANNEL commands is 5.  }
  88. var
  89.    TAudioStatusName : array[TAudioStatus] of string = ('None', 'Play',
  90.               'Pause', 'Complete', 'Error', 'Stop', 'Invalid');
  91. type
  92.    TsubchannelADRinfo = (ADRnone, ADRposition, ADRcatalogue,
  93.                          ADRISRC, ADRreserved);
  94.          {ADR code|   Sub-channel Q encoded information   |
  95.          |--------+---------------------------------------|
  96.          |   0h   | Mode information not supplied         |
  97.          |   1h   | Current position data ( track, index, |
  98.          |        |      absolute and relative addresses) |
  99.          |   2h   | Media catalogue number                |
  100.          |   3h   | ISRC                                  |
  101.          | 4h-Fh  | Reserved                              }
  102. var
  103.    TsubchannelADRname : array[TsubchannelADRinfo] of string = ('None',
  104.             'Position', 'Catalogue', 'ISRC', 'Reserved');
  105. type
  106.    TCdRomSubQinfo = packed record
  107.       AudioStatus  : TAudioStatus;
  108.       ADR          : TsubchannelADRinfo;
  109.       PreEmphasis  : BOOLEAN;   // TRUE=Audio track with pre-emphasis
  110.       CopyPermit   : BOOLEAN;   // TRUE=Digital copy permitted
  111.                                 // FALSE=Digital copy prohibited
  112.       DataTrack    : BOOLEAN;   // TRUE=Data track, FALSE=Audio track
  113.       QuadAudio    : BOOLEAN;   // TRUE=Four-channel audio
  114.                                 // FALSE=Two-channel audio
  115.       TrackNumber  : byte;      // Number (0..99d) of current track
  116.       IndexNumber  : byte;      // Index  (0..99d) of part of the track
  117.       UPC  : string[16];        // identifying number of this media
  118.                                 //  according to the uniform product code
  119.                                 //  values (UPC/EAN bar coding) in ASCII.
  120.       ISRC : string[16];        // identifying number of this media
  121.                                 //  according to the ISRC standards
  122.                                 //  (DIN-31-621) expressed in ASCII.
  123.       case BOOLEAN of           // Address of current location: absolute
  124.                                 // and relative to current track beginning
  125.         TRUE : (AbsAddress,          //  in LBA format
  126.                 RelAddress  : DWORD);
  127.         FALSE: (AbsAddressF,         //  in MSF format : Frames (0..74d),
  128.                 AbsAddressS,         //    Seconds (0..59d) and
  129.                 AbsAddressM,         //    Minutes (0..74d)
  130.                 AbsAddressR,         //    And reserved byte field in MSB
  131.                 RelAddressF,
  132.                 RelAddressS,
  133.                 RelAddressM,
  134.                 RelAddressR : BYTE);
  135.    end;
  136. { =============== CD TABLE-OF-CONTENT DEFINITION =============== }
  137.    TCdRomTrackDescriptor = packed record
  138.       TrackNumber  : byte;      // Track no 0AAh is lead-out area
  139.       ADR          : TsubchannelADRinfo;
  140.       PreEmphasis  : BOOLEAN;   // TRUE=Audio track with pre-emphasis
  141.       CopyPermit   : BOOLEAN;   // TRUE=Digital copy permitted
  142.                                 // FALSE=Digital copy prohibited
  143.       DataTrack    : BOOLEAN;   // TRUE=Data track, FALSE=Audio track
  144.       QuadAudio    : BOOLEAN;   // TRUE=Four-channel audio
  145.                                 // FALSE=Two-channel audio
  146.       case BOOLEAN of             // Absolute linear address of track
  147.         TRUE : (Address : DWORD); //  in LBA format
  148.         FALSE: (AddressF,         //  in MSF format : Frames (0..74d),
  149.                 AddressS,         //    Seconds (0..59d) and
  150.                 AddressM,         //    Minutes (0..74d)
  151.                 AddressR : BYTE); //    And reserved byte field in MSB
  152.    end;
  153.    TCdRomToc = packed record
  154.       Track      : array[0..99] of TCdRomTrackDescriptor;
  155.       FirstTrack : integer;
  156.       LastTrack  : integer;
  157.       TrackCount : integer;   // Total tracks amount
  158.       InMSF      : BOOLEAN;   // Track[i].Address format:
  159.                               //   TRUE=MSF, FALSE=LBA
  160.    end;
  161. { ========== CD-ROM MODE SELECT/SENSE DEFINITIONS =========== }
  162.    TCdRomModePageType = (MPTcurrent, MPTchangeable, MPTdefault, MPTsaved);
  163. var
  164.    TCdRomModePageTypeName : array[TCdRomModePageType] of string =
  165.       ('MPTcurrent', 'MPTchangeable', 'MPTdefault', 'MPTsaved');
  166. type
  167.    TCdRomMediumType = (CDTdefault, CDTdata, CDTaudio, CDTmixed,
  168.                  CDTreserved, CDTdata80, CDTaudio80, CDTmixed80, CDTnone);
  169.          { 'CDTmixed' means that medium may contain data and audio tracks,
  170.            'CDTdefault' - that device supports only one medium type (??),
  171.             suffix '80' - that media is a 80 mm diameter disk instead the
  172.             default 120 mm.  }
  173. var
  174.    TCdRomMediumName : array[TCdRomMediumType] of string = ('Default',
  175.        'CD-ROM 5"', 'CD-Audio 5"', 'CD-XA 5"', 'Unknown',
  176.        'CD-ROM 3"', 'CD-Audio 3"', 'CD-XA 3"', 'None');
  177. type
  178.    TCdRomBlockDescriptor = record
  179.       Density  : BYTE;     // type of physical sectors on CD (see below)
  180.       BlkCount : DWORD;    // amount of sectors with declared size
  181.       BlkSize  : DWORD;    // physical sector size in bytes
  182.    end;
  183.      { CD-ROM density codes:
  184.      00h = Default density code (the only density supported by target)
  185.      01h = CD-ROM mode 1 - User data only (2048 bytes per physical sector)
  186.      02h = CD-ROM mode 2 - User data plus auxiliary data field (2336 bytes)
  187.      03h = 4-byte tag field, user data plus auxiliary data     (2340 bytes)
  188.      04h = CD-Audio mode - 1/75th of a second per logical block
  189.      values 05h-7Fh are reserved,  80h-FFh - vendor-specific.   }
  190.    TCdRomModeHeader = record
  191.       Medium   : TCdRomMediumType; // see descriptions above
  192.       DPOFUA   : BOOLEAN;          // DPO/FUA (see SCSIread10) valid if TRUE
  193.       BDlength : BYTE;             // real number of records in BD field
  194.       BD       : array[0..99] of TCdRomBlockDescriptor;
  195.    end;
  196.    TCdRomModePageAudio = record
  197.       PSAV,                 // TRUE means parameter saving capability
  198.       IMM,                  // TRUE enables background audio playback
  199.       SOTC,                 // TRUE enables multi-track audio playback
  200.       APRV : BOOLEAN;       // LBAformat and LBSaudio are valid if TRUE
  201.       LBAformat : BYTE;     // LBSaudio fractional coeff.index (1 or 1/256)
  202.       LBSaudio  : WORD;     // LBAs per second in audio mode
  203.       Volume,               // volume for indexed target's audio output
  204.                             // port. 00h is silence, FFh is maximum.
  205.       Channel :             // set of disc's audio channels, connected to
  206.        array[0..3] of BYTE; // indexed output port (ORed bitset 00h..0Fh)
  207.    end;
  208. { ================ CD-ROM CLASS DEFINITION ================= }
  209.    TCdRom = class(TASPIDevice)
  210.      private
  211.         fLocked : BOOLEAN;
  212.      protected
  213.         procedure SetLockUnlock(Value : boolean);
  214.         procedure ChangeDevice(Value : TdeviceID); override;
  215.      public
  216.         constructor Create(AOwner : TObject);
  217.         destructor  Destroy; override;
  218.              {============== MANDATORY SCSI-II COMMANDS ============}
  219.         function SCSItestReady : BOOLEAN;       // True if READY
  220.         function SCSIrequestSense(Pbuf : pointer; BufLen : BYTE) : BOOLEAN;
  221.         function SCSIinquiry(Vital : BOOLEAN; CodePage : BYTE;
  222.                                   Pbuf : pointer; BufLen : BYTE) : BOOLEAN;
  223.         function SCSIreserve : BOOLEAN;
  224.         function SCSIrelease : BOOLEAN;
  225.         function SCSIreserve3rdParty(TrdPartyDeviceID: BYTE)     : BOOLEAN;
  226.         function SCSIrelease3rdParty(TrdPartyDeviceID: BYTE)     : BOOLEAN;
  227.         function SCSIselfTest(DOFF, UOFF : BOOLEAN)              : BOOLEAN;
  228.         function SCSIreadCapacity(var BlkCount, BlkSize : DWORD) : BOOLEAN;
  229.         function SCSIreadCapacityPM(Partition : WORD; GLBA : DWORD;
  230.                                   var BlkCount, BlkSize : DWORD) : BOOLEAN;
  231.         function SCSIread10(DPO,FUA: boolean; GLBA: DWORD; Sectors: WORD;
  232.                                   Buf : pointer; BufLen : DWORD) : BOOLEAN;
  233.              {============== OPTIONAL SCSI-II COMMANDS =============}
  234.         function SCSIrezeroUnit                                  : BOOLEAN;
  235.         function SCSIstartStopUnit(STRT, LOEJ, IMM : boolean)    : BOOLEAN;
  236.         function SCSIseek10(GLBA : DWORD)                        : BOOLEAN;
  237.         function SCSIpreFetch(IMM : BOOLEAN;
  238.                                   GLBA : DWORD;  Sectors : WORD) : BOOLEAN;
  239.         function SCSIsynchronizeCache(IMM : BOOLEAN;
  240.                                   GLBA : DWORD;  Sectors : WORD) : BOOLEAN;
  241.         function SCSIlockUnlockCache(LOK : boolean;
  242.                                   GLBA : DWORD;  Sectors : WORD) : BOOLEAN;
  243.         function SCSIreadLong(CORR : BOOLEAN; GLBA : DWORD;
  244.                                   Buf : pointer; BufLen : DWORD) : BOOLEAN;
  245.         function SCSIreadSubchannel(MSFform : BOOLEAN;
  246.                                       var Info : TCdRomSubQinfo) : BOOLEAN;
  247.         function SCSIreadToc(MSFform : BOOLEAN; Start : BYTE;
  248.                                             var Toc : TCdRomToc) : BOOLEAN;
  249.         function SCSIreadHeaderLBA(GLBA : DWORD;
  250.                          var CDmode : BYTE; var Address : DWORD) : BOOLEAN;
  251.         function SCSIreadHeaderMSF(GLBA : DWORD;
  252.                 var CDmode, AddressM, AddressS, AddressF : BYTE) : BOOLEAN;
  253.         function SCSImodeSelectEX(PF, SP : BOOLEAN;
  254.                                 Buf : pointer; BufLen : DWORD)   : BOOLEAN;
  255.         function SCSImodeSelect(Buf : pointer; BufLen : DWORD)   : BOOLEAN;
  256.         function SCSImodeSense(DBD : BOOLEAN; PCTL : TCdRomModePageType;
  257.                      PAGE : BYTE; Buf : pointer; BufLen : DWORD) : BOOLEAN;
  258.    // The function set below have to describe all possible MODE SELECT
  259.    // and MODE SENSE implementations for various mode pages. Almost
  260.    // each function is in two forms - full-featured with 'EX' suffix
  261.    // in name, and shortened-form (without any suffixes).
  262.    // For now, the only MODE SENSE is implemented. It is necessary to
  263.    // manipulate with mode parameters highly carefull. I'm not ready
  264.    // yet. That's why.
  265.    //     Unimplemented mode pages are:
  266.    // 02h Disconnect-reconnect page  
  267.    // 09h Peripheral device page      > unuseful with ATAPI devices
  268.    // 0Ah Control mode page          /
  269.    // 07h Verify error recovery page - verify command are not used
  270.    // 08h Caching page            - do you need to control CD-ROM's cache?
  271.         function SCSImodeSenseHeader(var sh : TCdRomModeHeader)  : BOOLEAN;
  272.         function SCSImodeSenseRecoverEX(PCTL: TCdRomModePageType;
  273.                   var PSAV : BOOLEAN; var RLEV, RETR : BYTE)     : BOOLEAN;
  274.         function SCSImodeSenseRecover(var RLEV, RETR : BYTE)     : BOOLEAN;
  275.         function SCSImodeSenseMediumEX(PCTL : TCdRomModePageType;
  276.                      var PSAV : BOOLEAN;
  277.                      var Med1,Med2,Med3,Med4 : TCdRomMediumType) : BOOLEAN;
  278.         function SCSImodeSenseDeviceEX(PCTL : TCdRomModePageType;
  279.                      var PSAV : BOOLEAN; var ITimer : BYTE;
  280.                      var SperMunits, FperSunits : WORD)          : BOOLEAN;
  281.         function SCSImodeSenseDevice(var ITimer : BYTE)          : BOOLEAN;
  282.         function SCSImodeSenseAudioEX(PCTL : TCdRomModePageType;
  283.                                    var sh : TCdRomModePageAudio) : BOOLEAN;
  284.         function SCSImodeSenseAudio(var sh: TCdRomModePageAudio) : BOOLEAN;
  285.          {============== OPTIONAL AUDIO PLAYBACK COMMANDS =============}
  286.         function SCSIplayAudio10(GLBA : DWORD; Sectors : WORD)   : BOOLEAN;
  287.         function SCSIplayAudio12(GLBA, Sectors : DWORD)          : BOOLEAN;
  288.         function SCSIplayAudioMSF(StartM, StartS, StartF,
  289.                                   StopM, StopS, StopF : BYTE )   : BOOLEAN;
  290.         function SCSIplayAudioTI(StartTrack, StartIndex,
  291.                                    StopTrack, StopIndex : BYTE ) : BOOLEAN;
  292.         function SCSIplayAudioR10(TRLBA : DWORD;
  293.                               StartTrack : BYTE; Sectors : WORD) : BOOLEAN;
  294.         function SCSIplayAudioR12(TRLBA : DWORD;
  295.                             StartTrack : BYTE;  Sectors : DWORD) : BOOLEAN;
  296.         function SCSIpauseAudio                                  : BOOLEAN;
  297.         function SCSIresumeAudio                                 : BOOLEAN;
  298.         property Locked : BOOLEAN read fLocked write SetLockUnlock;
  299.      end;
  300. implementation
  301. constructor TCdRom.Create;
  302.    begin
  303.       inherited Create(AOwner);
  304.       fLocked := FALSE;
  305.    end;
  306. destructor  TCdRom.Destroy;
  307.    begin
  308.       SetLockUnlock(FALSE);               // Unlock the medium
  309.       SCSIlockUnlockCache(FALSE, 0, 0);   // Unlock entire cache
  310.       inherited Destroy;
  311.    end;
  312. procedure TCdRom.ChangeDevice(Value : TdeviceID);
  313.    begin
  314.       SetLockUnlock(FALSE);               // Unlock the medium
  315.       SCSIlockUnlockCache(FALSE, 0, 0);   // Unlock entire cache
  316.       inherited ChangeDevice(Value);
  317.    end;
  318.    {======================= MANDATORY SCSI-II COMMANDS ====================}
  319.  { TEST UNIT READY command provides a means to check if the logical unit
  320.    is ready.  This is not a request for a self-test.  It is especially
  321.    useful to check cartridge status of logical units with removable media.
  322. =====================   Possible LastError codes:   =========================
  323. |Status|Sense key|        ASC and ASCQ                |LastError code(Err_..)
  324. |------+---------+------------------------------------+---------------------|
  325. | GOOD |   ---   |                ---                 | None                |
  326. | BUSY |   ---   |                ---                 | TargetBusy          |
  327. | RESERVATION CONFLICT            ---                 | ReservationConflict |
  328. |------+---------+------------------------------------+---------------------|
  329. | CHECK| ILLEGAL | LOGICAL UNIT NOT SUPPORTED         | SenseIllegalRequest |
  330. |CONDI-| REQUEST |                                    |                     |
  331. | TION ----------+------------------------------------+---------------------|
  332. |      |   NOT   | LUN DOES NOT RESPOND TO SELECTION  | SenseNotReady       |
  333. |      |  READY  | MEDIUM NOT PRESENT                 |                     |
  334. |      |         | LUN IS IN PROCESS OF BECOMING READY|                     |
  335. |      |         | INIT COMMAND REQUIRED              |                     |
  336. |      |         | MANUAL INTERVENTION REQUIRED       |                     |
  337. |      |         | FORMAT IS IN PROGRESS              |                     |
  338. |      |         | LUN NOT READY,CAUSE NOT REPORTABLE |                     |
  339. |------+---------+------------------------------------+---------------------|}
  340. function TCdRom.SCSItestReady : BOOLEAN; //True if READY
  341.   begin result := ASPIsend6(0, 0, 0, NIL, 0, SRB_NODIR, ShortTimeout); end;
  342.  { REQUEST SENSE command (code 03h) is fully implemented in ASPI layer.
  343.    If still need it, you got it below. }
  344. function TCdRom.SCSIrequestSense(Pbuf : pointer; BufLen : BYTE) : BOOLEAN;
  345.   var  I : DWORD;
  346.   begin
  347.      I := DWORD(BufLen) AND $FF;
  348.      result := ASPIsend6(3, 0, BufLen, PBuf, I, SRB_NODIR, ShortTimeout);
  349.   end;
  350.  { INQUIRY command requests some parameters of the target and its attached
  351.    peripheral device(s). In the common case it seems like below.
  352.    More practical styled call is GetDeviceInfo included into TASPIdevice. }
  353. function TCdRom.SCSIinquiry(Vital : BOOLEAN; CodePage : BYTE;
  354.                                  Pbuf : pointer; BufLen : BYTE) : BOOLEAN;
  355.   var  I, J : DWORD;
  356.   begin
  357.      J := DWORD(CodePage) AND $FF;
  358.      if Vital then J := (J OR $100) SHL 8  else  J := 0;
  359.      I := DWORD(BufLen) AND $FF;
  360.      result := ASPIsend6($12, J, BufLen, PBuf, I, SRB_NODIR, ShortTimeout);
  361.   end;
  362.  {      RESERVE and RELEASE commands define how different types of
  363.    restricted access may be achieved, and to whom the access is restricted.
  364.    Use reservations to gain a level  of exclusivity in access to all or part
  365.    of the medium. It is expected that the reservation will be retained until
  366.    released. Partial medium reservation is excluded from this implementation
  367.    because it's not-so-useful and optional in SCSI-2 terms.
  368.         RESERVE requests that the entire logical unit be reserved for the
  369.    exclusive use until released by a RELEASE command or by a hard/soft RESET
  370.    condition.  A reservation is not granted if the logical unit is reserved
  371.    by another task. If, after honouring the reservation, any other initiator
  372.    attempts to perform any command on the reserved LUN other than INQUIRY or
  373.    REQUEST SENSE, it shall be rejected with Err_SenseReservationConflict
  374.    status.  }
  375. function TCdRom.SCSIreserve : BOOLEAN;
  376.   begin result := ASPIsend6($16, 0,0, NIL,0, SRB_NODIR, ShortTimeout); end;
  377. function TCdRom.SCSIrelease : BOOLEAN;
  378.   begin result := ASPIsend6($17, 0,0, NIL,0, SRB_NODIR, ShortTimeout); end;
  379.  {      The third-party reservation for  RESERVE  command allows to reserve
  380.    the logical unit  for another SCSI device specified in the third-party
  381.    DeviceID field.  This is intended for use in multiple-initiator systems
  382.    that use the COPY command. }
  383. function TCdRom.SCSIreserve3rdParty(TrdPartyDeviceID: BYTE): BOOLEAN;
  384.    var  I : DWORD;
  385.    begin
  386.       I := DWORD(TrdPartyDeviceID) AND 7;  I := (I SHL 17) OR $100000;
  387.       result := ASPIsend6($16, I, 0, NIL, 0, SRB_NODIR, ShortTimeout);
  388.    end;
  389. function TCdRom.SCSIrelease3rdParty(TrdPartyDeviceID: BYTE): BOOLEAN;
  390.    var  I : DWORD;
  391.    begin
  392.       I := DWORD(TrdPartyDeviceID) AND 7;  I := (I SHL 17) OR $100000;
  393.       result := ASPIsend6($17, I, 0, NIL, 0, SRB_NODIR, ShortTimeout);
  394.    end;
  395.  { SelfSets is the mandatory subset implementation of SEND DIAGNOSTIC
  396.    command. SEND DIAGNOSTIC requests the target to perform diagnostic
  397.    operations on itself. Here is only mandatory implementation of this
  398.    command: self-test feature with the  parameter list length of zero.
  399.    Full command implementation (with page structure) is OPTIONAL.
  400.    DOFF=TRUE (Device Off-line) and UOFF=TRUE (Unit Off-line) flags grant
  401.         permission to perform vendor-specific diagnostic operations on the
  402.         target that may be visible to attached initiators, e.g. writings
  403.         to the user accessible medium, or repositioning of the medium on
  404.         sequential access devices.  Setting this flags FALSE prohibits all
  405.         diagnostic operations that may be detected by other I/O processes.}
  406. function TCdRom.SCSIselfTest(DOFF, UOFF : BOOLEAN) : BOOLEAN;
  407.    var I : DWORD;
  408.    begin
  409.       I := $40000;
  410.       if DOFF then I := I OR $20000;
  411.       if UOFF then I := I OR $10000;
  412.       result := ASPIsend6($1D, I, 0, NIL, 0, SRB_NODIR, ShortTimeout);
  413.    end;
  414.  { READ CAPACITY command returns logical block address and block length
  415.    (in bytes) of the last valid logical block of the logical unit for seek
  416.    operations. BlkCount may be greater (up to +75) than or equal to the
  417.    last readable or playable block. If greater, this address may be in a
  418.    transition area beyond the last valid logical block for read or audio
  419.    play operations because the CD-ROM TOC (table of contents) lead-out
  420.    track location  has a +/- 75 sector tolerance when the lead-out track
  421.    is encoded as an audio track. }
  422. function TCdRom.SCSIreadCapacity(var BlkCount, BlkSize : DWORD) : BOOLEAN;
  423.    var cd  : array[0..1] of DWORD;
  424.    begin
  425.       FillChar(cd,  sizeof(cd),  0);
  426.       result := ASPIsend10($25, 0,0,0,0, @cd,8, SRB_DIR_IN, ShortTimeout);
  427.       BlkCount := BigEndianD(cd[0]);
  428.       BlkSize  := BigEndianD(cd[1]);
  429.    end;
  430.  { READ CAPACITY command with enabled partial medium feature returns
  431.    logical block address and block length (in bytes) of the last logical
  432.    block address after which a substantial delay in data transfer will be
  433.    encountered. This logical block address shall be greater than or equal
  434.    to the logical block address specified in GLBA parameter.
  435.    On CD-ROM media, this is interpreted as being the last readable or
  436.    playable logical block beyond the specified GLBA.
  437.    NOTE: This option may take several seconds to complete.    }
  438. function TCdRom.SCSIreadCapacityPM(Partition : WORD; GLBA : DWORD;
  439.                                 var BlkCount, BlkSize : DWORD) : BOOLEAN;
  440.    var cd  : array[0..1] of DWORD;
  441.    begin
  442.       FillChar(cd,  sizeof(cd),  0);
  443.       result := ASPIsend10($25,0,GLBA,0,((Partition AND $7F) SHL 1) OR 1,
  444.                                        @cd, 8, SRB_DIR_IN, LongTimeout);
  445.       BlkCount := BigEndianD(cd[0]);
  446.       BlkSize  := BigEndianD(cd[1]);
  447.    end;
  448.  { READ(10) command requests data from the target.
  449.    DPO (Disable Page Out) flag is used to control replacement of logical
  450.        blocks in the device's cache memory when the host has information
  451.        on the future usage of the logical blocks.
  452.        DPO = TRUE indicates that the target shall assign the logical
  453.        blocks accessed the lowest caching priority. It overrides any
  454.        retention priority specified in the cache page. If the DPO = TRUE,
  455.        the host knows the logical blocks are not likely to be accessed
  456.        again in the near future and should not be put in the cache
  457.        memory nor retained by the cache memory.
  458.        DPO = FALSE means that the host expects that logical blocks
  459.        accessed by this command are likely to be accessed again in the
  460.        near future and the priority shall be determined by the retention
  461.        priority fields in the cache page.
  462.    FUA (Force Unit Access) flag controls cache using too.
  463.        FUA = TRUE indicates that the target shall access the specified
  464.        logical blocks from the media (i.e.  the data is not directly
  465.        retrieved from the cache).
  466.        FUA = FALSE means that any logical blocks that are contained in
  467.        the cache memory may be transferred directly from the cache memory.
  468.    GLBA is a number of first logical block to transfer.
  469.    Sectors is a number of contiguous logical blocks of data that shall be
  470.        transferred.  A transfer length of zero indicates that no logical
  471.        blocks shall be transferred (this shall not be considered an error).
  472.    BufLen is the length of data buffer "Buf" in bytes.  }
  473. function TCdRom.SCSIread10(DPO,FUA: boolean; GLBA: DWORD; Sectors: WORD;
  474.                                  Buf : pointer; BufLen : DWORD) : BOOLEAN;
  475.    var Arg1 : byte;
  476.    begin
  477.       FillChar(Buf^, BufLen, 0);
  478.       Arg1 := 0;  if DPO then Arg1:=$10;  if FUA then Arg1:=Arg1 OR 8;
  479.       result := ASPIsend10($28, Arg1, GLBA, 0, Sectors,
  480.                                 Buf, BufLen, SRB_DIR_IN, MediumTimeout);
  481.    end;
  482.    {======================= OPTIONAL SCSI-II COMMANDS ====================}
  483.  { REZERO UNIT command is used in some devices to position the actuator at
  484.    cylinder zero.  Some devices return GOOD status without attempting any
  485.    action. }
  486. function TCdRom.SCSIrezeroUnit : BOOLEAN;
  487.    begin result := ASPIsend6(1,0,0,NIL,0, SRB_NODIR, ShortTimeout); end;
  488.  { START STOP UNIT command requests that the target enable or disable the
  489.    logical unit for media access operations.  Targets that contain cache
  490.    memory shall implicitly perform a  SYNCHRONIZE CACHE  command for the
  491.    entire medium prior to executing the STOP UNIT command.
  492.    STRT=TRUE requests the LUN be made ready for use, STRT=FALSE - be stopped
  493.         (media cannot be accessed by the initiator).
  494.    LOEJ=TRUE enables medium to be loaded (STRT=TRUE) or ejected (STRT=FALSE)
  495.         LOEJ=FALSE requests that no medium action be taken.
  496.    IMM=TRUE indicates that status shall be returned as soon as the CDB has
  497.         been validated,  else status shall be returned after the operation
  498.         is completed.   }
  499. function TCdRom.SCSIstartStopUnit(STRT, LOEJ, IMM : boolean) : BOOLEAN;
  500.    var
  501.       Arg1 : DWORD;
  502.       Arg2 : byte;
  503.    begin
  504.       Arg1 := 0;   if IMM then Arg1 := $10000;
  505.       Arg2 := 0;   if LOEJ then Arg2:=2;  if STRT then Arg2:=Arg2 OR 1;
  506.       result := ASPIsend6($1B, Arg1, Arg2, NIL, 0,
  507.                                 SRB_NODIR, MediumTimeout);
  508.    end;
  509.  { PREVENT ALLOW MEDIUM REMOVAL enables or disables the removal of the
  510.    medium in the unit. While prevention of medium removal condition is in
  511.    effect the target shall inhibit mechanisms that normally allow removal
  512.    of the medium by an operator.  This mechanism is independent of device
  513.    reservations and the target shall not allow medium removal if any
  514.    initiator currently has medium removal prevented.
  515.         The prevention of medium removal shall terminate:
  516.    a) after all initiators that have medium removal prevented issue this
  517.       commands with a prevent bit of zero, and the target has successfully
  518.       performed a synchronize cache operation;
  519.    b) upon the receipt of a BUS DEVICE RESET message from any initiator;
  520.    c) upon a hard RESET condition.
  521.    NOTE: We will use it only as property's SET procedure. }
  522. procedure TCdRom.SetLockUnlock(Value : boolean);
  523.    begin
  524.       if Value = fLocked then exit;
  525.       if Value then ASPIsend6($1E,0,1,NIL,0,SRB_NODIR, ShortTimeout)
  526.                else ASPIsend6($1E,0,0,NIL,0,SRB_NODIR, ShortTimeout);
  527.       fLocked := Value;
  528.    end;
  529.  { SEEK(10) requests LUN seek to the specified logical block address. }
  530. function TCdRom.SCSIseek10(GLBA : DWORD) : BOOLEAN;
  531.    begin
  532.       result := ASPIsend10($2B,0,GLBA,0,0,NIL,0,SRB_NODIR, MediumTimeout);
  533.    end;
  534.  { PRE-FETCH requests that the target transfer the specified logical blocks
  535.    to the cache memory.  No data shall be transferred to the initiator.
  536.    The target shall transfer to cache memory as many logical blocks as
  537.    will fit and may elect to not transfer logical blocks that already are
  538.    contained in the cache memory.
  539.    IMM=TRUE indicates that status shall be returned as soon as the CDB has
  540.         been validated,  else status shall be returned after the operation
  541.         is completed.
  542.    GLBA and Sectors specifies the block range.  Sectors=0  indicates that
  543.         all remaining logical blocks shall be within the range. }
  544. function TCdRom.SCSIpreFetch(IMM : BOOLEAN;
  545.                               GLBA : DWORD;  Sectors : WORD) : BOOLEAN;
  546.    begin
  547.       if IMM then result := ASPIsend10($34, 2, GLBA, 0, Sectors,
  548.                                  NIL, 0, SRB_NODIR, ShortTimeout)
  549.              else result := ASPIsend10($34, 0, GLBA, 0, Sectors,
  550.                                  NIL, 0, SRB_NODIR, MediumTimeout);
  551.    end;
  552.  { SYNCHRONIZE CACHE ensures that logical blocks in the cache memory,
  553.    within the specified range,  have their most recent data value recorded
  554.    on the physical medium. If a more recent data value exists in the cache
  555.    memory than on the physical medium, then all data from the cache memory
  556.    shall be written to the physical medium.  Logical blocks are not
  557.    necessarily removed from the cache memory as a result of the operation.
  558.    IMM=TRUE indicates that status shall be returned as soon as the CDB has
  559.         been validated,  else status shall be returned after the operation
  560.         is completed.  If IMM=TRUE and the target does not support it then
  561.         the command shall terminate with Err_SenseIllegalRequest status.
  562.    GLBA and Sectors specifies the block range.  Sectors=0  indicates that
  563.         all remaining logical blocks shall be within the range. }
  564. function TCdRom.SCSIsynchronizeCache(IMM : BOOLEAN;
  565.                               GLBA : DWORD;  Sectors : WORD) : BOOLEAN;
  566.    begin
  567.       if IMM then result := ASPIsend10($35, 2, GLBA, 0, Sectors, NIL, 0,
  568.                                        SRB_NODIR, ShortTimeout)
  569.              else result := ASPIsend10($35, 0, GLBA, 0, Sectors, NIL, 0,
  570.                                        SRB_NODIR, MediumTimeout);
  571.    end;
  572.  { LOCK UNLOCK CACHE requests that the target disallow or allow logical
  573.    blocks within the specified range to be removed from the cache memory
  574.    by the target's cache replacement algorithm. Locked logical blocks may
  575.    be written to the medium when modified, but a copy of the modified
  576.    logical block shall  remain in the cache memory.  Multiple locks may be
  577.    in effect from more than one initiator. Locks from different initiators
  578.    may overlap.  An unlock of an overlapped area does not release the lock
  579.    of another initiator.
  580.    LOK=TRUE means that any logical block  in the specified range  that is
  581.         currently present in the cache memory  shall be locked into cache
  582.         memory. Only logical blocks that are already present in the cache
  583.         memory are actually locked.  LOK=FALSE indicates that all logical
  584.         blocks in the specified range  that are currently locked into the
  585.         cache memory shall be unlocked, but not necessarily removed.
  586.    GLBA and Sectors specifies the block range.  Sectors=0  indicates that
  587.         all remaining logical blocks shall be within the range. }
  588. function TCdRom.SCSIlockUnlockCache(LOK : boolean;
  589.                               GLBA : DWORD;  Sectors : WORD) : BOOLEAN;
  590.    var Arg1 : byte;
  591.    begin
  592.       if LOK then  Arg1 := 2  else  Arg1 := 0;
  593.       result := ASPIsend10($36, Arg1, GLBA, 0, Sectors, NIL, 0,
  594.                                 SRB_NODIR, ShortTimeout);
  595.    end;
  596.  { READ LONG requests to transfer data to the initiator.  This data is
  597.    vendor-specific, but shall include the data bytes and the ECC bytes
  598.    recorded on the medium. Any other bytes that can be corrected by ECC
  599.    should be included (e.g. data synchronization mark within the area
  600.    covered by ECC). All tranferred bytes should be in the same order as
  601.    they are on the media.
  602.    CORR=TRUE (Corrected) causes the data to be corrected by ECC before
  603.        being transferred to the initiator, CORR=FALSE causes a LB to be
  604.        read without any correction made by the target.
  605.    GLBA is a number of first logical block to transfer.
  606.    BufLen specifies the number of bytes of data that are available for
  607.        transfer.  If it does not exactly match the available data length,
  608.        the target shall terminate the command with Err_SenseIllegalRequest
  609.        status. BufLen=0 indicates that no bytes shall be transferred and
  610.        shall not be considered an error.  }
  611. function TCdRom.SCSIreadLong(CORR : BOOLEAN; GLBA : DWORD;
  612.                               Buf : pointer; BufLen : DWORD) : BOOLEAN;
  613.    var Arg1 : byte;
  614.    begin
  615.       FillChar(Buf^,  BufLen,  0);
  616.       if CORR then  Arg1 := 2  else  Arg1 := 0;
  617.       result := ASPIsend10($3E, Arg1, GLBA, 0, BufLen,
  618.                                 Buf, BufLen, SRB_DIR_IN, MediumTimeout);
  619.    end;
  620.  { READ SUB-CHANNEL requests the sub-channel data plus the state of audio
  621.    play operations. Sub-channel data returned by this command may be from
  622.    the last appropriate sector encountered by a current or previous media
  623.    accessing operation. When there is no current audio play operation, the
  624.    target may access the media to read the sub-channel data. The target is
  625.    responsible that the data returned are current and consistent.
  626.    MSFform=TRUE requests that MSF format be used for address fields, FALSE
  627.        requests to LBA format. }
  628. function TCdRom.SCSIreadSubchannel(MSFform : BOOLEAN;
  629.                               var Info : TCdRomSubQinfo) : BOOLEAN;
  630.    type SCSI_SubQinfo = packed record
  631.        Reserved      : byte;
  632.        AudioStatus   : byte;
  633.        DataLength    : word;  // byte-reversed
  634.        FormatCode    : byte;  // no matter
  635.        ControlAndADR : byte;
  636.        TrackNumber   : byte;
  637.        IndexNumber   : byte;
  638.        AbsoluteAddr  : DWORD; // byte-reversed
  639.        RelativeAddr  : DWORD; // byte-reversed
  640.        MCVal : byte;                 // if bit7=1 then UPC is valid
  641.        UPC   : array[0..14] of char; // Media catalogue number(UPC/Bar code)
  642.        TCVal : byte;                 // if bit7=1 then ISRC is valid
  643.        ISRC  : array[0..14] of char; // Track international standard
  644.      end;                            //      recording code (ISRC)
  645.    var
  646.      Arg1, B : byte;
  647.      cd      : SCSI_SubQinfo;
  648.      Len     : WORD;
  649.      i       : integer;
  650.    begin
  651.       FillChar(cd,  sizeof(cd),  0);
  652.       if MSFform then Arg1 := 2  else  Arg1 := 0;
  653.       result := ASPIsend10($42, Arg1, $40000000, 0, sizeof(cd),
  654.                            @cd, sizeof(cd), SRB_DIR_IN, MediumTimeout);
  655.       Len := BigEndianW(cd.DataLength);
  656.       case cd.AudioStatus of
  657.          0   : Info.AudioStatus := AudioNone;
  658.          $11 : Info.AudioStatus := AudioPlay;
  659.          $12 : Info.AudioStatus := AudioPause;
  660.          $13 : Info.AudioStatus := AudioComplete;
  661.          $14 : Info.AudioStatus := AudioError;
  662.          $15 : Info.AudioStatus := AudioStop
  663.          else  Info.AudioStatus := AudioInvalid;
  664.       end;
  665.       B := (cd.ControlAndADR SHR 4) AND $0F;
  666.       if B >= 4 then Info.ADR := ADRreserved
  667.                 else Info.ADR := TsubchannelADRinfo(B);
  668.       Info.PreEmphasis  := (cd.ControlAndADR AND 1) <> 0;
  669.       Info.CopyPermit   := (cd.ControlAndADR AND 2) <> 0;
  670.       Info.DataTrack    := (cd.ControlAndADR AND 4) <> 0;
  671.       Info.QuadAudio    := (cd.ControlAndADR AND 8) <> 0;
  672.       Info.TrackNumber  := cd.TrackNumber;
  673.       Info.IndexNumber  := cd.IndexNumber;
  674.       Info.AbsAddress   := BigEndianD(cd.AbsoluteAddr);
  675.       Info.RelAddress   := BigEndianD(cd.RelativeAddr);
  676.       Info.UPC := '';    i := 0;
  677.       if (cd.MCVal AND $80) <> 0 then
  678.        while (i <= 14) AND (Len >= (i + 14)) do
  679.          begin   Info.UPC  :=  Info.UPC  + cd.UPC[i];   i := i + 1;   end;
  680.       Info.ISRC := '';    i := 0;
  681.       if (cd.TCVal AND $80) <> 0 then
  682.        while (i <= 14) AND (Len >= (i + 30)) do
  683.          begin   Info.ISRC := Info.ISRC + cd.ISRC[i];   i := i + 1;   end;
  684.       if Len < 12 then begin  result := FALSE;  exit;  end;
  685.    end;
  686.  { READ TOC requests that the target transfers the table of contents.
  687.    NOTE: The maximum TOC data length possible on ISO-9660 CD-ROM media
  688.    is 804 bytes, or 100 TOC track descriptors. Track number 0 is CD lead-in
  689.    area, track number 0AAh is CD lead-out area, and they are not accessible
  690.    through SCSI.
  691.    MSFform=TRUE requests that MSF format be used for LBA fields, i.e. least
  692.        significant byte should be the Frame number (0..74d), next byte -
  693.        Second number (0..59d), next byte - Minute number (0..74bcd), and
  694.        the MSB of DWORD is reserved. In certain transition areas, the
  695.        relative MSF are decreasing positive values.  The absolute MSF
  696.        addresses are always increasing positive values.
  697.        MSF=FALSE requests that the logical block address format be used
  698.        for the CD-ROM absolute address field or for the offset from the
  699.        beginning of the current track expressed as a number of LBs in a
  700.        CD-ROM track relative address field.  This track relative logical
  701.        block address (TRLBA) value is reported as a negative value in
  702.        twos-complement notation for transition areas that have decreasing
  703.        MSF encoded relative addresses.
  704.    Start field specifies the starting track number for which the data
  705.        shall be returned. The data are returned in contiguous ascending
  706.        track number order. If the starting track field is not valid for
  707.        the currently installed medium,  the command shall be terminated
  708.        with Err_SenseIllegalRequest status. Start=0 means entire table of
  709.        contents request and it's the only way we recommend to use this
  710.        function.
  711.    The returned data format shown below in SCSI_Toc record structure.
  712.    The TOC data block contains a 4-byte header followed by zero or more
  713.    TOC track descriptors. The first track number is not required to be one.
  714.    A disc may start at any valid track number.  The track numbers between
  715.    the first and the last track number are required to be in contiguous
  716.    ascending order, except for the lead-out track (always numbered 0AAh).
  717.    NOTE: The starting logical block address value recovered from the TOC
  718.    has a tolerance of zero for data tracks and plus or minus 75 CD sectors
  719.    for audio tracks. }
  720. function TCdRom.SCSIreadToc(MSFform : BOOLEAN; Start : BYTE;
  721.                                             var Toc: TCdRomToc) : BOOLEAN;
  722.    type
  723.      SCSI_TrackDescriptor = packed record
  724.         Reserved1    : byte;
  725.         ControlAndADR: byte;  //bitset: D7..D4=ADR, D3..D0=Control
  726.         TrackNumber  : byte;
  727.         Reserved2    : byte;
  728.         AbsAddress   : DWORD; //byte-reversed!
  729.      end;
  730.      SCSI_Toc = packed record
  731.         Length    : WORD;  //byte-reversed!
  732.         FirstTrack: byte;
  733.         LastTrack : byte;
  734.         Tracks    : array[0..99] of SCSI_TrackDescriptor;
  735.      end;
  736.    var
  737.       cd      : SCSI_Toc;
  738.       cdt     : SCSI_TrackDescriptor;
  739.       Arg1, B : byte;
  740.       i       : integer;
  741.    begin
  742.       FillChar(cd,  sizeof(cd),  0);
  743.       if MSFform then  Arg1 := 2  else  Arg1 := 0;
  744.       result := ASPIsend10($43, Arg1, 0, Start, sizeof(cd),
  745.                            @cd, sizeof(cd), SRB_DIR_IN, ShortTimeout);
  746.       if NOT result then begin
  747.          Toc.FirstTrack := 0;         Toc.LastTrack  := 0;
  748.          Toc.TrackCount := 0;         exit;
  749.       end;
  750.       Toc.FirstTrack := integer(cd.FirstTrack) AND $00FF;
  751.       Toc.LastTrack  := integer(cd.LastTrack)  AND $00FF;
  752.       Toc.TrackCount := (integer(BigEndianW(cd.Length))-2) DIV sizeof(cdt);
  753.       Toc.InMSF      := MSFform;
  754.       for i := 0 to Toc.TrackCount-1 do begin
  755.          cdt := cd.Tracks[i];
  756.          with Toc.Track[i] do begin
  757.             Address      := BigEndianD(cdt.AbsAddress);
  758.             TrackNumber  := cdt.TrackNumber;
  759.             B := (cdt.ControlAndADR SHR 4) AND $0F;
  760.             if B >= 4 then ADR := ADRreserved
  761.                       else ADR := TsubchannelADRinfo(B);
  762.             PreEmphasis  := (cdt.ControlAndADR AND 1) <> 0;
  763.             CopyPermit   := (cdt.ControlAndADR AND 2) <> 0;
  764.             DataTrack    := (cdt.ControlAndADR AND 4) <> 0;
  765.             QuadAudio    := (cdt.ControlAndADR AND 8) <> 0;
  766.    end; end; end;
  767.  { READ HEADER requests the CD-ROM data block address header of the
  768.    requested logical block.
  769.    CDmode field specifies the CD-ROM data mode of the logical blocks:
  770.               +======================================+
  771.               |CD-ROM|User data field|Auxiliary field|
  772.               | mode | (2 048 bytes) |  (288 bytes)  |
  773.               |------+---------------+---------------|
  774.               | 00h  |  All zeroes   |  All zeroes   |
  775.               | 01h  |  User data    |  L-EC symbols |
  776.               | 02h  |  User data    |  User data    |
  777.               |03-FFh|  Reserved     |  Reserved     |
  778.               +======================================+
  779.    Address field gives the address of the first logical block in the
  780.    physical sector where the data for the requested GLBA is found. }
  781. function TCdRom.SCSIreadHeaderLBA(GLBA : DWORD;
  782.                          var CDmode : BYTE; var Address : DWORD) : BOOLEAN;
  783.    type SCSI_HeaderInfo = packed record
  784.          Mode : BYTE;
  785.          Resv1,Resv2,Resv3 : BYTE;
  786.          Addr : DWORD;
  787.       end;
  788.    var  cd : SCSI_HeaderInfo;
  789.    begin
  790.       FillChar(cd, sizeof(cd), 0);
  791.       result := ASPIsend10($44, 0, GLBA, 0, sizeof(cd),
  792.                            @cd, sizeof(cd), SRB_DIR_IN, MediumTimeout);
  793.       CDmode  := cd.Mode;
  794.       Address := BigEndianD(cd.Addr);
  795.    end;
  796.  { Second implementation of READ HEADER requests that MSF format be used
  797.    for Address field. }
  798. function TCdRom.SCSIreadHeaderMSF(GLBA : DWORD;
  799.                 var CDmode, AddressM, AddressS, AddressF : BYTE) : BOOLEAN;
  800.    var  cd : array[0..7] of BYTE;
  801.    begin
  802.       FillChar(cd, sizeof(cd), 0);
  803.       result := ASPIsend10($44, 2, GLBA, 0, sizeof(cd),
  804.                            @cd, sizeof(cd), SRB_DIR_IN, MediumTimeout);
  805.       CDmode   := cd[0];
  806.       AddressM := cd[5];
  807.       AddressS := cd[6];
  808.       AddressF := cd[7];
  809.    end;
  810.  { MODE SELECT(10) is a means to specify medium or device parameters to
  811.    the target. The target may provide for independent sets of parameters
  812.    for each attached logical unit.
  813.    PF (Page Form)=TRUE indicates that parameters following the header and
  814.        block descriptor(s) are structured as pages as specified in SCSI-2,
  815.        PF=FALSE means that all parameters after the block descriptors are
  816.        vendor-specific according to SCSI-1.
  817.    SP (Save Pages)=TRUE indicates that the target (after performing the
  818.        specified MODE SELECT operation) shall save to a non-volatile
  819.        vendor-specific location all the savable pages.  The SP flag is
  820.        optional, even when mode pages are supported by the target.
  821.        If the target does not implement saved pages and the SP flag is
  822.        set to TRUE, the command shall be terminated with
  823.        Err_SenseIllegalRequest status.
  824.    BufLen is the length in bytes of the mode parameter list that shall be
  825.        transferred to the target. BufLen=0 indicates that no data shall be
  826.        transferred.  This condition shall not be considered as an error.
  827.    Command shall be terminated with Err_SenseIllegalRequest status and all
  828.    mode parameters shall be remains unchanged if:
  829.    a) It sets any field that is reported by the target as not changeable
  830.       to a value other than its current value.
  831.    b) It sets any field in the mode parameter header, block descriptor(s)
  832.       or mode parameter to an unsupported value.
  833.    c) It sends a mode page with a length not equal to the page length
  834.       returned by the MODE SENSE command for that page.
  835.    d) It sets any reserved field in the parameter list to a non-zero value.
  836.    e) BufLen results in the truncation of any mode parameter header, mode
  837.       parameter block descriptor(s), or mode page.
  838.    A target may alter any mode parameter in any mode page (even reported
  839.    as non-changeable) as a result of changes to other mode parameters.
  840.    NOTE: MODE SENSE command should be issued prior to each MODE SELECT to
  841.    determine supported mode pages, page lengths, and other parameters.
  842.    NOTE: The initiator should issue a MODE SENSE command with the PCTL=1
  843.    and PAGE=3Fh to determine which mode pages are supported, which mode
  844.    parameters within the pages are changeable, and the supported length
  845.    of each mode page prior to issuing any MODE SELECT commands.
  846.    NOTE: MODE SELECT does not support page code of 3Fh (all pages). }
  847. function TCdRom.SCSImodeSelectEX(PF, SP : BOOLEAN;
  848.                                  Buf : pointer; BufLen : DWORD) : BOOLEAN;
  849.    var  Arg1 : byte;
  850.    begin
  851.       if PF then  Arg1 := $10  else  Arg1 := 0;
  852.       if SP then  Arg1 := Arg1 OR 1;
  853.       result := ASPIsend10($55, Arg1, 0, 0, BufLen,
  854.                                 Buf, BufLen, SRB_DIR_OUT, ShortTimeout);
  855.    end;
  856. function TCdRom.SCSImodeSelect(Buf : pointer; BufLen : DWORD) : BOOLEAN;
  857.    begin  result := SCSImodeSelectEX(TRUE, FALSE, Buf, BufLen);  end;
  858.  { MODE SENSE(10) provides a means for a target to report parameters to
  859.    the initiator.  It is a complementary command to the MODE SELECT(10).
  860.    DBD (Disable Block Descriptors) = TRUE specifies that the target shall
  861.        not return any block descriptors in the returned MODE SENSE data.
  862.        DBD=FALSE  means that the target may return zero or more block
  863.        descriptors there, at the target's discretion.
  864.    PCTL (Page Control) field defines the type of mode parameter values to
  865.        be returned in the mode pages: 00b=Current Values, 01b=Changeable
  866.        Values, 10b=Default Values and 11b=Saved Values (see below).
  867.        NOTE: PCTL field only affects the mode parameters within the mode
  868.        pages, however the PS bit, page code and page length fields should
  869.        return current values since they have no meaning when used with
  870.        other types. The mode parameter header and mode parameter block
  871.        descriptor should return current values.
  872.        The current values (PCTL=0) are:
  873.          a) the values established by last successful MODE SELECT command;
  874.          b) the saved values if a MODE SELECT command has not successfully
  875.             completed since the last power-on, hard or soft RESET;
  876.          c) the default values, if saved values are not available or not
  877.             supported.
  878.        If the changeable values (PCTL=1) are selected, then all fields of
  879.        the mode parameters that are changeable shall be set to all one bits
  880.        and the fields of the mode parameters that are non-changeable (i.e.
  881.        defined by the target) shall be set to all zero bits.
  882.        NOTE: An attempt to change a non-changeable mode parameter (via MODE
  883.        SELECT) results in an error condition.
  884.        The default values (PCTL=2) are accessible even if the device is not
  885.        ready. Parameters not supported by the target shall be set to zero.
  886.        Implementation of saved (PCTL=3) page parameters is optional.
  887.        Mode parameters not supported by the target shall be set to zero.
  888.        If saved values are not implemented, the command shall return
  889.        Err_SenseIllegalRequest status.
  890.    PAGE specifies which mode page(s) to return. If requested page is not
  891.        implemented by the target, it shall return Err_SenseIllegalRequest
  892.        status.
  893.        A page code of 3Fh indicates that all pages implemented by the
  894.        target shall be returned. Pages should be returned in ascending
  895.        page code order except for mode page 00h. If implemented, it shall
  896.        be returned after all other mode pages.
  897.        NOTE: If the PCTL field and PAGE field are both set to zero, the
  898.        target should return a mode parameter header and block descriptor
  899.        (if applicable). This is for compatibility with SCSI-1 initiators.
  900.             +====================================================+
  901.             |Page code|            Description                   |
  902.             |---------+------------------------------------------+
  903.             |   01h   |  Read error recovery page                |
  904.             |   02h   |  Disconnect-reconnect page               |
  905.             |   07h   |  Verify error recovery page              |
  906.             |   08h   |  Caching page                            |
  907.             |   09h   |  Peripheral device page                  |
  908.             |   0Ah   |  Control mode page                       |
  909.             |   0Bh   |  Medium types supported page             |
  910.             |   0Dh   |  CD-ROM page                             |
  911.             |   0Eh   |  CD-ROM audio control page               |
  912.             | 20h-3Eh |Vendor-specific (page format required)    |
  913.             |   00h   |Vendor-specific (page format not required)|
  914.             +====================================================+   }
  915. function TCdRom.SCSImodeSense(DBD : BOOLEAN; PCTL : TCdRomModePageType;
  916.                     PAGE : BYTE; Buf : pointer; BufLen : DWORD) : BOOLEAN;
  917.    var
  918.       Arg1 : byte;
  919.       Arg2 : DWORD;
  920.    begin
  921.       FillChar(Buf^, BufLen, 0);
  922.       if DBD then  Arg1 := 8  else  Arg1 := 0;
  923.       Arg2 := ((Ord(PCTL) AND 3) SHL 6) OR (PAGE AND $3F);
  924.       result := ASPIsend10($5A, Arg1, Arg2 SHL 24, 0, BufLen,
  925.                                 Buf, BufLen, SRB_DIR_IN, ShortTimeout);
  926.    end;
  927. {============= MODE SELECT/MODE SENSE - Mode list header. =============}
  928.  { The mode parameter list used by MODE SELECT/MODE SENSE contains a header,
  929.    followed by zero or more block descriptors, followed by zero or more
  930.    variable-length pages. Here are procedures for header and descriptors
  931.    reading/modifying.
  932.    The list header consist of two useful parameters:
  933.    a) The medium-type code field defined by TCdRomMediumType type.
  934.       'mixed' means that medium may contain data and audio tracks,
  935.       'default' - that device supports only one medium type (??),
  936.       suffix '80' - that media is a 80 mm diameter disk instead the
  937.       default 120 mm.
  938.    b) The DPOFUA flag (not supported for MODE SELECT). If MODE SENSE
  939.       returns this flag TRUE, then target supports DPO and FUA flags
  940.       using (see SCSIread10 function description).
  941.    Each block descriptor contains three fields:
  942.    a) Density - Enumerated CD-ROM density codes:
  943.       00h = Default density code (the only density supported by target)
  944.       01h = CD-ROM mode 1 - User data only (2048 bytes per physical sector)
  945.       02h = CD-ROM mode 2 - User data plus auxiliary data field (2336 bytes)
  946.       03h = 4-byte tag field, user data plus auxiliary data     (2340 bytes)
  947.       04h = CD-Audio mode - 1/75th of a second per logical block
  948.       values 05h-7Fh are reserved,  80h-FFh - vendor-specific.
  949.       NOTE: The number of bytes per sector specified here is used with the
  950.       block length to map CD-ROM sectors to logical block addresses.
  951.    b) BlkCount - The number of logical blocks on the medium to which the
  952.       density code and block length fields apply.  A zero value indicates
  953.       that all of the remaining logical blocks of the logical unit shall
  954.       have the medium characteristics specified.
  955.    c) BlkSize - The length in bytes of each logical block described by
  956.       the block descriptor.   }
  957. function TCdRom.SCSImodeSenseHeader(var sh : TCdRomModeHeader) : BOOLEAN;
  958.    type SCSI_ModeHeader = packed record
  959.       ModeDataLength   : WORD;   // full record length except itself (SENSE only)
  960.       MediumType       : BYTE;
  961.       DeviceSpecific   : BYTE;   // $10 bit is DPOFUA flag
  962.       Reserved1        : WORD;
  963.       DescriptorLength : WORD;   // equal to 8*Number of Block Descriptors
  964.                                  // must be zero when DBD=TRUE
  965.       BDP              : array[0..511] of DWORD;  // intuitional choice :)
  966.      end;
  967.    var
  968.       cd : SCSI_ModeHeader;
  969.       i  : integer;
  970.    begin
  971.       FillChar(sh, sizeof(sh), 0);
  972.       result := SCSImodeSense(FALSE, MPTcurrent, $3F, @cd, sizeof(cd));
  973.       if result then begin
  974.          if cd.MediumType > 7 then sh.Medium := CDTreserved
  975.              else sh.Medium := TCdRomMediumType(cd.MediumType);
  976.          sh.DPOFUA   := (cd.DeviceSpecific AND $10) <> 0;
  977.          sh.BDlength := cd.DescriptorLength DIV 8;
  978.          for i := 0 to sh.BDlength - 1 do with sh.BD[i] do begin
  979.             Density  := BYTE($0FF AND cd.BDP[i SHL 1]);
  980.             BlkCount := $0FFFFFF AND BigEndianD(cd.BDP[i SHL 1]);
  981.             BlkSize  := $0FFFFFF AND BigEndianD(cd.BDP[(i SHL 1)+1]);
  982.    end; end; end;
  983. {======== MODE SELECT/MODE SENSE - Error recovery parameters page. ========}
  984.  { This page specifies the error recovery parameters the target shall use
  985.    during any command that performs a data read operation to the media
  986.    (e.g. READ, READ TOC, etc.).
  987.    PSAV (Parameters SAVable) = TRUE indicates that the target is capable
  988.        of saving the page in a non-volatile vendor-specific location.
  989.        This bit is only used with the MODE SENSE command.
  990.    RLEV (Recovery Level) is the set of five bits:
  991.        $20 = Transfer block bit. If set, data block that is not recovered
  992.              shall be transferred to data buffer anyway. This bit does not
  993.              affect the action taken for recovered data and does not
  994.              suppress the Err_SenseMediumError status of read commands.
  995.        $10 = Read continuous bit. If set, indicates the target shall
  996.              transfer the entire requested length of data without adding
  997.              delays to perform error recovery procedures.
  998.              This implies that the target may send data that is erroneous
  999.              or fabricated in order to maintain a continuous flow of data.
  1000.              This bit is typically used in audio or video applications.
  1001.        $04 = Post error bit. If set, target shall report recovered errors.
  1002.        $02 = Disable transfer on error bit. Ones indicates that the target
  1003.              shall terminate the data phase upon detection of a recovered
  1004.              error.
  1005.        $01 = Disable correction bit. If set, error correction codes shall
  1006.              not be used for data error recovery. This is applicable to
  1007.              layered error correction (L-EC) feature only. The retries and
  1008.              CIRC (Cross Interleaved Reed-solomon Code,the error detection
  1009.              and correction technique used within small (24 bytes) frames
  1010.              of audio or data) are performed anyway.
  1011.        The interpretation of these bit settings for CD-ROM devices and
  1012.        valid RLEV values are given below. If RLEV is set to any other
  1013.        value, the command shall be terminated with Err_SenseInvalidRequest
  1014.        status.
  1015.    00h/01h  If an uncorrectable error occurs, transfer is terminated
  1016.      with Err_SenseMediumError status. The block with the error is not
  1017.      transferred.  Recovered errors are not reported.
  1018.    04h/05h  Recovered errors are reported by Err_SenseRecoveredError,
  1019.      but data transfer is not terminated. Uncorrectable error terminates
  1020.      data transfer with Err_SenseMediumError status and the block with
  1021.      the error is not transferred.
  1022.    06h/07h  Recovered data error terminates data transfer with
  1023.      Err_SenseRecoveredError status. The block with the recovered error is
  1024.      not transferred.  Uncorrectable error terminates data transfer with
  1025.      Err_SenseMediumError status and errata block is not transferred.
  1026.    10h/11h  If an error occurs which is uncorrectable in time to maintain
  1027.      data transfer, the data transfer is not terminated. However, when the
  1028.      data transfer has completed, Err_SenseMediumError status is reported.
  1029.       Recovered errors are not reported.
  1030.    14h/15h  Data transfer is never terminated. Recovered errors are
  1031.       reported at completion by Err_SenseRecoveredError status,
  1032.       uncorrectable errors - by Err_SenseMediumError.  Reporting
  1033.       unrecovered errors takes precedence over reporting recovered errors.
  1034.    20h/21h/24h/25h/26h/27h  are the same as 00h/01h/04h/05h/06h/07h, but
  1035.       the block with the error is always transferred.
  1036.    30h/31h/34h/35h  are the same as 10h/11h/14h/15h.
  1037.        A CIRC recovered data error is defined as a block for which the
  1038.        CIRC based error correction algorithm was unsuccessful for a read
  1039.        attempt, but on a subsequent read operation no error was reported.
  1040.        The number of subsequent read operations is limited to the read
  1041.        retry count. Layered error correction was not used.
  1042.        An L-EC recovered data error is defined as a block for which the
  1043.        CIRC based error correction was unsuccessful, but the layered error
  1044.        correction was able to correct the block within the read retry count.
  1045.    RETR (Read Retry Count) specifies the number of times that the controller
  1046.        shall attempt its read recovery algorithm. }
  1047. function TCdRom.SCSImodeSenseRecoverEX(PCTL : TCdRomModePageType;
  1048.                       var PSAV : BOOLEAN; var RLEV, RETR : BYTE) : BOOLEAN;
  1049.    type SCSI_ModeRecovery = packed record
  1050.       ModeDataLength   : WORD;   // full record length except itself
  1051.       MediumType       : BYTE;
  1052.       DeviceSpecific   : BYTE;
  1053.       Reserved1        : WORD;
  1054.       DescriptorLength : WORD;   // equal to 8*Number of Block Descriptors
  1055.                                  // must be zero when DBD=TRUE
  1056.       PageCode         : BYTE;   // six LSBs of page code (01h) plus
  1057.                                  //  MSbit is PSAV flag (MODE SENSE only)
  1058.       ParamLength      : BYTE;   // must be = 6
  1059.       RecoveryLevel    : BYTE;   // returns as RLEV
  1060.       MaxRetryCount    : BYTE;
  1061.       Reserved2        : DWORD;
  1062.      end;
  1063.    var cd :  SCSI_ModeRecovery;
  1064.    begin
  1065.       result := SCSImodeSense(TRUE, PCTL, 1, @cd, sizeof(cd));
  1066.       PSAV := (cd.PageCode AND $80) <> 0;
  1067.       RLEV :=  cd.RecoveryLevel;
  1068.       RETR :=  cd.MaxRetryCount;
  1069.    end;
  1070. function TCdRom.SCSImodeSenseRecover(var RLEV, RETR : BYTE)     : BOOLEAN;
  1071.    var PSAVtemp : BOOLEAN;
  1072.    begin
  1073.       result := SCSImodeSenseRecoverEX(MPTcurrent, PSAVtemp, RLEV, RETR);
  1074.    end;
  1075. {======== MODE SELECT/MODE SENSE - Supported medium page. ========}
  1076.  { This page contains a list of the medium types implemented by the target.
  1077.    The code values (up to four) are reported in ascending order.
  1078.    PSAV=TRUE indicates that the target is capable of saving the page
  1079.       in a non-volatile location. Used with the MODE SENSE only.  }
  1080. function TCdRom.SCSImodeSenseMediumEX(PCTL : TCdRomModePageType;
  1081.    var PSAV: BOOLEAN; var Med1,Med2,Med3,Med4: TCdRomMediumType): BOOLEAN;
  1082.    type SCSI_ModeMedium = packed record
  1083.       ModeDataLength   : WORD;   // full record length except itself
  1084.       MediumType       : BYTE;
  1085.       DeviceSpecific   : BYTE;
  1086.       Reserved1        : WORD;
  1087.       DescriptorLength : WORD;
  1088.       PageCode         : BYTE;   // six LSBs of page code (0Bh) plus
  1089.                                  //  MSbit is PSAV flag (MODE SENSE only)
  1090.       ParamLength      : BYTE;   // must be = 6
  1091.       Reserved2        : WORD;
  1092.       Medium : array[0..3] of BYTE;
  1093.      end;
  1094.    var
  1095.       cd : SCSI_ModeMedium;
  1096.       i  : integer;
  1097.    begin
  1098.       result := SCSImodeSense(TRUE, PCTL, $0B, @cd, sizeof(cd));
  1099.       PSAV   := (cd.PageCode AND $80) <> 0;
  1100.       with cd do begin
  1101.          for i := 0 to 3 do begin
  1102.             if Medium[i] > 7 then Medium[i] := 4;  // CDTreserved
  1103.             if (Medium[i]=0) AND (i > 0)
  1104.                               then Medium[i] := 8;  // CDTnone
  1105.          end;
  1106.          Med1 := TCdRomMediumType(Medium[0]);
  1107.          Med2 := TCdRomMediumType(Medium[1]);
  1108.          Med3 := TCdRomMediumType(Medium[2]);
  1109.          Med4 := TCdRomMediumType(Medium[3]);
  1110.    end; end;
  1111. {======== MODE SELECT/MODE SENSE - CD-ROM device parameters page. ========}
  1112.  { This page specifies parameters that affect all CD-ROM data types.
  1113.    PSAV=TRUE indicates that the target is capable of saving the page in a
  1114.       non-volatile vendor-specific location. Used with the MODE SENSE only.
  1115.    ITimer (Inactivity Timer) specifies the time that the drive shall remain
  1116.       in the hold track state after completion of a seek or read operation.
  1117.       Value:   1     2     3   4  5  6  7   8   9  Ah  Bh  Ch  Dh  Eh  Fh
  1118.       Time:  0.125s 0.25s 0.5s 1s 2s 4s 8s 16s 32s 1m  2m  4m  8m  16m 32m
  1119.       where s=seconds, m=minutes. Value of zero means vendor-specific time.
  1120.       WARNING: High values in this parameter may have an adverse effect on
  1121.       the drive MTBF.
  1122.    SperMunits and FperSunits fields gives the ratio of these MSF address
  1123.       values. For media conforming to the CD-ROM and CD-DA standard, this
  1124.       value is 60 and 75 respectively.   }
  1125. function TCdRom.SCSImodeSenseDeviceEX(PCTL : TCdRomModePageType;
  1126.                       var PSAV : BOOLEAN; var ITimer : BYTE;
  1127.                       var SperMunits, FperSunits : WORD) : BOOLEAN;
  1128.    type SCSI_ModeDevice = packed record
  1129.       ModeDataLength   : WORD;   // full record length except itself
  1130.       MediumType       : BYTE;
  1131.       DeviceSpecific   : BYTE;
  1132.       Reserved1        : WORD;
  1133.       DescriptorLength : WORD;
  1134.       PageCode         : BYTE;   // six LSBs of page code (0Dh) plus
  1135.                                  //  MSbit is PSAV flag (MODE SENSE only)
  1136.       ParamLength      : BYTE;   // must be = 6
  1137.       Reserved2        : BYTE;
  1138.       InactivityTimer  : BYTE;   // returns as ITimer
  1139.       SunitsPerMunits  : WORD;   // returns as SperMunits. Usually=60d
  1140.       FunitsPerSunits  : WORD;   // returns as FperSunits. Usually=75d
  1141.      end;
  1142.    var cd :  SCSI_ModeDevice;
  1143.    begin
  1144.       result := SCSImodeSense(TRUE, PCTL, $0D, @cd, sizeof(cd));
  1145.       PSAV   := (cd.PageCode AND $80) <> 0;
  1146.       ITimer := $0F AND cd.InactivityTimer;
  1147.       SperMunits := BigEndianW(cd.SunitsPerMunits);
  1148.       FperSunits := BigEndianW(cd.FunitsPerSunits);
  1149.    end;
  1150. function TCdRom.SCSImodeSenseDevice(var ITimer : BYTE)     : BOOLEAN;
  1151.    var
  1152.      PSAVtemp : BOOLEAN;
  1153.      SPM, FPS : WORD;
  1154.    begin
  1155.       result := SCSImodeSenseDeviceEX(MPTcurrent,
  1156.                              PSAVtemp, ITimer, SPM, FPS);
  1157.    end;
  1158. {======== MODE SELECT/MODE SENSE - CD-ROM audio control  page. ========}
  1159.  { This page sets the playback modes and output controls for subsequent
  1160.    PLAY AUDIO commands and any current audio playback operation.
  1161.    PSAV (Parameters SAVable) = TRUE indicates that the target is capable
  1162.        of saving the page in a non-volatile vendor-specific location.
  1163.        This bit is only used with the MODE SENSE command.
  1164.    IMM=TRUE indicates that status of any audio playback operation shall be
  1165.        returned as soon as the CDB has been validated,  else status shall
  1166.        be returned after the playback has been completed or terminated.
  1167.        It is recommended to RESERVE a LUN prior to starting audio play
  1168.        operations with IMM=TRUE in any multitasking environment.
  1169.    SOTC (Stop On Track Crossing)=TRUE indicates the target shall terminate
  1170.        the audio playback operation when the beginning of a following track
  1171.        is encountered. SOTC=FALSE indicates the target shall do it when the
  1172.        transfer length is satisfied, even if multiple tracks will be played
  1173.        (including the audio pause/silence between the tracks).
  1174.    APRV (Audio Playback Rate Valid) = TRUE  indicates that the format of
  1175.        logical blocks per second field and the logical blocks per second of
  1176.        audio playback field are valid.
  1177.    LBAformat field is provided as a means to return fractional (i.e. non-
  1178.        integral) values in the logical block addresses per second of audio
  1179.        playback.  This shall occur when logical block sizes that are not
  1180.        even multiples or divisions of the physical block size are used.
  1181.        It gives the multiplier to be used with the logical blocks per
  1182.        second of audio playback: LBAformat=0 means 1 LBA/sec, LBAformat=8
  1183.        - 1/256 LBA/sec. All other values of this field are reserved.
  1184.    LBSaudio (Logical Blocks per Second of audio playback) field gives the
  1185.        relationship between time and the duration of play per logical block
  1186.        address. The value in this field is to be multiplied by the value in
  1187.        format of LBAs per second field.
  1188.        NOTE: Both LBAformat and LBSaudio may not be supported by CD-ROM
  1189.        devices as a modifiable mode select parameter.
  1190.    Volume[] is the relative volume level for this audio output port.
  1191.        Zero indicates the output is muted, and a value of FFh indicates
  1192.        maximum volume level. If volume controls are implemented, the
  1193.        default volume level should be no more than 25 % of the maximum.
  1194.    Channel[] specifies the audio channels from the disc to which this
  1195.        output port should be connected. More than one output port may be
  1196.        connected to an audio channel and vice versa. The four-bit value
  1197.        is a bitset of connected channels: 0=no connect (port is muted),
  1198.        1=channel0 only, 2=channel1 only, 3=channel0 plus channel1, etc.  }
  1199. function TCdRom.SCSImodeSenseAudioEX(PCTL : TCdRomModePageType;
  1200.                                   var sh : TCdRomModePageAudio) : BOOLEAN;
  1201.    type SCSI_ModeAudio = packed record
  1202.       ModeDataLength   : WORD;   // full record length except itself
  1203.       MediumType       : BYTE;
  1204.       DeviceSpecific   : BYTE;
  1205.       Reserved1        : WORD;
  1206.       DescriptorLength : WORD;
  1207.       PageCode         : BYTE;   // six LSBs of page code (0Eh) plus
  1208.                                  //  MSbit is PSAV flag (MODE SENSE only)
  1209.       ParamLength      : BYTE;   // must be = 14d
  1210.       IMMandSOTC       : BYTE;
  1211.       Reserved2        : WORD;
  1212.       APRVandLBAF      : BYTE;
  1213.       LBSaudioW        : WORD;
  1214.       ChVolume         : array[0..3] of WORD;
  1215.      end;
  1216.    var
  1217.       cd : SCSI_ModeAudio;
  1218.       i  : integer;
  1219.    begin
  1220.       FillChar(sh, sizeof(sh), 0);
  1221.       result := SCSImodeSense(TRUE, PCTL, $0E, @cd, sizeof(cd));
  1222.       if result then with sh do begin
  1223.          PSAV := (cd.PageCode AND $80) <> 0;
  1224.          IMM  := (cd.IMMandSOTC AND 4) <> 0;
  1225.          SOTC := (cd.IMMandSOTC AND 2) <> 0;
  1226.          APRV := (cd.APRVandLBAF AND $80) <> 0;
  1227.          LBAformat :=  cd.APRVandLBAF AND $0F;
  1228.          LBSaudio  :=  BigEndianW(cd.LBSaudioW);
  1229.          for i := 0 to 3 do begin
  1230.             Volume[i]  := $0FF AND BigEndianW(cd.ChVolume[i]);
  1231.             Channel[i] := $0F  AND cd.ChVolume[i];
  1232.    end; end; end;
  1233. function TCdRom.SCSImodeSenseAudio(var sh : TCdRomModePageAudio) : BOOLEAN;
  1234.    begin  result := SCSImodeSenseAudioEX(MPTcurrent, sh);  end;
  1235. {======================= PLAY AUDIO SCSI-II COMMANDS ====================}
  1236. { If any of the PLAY AUDIO commands are implemented, the PLAY AUDIO
  1237.   command shall be implemented by the target. A target responding
  1238.   to a PLAY AUDIO command that has a transfer length of zero with
  1239.   Err_SenseIllegalRequest status does not support audio play operations.
  1240. + 45h PLAY AUDIO(10)          ; begins an audio playback operation
  1241. + A5h PLAY AUDIO(12)          ; same for long-time (>14 min) operations
  1242. + 47h PLAY AUDIO MSF          ; same with MSF parameter format
  1243. + 48h PLAY AUDIO TRACK/INDEX  ; same with track/index parameter format
  1244. + 49h PLAY TRACK RELATIVE(10) ; same with track+RelLBA parameter format
  1245. + A9h PLAY TRACK RELATIVE(12) ; same for long-time (>14 min) operations
  1246. + 4Bh PAUSE/RESUME            ; Pauses/resumes paused audio play operation
  1247. }
  1248.  { PLAY AUDIO(10) and PLAY AUDIO(12) requests the target to begin an
  1249.    audio playback operation. The command function (IMM and SOTC flags)
  1250.    and the mapping/volume of audio signals are specified by the settings
  1251.    of the mode parameters (see SCSImodeSenseAudio function).
  1252.    GLBA is the logical block at which the playback operation shall begin.
  1253.    Sectors is a number of contiguous logical blocks of data that shall be
  1254.        played. A transfer length of zero indicates that no audio operation
  1255.        shall occur (this shall not be considered an error).
  1256.    If the logical block length is not equal to the sector size, the target
  1257.    may adjust the starting LBA and the transfer length. In such case,
  1258.    target starts the audio play operation with the beginning of a sector
  1259.    whenever the starting logical address falls within, and may continue
  1260.    the playback through the end of a sector whenever the ending logical
  1261.    address falls within.
  1262.    If the starting address is not found, if the address is not within an
  1263.    audio track, or if a not ready condition exists, the command shall be
  1264.    terminated with Err_CheckCondition status.
  1265.    If the logical block address requested is not within an audio track,
  1266.    or the CD-ROM information type (data vs. audio) changes, the command
  1267.    shall be terminated with Err_SenseBlankCheck status.  }
  1268. function TCdRom.SCSIplayAudio10(GLBA : DWORD; Sectors : WORD) : BOOLEAN;
  1269.    begin
  1270.       result := ASPIsend10($45, 0, GLBA, 0, Sectors,
  1271.                                 NIL, 0, SRB_NODIR, AudioTimeout);
  1272.    end;
  1273. function TCdRom.SCSIplayAudio12(GLBA, Sectors : DWORD) : BOOLEAN;
  1274.    begin
  1275.       result := ASPIsend12($A5, 0, GLBA, Sectors, 0,
  1276.                                 NIL, 0, SRB_NODIR, AudioTimeout);
  1277.    end;
  1278.  { PLAY AUDIO MSF requests the target to begin an audio playback operation.
  1279.    It is full equivalent to the PLAY AUDIO command except the CD addresses
  1280.    style - start and stop points in Minute-Second-Frame form.
  1281.    A starting MSF address equal to an ending MSF address causes no audio
  1282.    play operation to occur.  This shall not be considered an error.  If
  1283.    the starting MSF address is less than the ending MSF address, the
  1284.    command shall be terminated with Err_SenseIlleglRequest status.  }
  1285. function TCdRom.SCSIplayAudioMSF(StartM, StartS, StartF,
  1286.                                  StopM, StopS, StopF : BYTE ) : BOOLEAN;
  1287.    begin
  1288.       result := ASPIsend10($47, 0, GatherDWORD(0, StartM, StartS, StartF),
  1289.         StopM, GatherWORD(StopS, StopF), NIL, 0, SRB_NODIR, AudioTimeout);
  1290.    end;
  1291.  { PLAY AUDIO TRACK INDEX requests the target to begin an audio playback
  1292.    operation. It is full equivalent to the PLAY AUDIO command except the
  1293.    CD addresses style - start and stop points in Track-Index form.
  1294.    The audio play operation shall begin at the first block with specified
  1295.    starting track/index and terminate at the last block with specified
  1296.    ending track/index. All contiguous audio sectors between the starting
  1297.    and the ending address shall be played.
  1298.    Valid values for the track and index fields are 1 to 99. The first
  1299.    audio sector of the track following the (optional) pause has Index=1.
  1300.    Index=99 specifies that playback continues through the last sector of
  1301.    the track.
  1302.    All these conditions shall not be considered errors:
  1303.    a) If the ending track is greater than the last information track on the
  1304.      media, the playback shall continue until the last track is complete.
  1305.    b) If the ending index is greater than the largest index value on the
  1306.      ending track, the playback shall continue until this track is complete.
  1307.    c) If the starting index is greater than the largest index value on the
  1308.      starting track, and the SOTC flag of the audio control mode page (see
  1309.      SCSImodeSenseAudio function) is FALSE, the playback operation shall
  1310.      start at the beginning of the next track.
  1311.    The command shall be terminated with Err_SenseIlleglRequest status if
  1312.    the starting index is greater than the largest index value on the
  1313.    starting track and SOTC=TRUE, so that playback shall not begin.
  1314.    NOTE: The SOTC=TRUE allows the user to determine the largest index on
  1315.    the track. The SOTC=FALSE allows the user to set up play operations
  1316.    without complete knowledge of the media layout.  }
  1317. function TCdRom.SCSIplayAudioTI(StartTrack, StartIndex,
  1318.                                   StopTrack, StopIndex : BYTE ) : BOOLEAN;
  1319.    begin
  1320.       result := ASPIsend10($48, 0, GatherDWORD(0,0,StartTrack,StartIndex),
  1321.         0, GatherWORD(StopTrack,StopIndex), NIL,0,SRB_NODIR,AudioTimeout);
  1322.    end;
  1323.  { PLAY AUDIO TRACK RELATIVE(10) and PLAY AUDIO TRACK RELATIVE(12)
  1324.    requests the target to begin an audio playback operation. It is full
  1325.    equivalent to the PLAY AUDIO(10)/(12) commands except the CD start
  1326.    address which is specified as a track relative logical block address
  1327.    (TRLBA, two's complement) within the specified starting track.
  1328.    Negative TRLBA values indicate a starting location within the audio
  1329.    pause area at the beginning of the requested track.  }
  1330. function TCdRom.SCSIplayAudioR10(TRLBA : DWORD;
  1331.                          StartTrack : BYTE; Sectors : WORD) : BOOLEAN;
  1332.    begin
  1333.       result := ASPIsend10($49, 0, TRLBA, StartTrack, Sectors,
  1334.                                 NIL, 0, SRB_NODIR, AudioTimeout);
  1335.    end;
  1336. function TCdRom.SCSIplayAudioR12(TRLBA : DWORD;
  1337.                          StartTrack : BYTE;  Sectors : DWORD) : BOOLEAN;
  1338.    begin
  1339.       result := ASPIsend12($A9, 0, TRLBA, Sectors, StartTrack,
  1340.                                 NIL, 0, SRB_NODIR, AudioTimeout);
  1341.    end;
  1342.  { PAUSE RESUME command stops or starts an audio play operation.
  1343.    This command is used with PLAY AUDIO commands.
  1344.    PAUSE causes the drive to enter the hold track state with the audio
  1345.    output muted after the current block is played. If no audio play
  1346.    operation is in progress, or the requested audio play operation has
  1347.    been completed, the command is terminated with Err_CheckCondition
  1348.    status. It shall not be considered an error to request a pause when
  1349.    a pause is already in effect. }
  1350. function TCdRom.SCSIpauseAudio : BOOLEAN;
  1351.    begin result:=ASPIsend10($4B,0,0,0,0,NIL,0,SRB_NODIR,ShortTimeout); end;
  1352.  { RESUME causes the drive to release the pause and begin play at the
  1353.    block following the last block played. If an audio play operation
  1354.    cannot be resumed, the command is terminated with Err_CheckCondition
  1355.    status. It shall not be considered an error to request a resume when
  1356.    a play operation is in progress. }
  1357. function TCdRom.SCSIresumeAudio : BOOLEAN;
  1358.    begin result:=ASPIsend10($4B,0,0,0,1,NIL,0,SRB_NODIR,ShortTimeout); end;
  1359. end.