home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 113 / EnigmaAmiga113CD.iso / software / sviluppo / quake_src / twfsound_cd.c < prev    next >
C/C++ Source or Header  |  2000-06-17  |  33KB  |  866 lines

  1. //     ___       ___
  2. //   _/  /_______\  \_     ___ ___ __ _                       _ __ ___ ___
  3. //__//  / _______ \  \\___/                                               \___
  4. //_/ | '  \__ __/  ` | \_/        © Copyright 1998, Christopher Page       \__
  5. // \ | |    | |__  | | / \               All Rights Reserved               /
  6. //  >| .    |  _/  . |<   >--- --- -- -                       - -- --- ---<
  7. // / \  \   | |   /  / \ / This file may not be distributed, reproduced or \
  8. // \  \  \_/   \_/  /  / \  altered, in full or in part, without written   /
  9. //  \  \           /  /   \    permission from Christopher Page. Legal    /
  10. // //\  \_________/  /\\ //\     action will be taken in cases where     /
  11. //¯ ¯¯\   _______   /¯¯ ¯ ¯¯\        this notice is not obeyed.         /¯¯¯¯¯
  12. //¯¯¯¯¯\_/       \_/¯¯¯¯¯¯¯¯¯\   ___________________________________   /¯¯¯¯¯¯
  13. //                            \_/                                   \_/
  14. //
  15. // Description:
  16. //
  17. //  CD playback functions
  18. //
  19. // Functions:
  20. //
  21. //  struct TWFCDData * TWFCD_Setup(REG(a0) STRPTR DeviceName, REG(d0) UBYTE UnitNum)
  22. //  ULONG TWFCD_Shutdown(REG(a0) struct TWFCDData *tcs_OldData)
  23. //  ULONG TWFCD_ReadTOC(REG(a0) struct TWFCDData *tcrt_CDData)
  24. //  ULONG TWFCD_PlayTracks(REG(a0) struct TWFCDData *tcpt_CDData, REG(a1) struct TagItem *pt_Tags)
  25. //  ULONG TWFCD_StopPlay(REG(a0) struct TWFCDData *tcsp_CDData)
  26. //  ULONG TWFCD_PausePlay(REG(a0) struct TWFCDData *tcpp_CDData)
  27. //  ULONG TWFCD_MotorControl(REG(a0) struct TWFCDData *tcet_CDData, REG(d0) UBYTE newStatus)
  28. //  ULONG TWFCD_ReadSubChannel(REG(a0) struct TWFCDData *tcrs_CDData)
  29. //  static ULONG TWFCD_DoSCSICmd(struct TWFCDData *tcdc_CDData, UBYTE *buffer, int bufferSize, UBYTE *cmd, int cmdSize, UBYTE flags)
  30. //
  31. // Detail:
  32. //
  33. //  This code provides a simple method to query the contents of a CD and start, stop
  34. //  and pause playback of audio tracks on that CD. This is still work in progress and
  35. //  likely to have problems on some non-standard/ older CD-ROM models.
  36. //
  37. //
  38. // Version:
  39. //
  40. //  $VER: twfsound_cd.c 3.18 (17/10/1999)
  41. //
  42. // Fold Markers:
  43. //
  44. //  Start: /*GFS*/
  45. //    End: /*GFE*/
  46. //
  47. // Coded by and received from "The World Foundry",
  48. // (chris@worldfoundry.demon.co.uk)
  49. // This code is *not* under GPL, but it can be used for Freeware and
  50. // commercial projects. It is allowed to include the source-code, but
  51. // it is not required (Unless the game in question - like Quake -
  52. // requires to release the source). The release of this code inside
  53. // a GPL project like Quake does not put other projects which use
  54. // this code GPL, as this CD Audio Replay Code is not GPL, despite the
  55. // fact that the source code is allowed to be included.
  56. // Despite being freeware, source code modifications in this source
  57. // require the agreement of TWF. This especially includes also
  58. // recompiles to different Kernels.
  59.  
  60. /*
  61.             Portability
  62.             ===========
  63.  
  64.             This file includes support for WarpUP and 68k. In case of
  65.             PowerUP the AllocVecPPC/FreeVecPPC calls would be needed
  66.             to be replaced by alternative functions. I am not sure
  67.             if a modification to compile this with PowerUP would be
  68.             agreed by the original authors. It is possible that this
  69.             might be a problem. But if someone wants to compile Quake
  70.             for PowerUP - i personally doubt that this is worth the
  71.             effort for anyone - he has to check this out with TWF,
  72.             or look for a different CD Audio Replay Solution. I did
  73.             not ask TWF about this, as I personally don't care if a
  74.             PowerUP version will be done or not. Some includes would
  75.             need to be changed for different compilers, like usual.
  76.  
  77.             Steffen Haeuser (MagicSN@Birdland.es.bawue.de)
  78. */
  79.  
  80. /*
  81.  * Additional changes, as required for our Quake port, and
  82.  * PowerUp support by Frank Wille <frank@phoenix.owl.de> (phx)
  83.  */
  84.  
  85. #pragma amiga-align
  86. #include <exec/exec.h>
  87. #include <devices/trackdisk.h>
  88. #include "twfsound_cd.h"
  89. #ifdef __PPC__
  90. #ifdef WOS
  91. #include <clib/powerpc_protos.h>
  92. #else /* PowerUp */
  93. #include <powerup/gcclib/powerup_protos.h>
  94. #endif
  95. #endif
  96. #pragma default-align
  97. #include <stdio.h>
  98.  
  99. #if 0 /* initialized in sys_amiga.c (phx) */
  100. struct Library *UtilityBase;
  101. #endif
  102.  
  103. // Non-standard, and a bit of a cheat, but it makes the code easier to read
  104. struct TOCData
  105. {
  106.     UBYTE Empty1;  // Padding
  107.     UBYTE Flags ;
  108.     UBYTE Track ;
  109.     UBYTE Empty2;
  110.     ULONG Addr  ;
  111. };
  112.  
  113. struct SubQData
  114. {
  115.     UBYTE Reserved   ; // Header: bytes 0 to 3
  116.     UBYTE AudioStatus;
  117.     UWORD DataLength ;
  118.     UBYTE Formatcode ; // Data: byte 4
  119.     UBYTE AdrControl ; // Byte 5
  120.     UBYTE TrackNum   ; // Byte 6
  121.     UBYTE IndexNum   ; // Byte 7
  122.     ULONG AbsoluteAdr; // Bytes  8,  9, 10, 11
  123.     ULONG RelativeAdr; // Bytes 12, 13, 14, 15
  124. };
  125.  
  126. /* (phx) @@@?  use -DNDEBUG instead! */
  127. /* #define kprintf */
  128.  
  129. static ULONG TWFCD_DoSCSICmd(struct TWFCDData *, UBYTE *, int , UBYTE *, int, UBYTE);
  130.  
  131. /* ULONG TWFCD_Setup(STRPTR, UBYTE)                                          */
  132. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=                                          */
  133. /* This routine creates the message port and IO structure needed by the      */
  134. /* device, then it attepmts to open the device.. nothig really groundbreaking*/
  135. /* in here you know...                                                       */
  136.  
  137. /*GFS*/  struct TWFCDData * TWFCD_Setup(STRPTR DeviceName, UBYTE UnitNum)
  138. {
  139.     struct TWFCDData *NewData    = NULL;
  140.            BYTE       DeviceCode = 0;
  141.  
  142.  
  143. #if 0 /* this should be done in sys_amiga.c (phx) */
  144.     UtilityBase=(struct Library *)OpenLibrary("utility.library",0);
  145. #endif
  146.  
  147.     // create the table...
  148. #ifdef __PPC__
  149. #ifdef WOS
  150.     if(!(NewData = AllocVecPPC(sizeof(struct TWFCDData), MEMF_ANY|MEMF_CLEAR,0))) return(NULL);
  151.     (NewData->TWFCD_Cmd).cdc_SCSIData=AllocVecPPC(CDBUFFER_SIZE,MEMF_CHIP|MEMF_CLEAR,0);
  152. #else /* PowerUp */
  153.     if(!(NewData = PPCAllocVec(sizeof(struct TWFCDData), MEMF_ANY|MEMF_CLEAR))) return(NULL);
  154.     (NewData->TWFCD_Cmd).cdc_SCSIData=PPCAllocVec(CDBUFFER_SIZE,MEMF_CHIP|MEMF_CLEAR);
  155. #endif
  156. #else
  157.     if(!(NewData = AllocVec(sizeof(struct TWFCDData), MEMF_ANY|MEMF_CLEAR))) return(NULL);
  158.     (NewData->TWFCD_Cmd).cdc_SCSIData=AllocVec(CDBUFFER_SIZE,MEMF_CHIP|MEMF_CLEAR);
  159. #endif
  160.  
  161.     if (!((NewData->TWFCD_Cmd).cdc_SCSIData))
  162.     {
  163. #ifdef __PPC__
  164. #ifdef WOS
  165.      FreeVecPPC(NewData);
  166. #else /* PowerUp */
  167.      PPCFreeVec(NewData);
  168. #endif
  169. #else
  170.      FreeVec(NewData);
  171. #endif
  172.      return 0;
  173.     }
  174. #ifdef __PPC__
  175. #ifdef WOS
  176.     (NewData->TWFCD_Cmd).cdc_SCSISense=AllocVecPPC(CDSENSE_SIZE,MEMF_CHIP|MEMF_CLEAR,0);
  177.     if (!((NewData->TWFCD_Cmd).cdc_SCSISense))
  178.     {
  179.      FreeVecPPC((NewData->TWFCD_Cmd).cdc_SCSIData);
  180.      FreeVecPPC(NewData);
  181.      return 0;
  182.     }
  183.     (NewData->TWFCD_Cmd).cdc_TOCBuffer=AllocVecPPC(CDTOC_SIZE,MEMF_CHIP|MEMF_CLEAR,0);
  184.     if (!((NewData->TWFCD_Cmd).cdc_TOCBuffer))
  185.     {
  186.      FreeVecPPC((NewData->TWFCD_Cmd).cdc_SCSIData);
  187.      FreeVecPPC((NewData->TWFCD_Cmd).cdc_SCSISense);
  188.      FreeVecPPC(NewData);
  189.     }
  190. #else /* PowerUp */
  191.     (NewData->TWFCD_Cmd).cdc_SCSISense=PPCAllocVec(CDSENSE_SIZE,MEMF_CHIP|MEMF_CLEAR);
  192.     if (!((NewData->TWFCD_Cmd).cdc_SCSISense))
  193.     {
  194.      PPCFreeVec((NewData->TWFCD_Cmd).cdc_SCSIData);
  195.      PPCFreeVec(NewData);
  196.      return 0;
  197.     }
  198.     (NewData->TWFCD_Cmd).cdc_TOCBuffer=PPCAllocVec(CDTOC_SIZE,MEMF_CHIP|MEMF_CLEAR);
  199.     if (!((NewData->TWFCD_Cmd).cdc_TOCBuffer))
  200.     {
  201.      PPCFreeVec((NewData->TWFCD_Cmd).cdc_SCSIData);
  202.      PPCFreeVec((NewData->TWFCD_Cmd).cdc_SCSISense);
  203.      PPCFreeVec(NewData);
  204.     }
  205. #endif
  206. #else
  207.     (NewData->TWFCD_Cmd).cdc_SCSISense=AllocVec(CDSENSE_SIZE,MEMF_CHIP|MEMF_CLEAR);
  208.     if (!((NewData->TWFCD_Cmd).cdc_SCSISense))
  209.     {
  210.      FreeVec((NewData->TWFCD_Cmd).cdc_SCSIData);
  211.      FreeVec(NewData);
  212.      return 0;
  213.     }
  214.     (NewData->TWFCD_Cmd).cdc_TOCBuffer=AllocVec(CDTOC_SIZE,MEMF_CHIP|MEMF_CLEAR);
  215.     if (!((NewData->TWFCD_Cmd).cdc_TOCBuffer))
  216.     {
  217.      FreeVec((NewData->TWFCD_Cmd).cdc_SCSIData);
  218.      FreeVec((NewData->TWFCD_Cmd).cdc_SCSISense);
  219.      FreeVec(NewData);
  220.     }
  221. #endif
  222.  
  223.     // Create the reply MsgPort for the device
  224.     if(!(NewData -> TWFCD_Cmd.cdc_MsgPort = (struct MsgPort *)CreateMsgPort())) return(NULL);
  225.  
  226.     // Create our IORequest, to send Play IO messages to the CD device
  227.     NewData -> TWFCD_Cmd.cdc_IOStdReq = (struct IOStdReq *)CreateIORequest(NewData -> TWFCD_Cmd.cdc_MsgPort, sizeof(struct IOStdReq));
  228.     if(NewData -> TWFCD_Cmd.cdc_IOStdReq == NULL) return (NULL);
  229.  
  230.     // Open the CD device for the Play commands
  231.     DeviceCode = OpenDevice(DeviceName, UnitNum, (struct IORequest *)NewData -> TWFCD_Cmd.cdc_IOStdReq, 0);
  232.     if(DeviceCode != 0) return(NULL);
  233.  
  234.     ((NewData->TWFCD_Cmd).cdc_IOStdReq)->io_Command = CMD_START;
  235.     DoIO((struct IORequest *)((NewData->TWFCD_Cmd).cdc_IOStdReq));
  236.  
  237.     // Device is now open, CD play available.
  238.     return(NewData);
  239. } /*GFE*/
  240.  
  241.  
  242. /* ULONG TWFCD_Shutdown(TWFCDData *)                                         */
  243. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-                                         */
  244. /* Call this to close the CD playback device, IO struct and message port     */
  245. /* allocated by the TWFCD_Setup routine.                                     */
  246.  
  247. /*GFS*/  ULONG TWFCD_Shutdown(struct TWFCDData *tcs_OldData)
  248. {
  249.     if(!tcs_OldData) return(TWFCD_FAIL);
  250.  
  251.     // Halt playback (so few people bother doing this.....)
  252.     if(tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq) TWFCD_StopPlay(tcs_OldData);
  253.  
  254.     // Kill memory allocation
  255.     if(tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq) CloseDevice    ((struct IORequest*)tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq);
  256.  
  257.     if(tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq) {
  258.         DeleteIORequest((struct IORequest*)tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq);
  259.         tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq = NULL;
  260.     }
  261.     if(tcs_OldData -> TWFCD_Cmd.cdc_MsgPort ) {
  262.         DeleteMsgPort  (tcs_OldData -> TWFCD_Cmd.cdc_MsgPort);
  263.         tcs_OldData -> TWFCD_Cmd.cdc_MsgPort = NULL;
  264.     }
  265.  
  266. #ifdef __PPC__
  267. #ifdef WOS
  268.     if (tcs_OldData->TWFCD_Cmd.cdc_SCSIData) FreeVecPPC(tcs_OldData->TWFCD_Cmd.cdc_SCSIData);
  269.     tcs_OldData->TWFCD_Cmd.cdc_SCSIData=0;
  270.     if (tcs_OldData->TWFCD_Cmd.cdc_SCSISense) FreeVecPPC(tcs_OldData->TWFCD_Cmd.cdc_SCSISense);
  271.     tcs_OldData->TWFCD_Cmd.cdc_SCSISense=0;
  272.     if (tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer) FreeVecPPC(tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer);
  273.     tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer=0;
  274.     if (tcs_OldData) FreeVecPPC(tcs_OldData);
  275. #else /* PowerUp */
  276.     if (tcs_OldData->TWFCD_Cmd.cdc_SCSIData) PPCFreeVec(tcs_OldData->TWFCD_Cmd.cdc_SCSIData);
  277.     tcs_OldData->TWFCD_Cmd.cdc_SCSIData=0;
  278.     if (tcs_OldData->TWFCD_Cmd.cdc_SCSISense) PPCFreeVec(tcs_OldData->TWFCD_Cmd.cdc_SCSISense);
  279.     tcs_OldData->TWFCD_Cmd.cdc_SCSISense=0;
  280.     if (tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer) PPCFreeVec(tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer);
  281.     tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer=0;
  282.     if (tcs_OldData) PPCFreeVec(tcs_OldData);
  283. #endif
  284. #else
  285.     if (tcs_OldData->TWFCD_Cmd.cdc_SCSIData) FreeVec(tcs_OldData->TWFCD_Cmd.cdc_SCSIData);
  286.     tcs_OldData->TWFCD_Cmd.cdc_SCSIData=0;
  287.     if (tcs_OldData->TWFCD_Cmd.cdc_SCSISense) FreeVec(tcs_OldData->TWFCD_Cmd.cdc_SCSISense);
  288.     tcs_OldData->TWFCD_Cmd.cdc_SCSISense=0;
  289.     if (tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer) FreeVec(tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer);
  290.     tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer=0;
  291.     if (tcs_OldData) FreeVec(tcs_OldData);
  292. #endif
  293.     tcs_OldData=0;
  294.     return(TWFCD_OK);
  295. } /*GFE*/
  296.  
  297.  
  298. /* ULONG TWFCD_ReadTOC(void)                                                 */
  299. /* -=-=-=-=-=-=-=-=-=-=-=-=-                                                 */
  300. /* This routine loads the table of contents off a CD and calculates a unique */
  301. /* disc identifier. out of range track identifiers are ignored (except by the*/
  302. /* ID generation) as most disks seem to have a mysterious track 170..?!!     */
  303.  
  304. /*GFS*/  ULONG TWFCD_ReadTOC(REG(a0) struct TWFCDData *tcrt_CDData)
  305. {
  306.     ULONG ReturnCode = 0;
  307.     BYTE  Position   = 0;
  308.     ULONG Status     = 0;
  309.  
  310.            ULONG    TOCLength  = 0;
  311.            ULONG    TOC170Adr  = 0;     // This records the address of track 170
  312.            BOOL     Enhanced   = FALSE; // TRUE if a data track above track 1.
  313.     struct TOCData *TOCPtr     = NULL;
  314.     struct TOCData *NextTOCPtr = NULL;
  315.  
  316.     ULONG LastStart = 0;
  317.     ULONG ThisStart = 0;
  318.  
  319.     UBYTE ReadTOCCmd[10] = {
  320.         SCSI_CMD_READTOC,
  321.         0,
  322.         PAD,
  323.         PAD,
  324.         PAD,
  325.         PAD,
  326.         0,                      // Starting track
  327.         0x03, 0x24,             // Max. TOC data on current CDs is 0x324 bytes
  328.                                 // or 100 track descriptors
  329.         PAD
  330.     };
  331.  
  332.     // Check for CD in drive
  333.     tcrt_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Command = TD_CHANGESTATE;
  334.     DoIO((struct IORequest *)tcrt_CDData -> TWFCD_Cmd.cdc_IOStdReq);
  335.     if(tcrt_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Actual != 0)
  336.     {
  337.         tcrt_CDData -> TWFCD_Table.cda_FirstTrack  = 0;
  338.         tcrt_CDData -> TWFCD_Table.cda_LastTrack   = 0;
  339.         tcrt_CDData -> TWFCD_Table.cda_FirstAudio  = 0;
  340.         tcrt_CDData -> TWFCD_Table.cda_CDLength    = 0;
  341.         tcrt_CDData -> TWFCD_Table.cda_AudioLen    = 0;
  342.         tcrt_CDData -> TWFCD_Table.cda_DiskId[0]   = '\0';
  343.         tcrt_CDData -> TWFCD_Track.cds_AudioStatus = TWFCD_AUDIOSTATUS_INVALID;
  344.         tcrt_CDData -> TWFCD_Track.cds_PlayTrack   = 0;
  345.         return(TWFCD_FAIL);
  346.     }
  347.  
  348.     // Setup the direct SCSICmd
  349.     Status = TWFCD_DoSCSICmd(tcrt_CDData,
  350.                              tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer, CDTOC_SIZE,
  351.                              ReadTOCCmd, sizeof(ReadTOCCmd),
  352.                              (SCSIF_READ | SCSIF_AUTOSENSE));
  353.  
  354.     if(Status != TWFCD_OK) {
  355.         DEBUGLOG(kprintf("TWFCD_ReadTOC(): Status is not OK\n");)
  356.         return(TWFCD_FAIL);
  357.     }
  358.  
  359.     // Read the TOC in!
  360.     ReturnCode = DoIO((struct IORequest *)tcrt_CDData -> TWFCD_Cmd.cdc_IOStdReq);
  361.  
  362.     // Check the status of the direct cmd
  363.     if(tcrt_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) {
  364.  
  365.         DEBUGLOG(kprintf("TWFCD_ReadTOC(): Command failed\n");)
  366.  
  367.         if(tcrt_CDData -> TWFCD_Table.cda_DiskId[0] != '\0') {
  368.             tcrt_CDData -> TWFCD_Table.cda_FirstTrack  = 0;
  369.             tcrt_CDData -> TWFCD_Table.cda_LastTrack   = 0;
  370.             tcrt_CDData -> TWFCD_Table.cda_FirstAudio  = 0;
  371.             tcrt_CDData -> TWFCD_Table.cda_CDLength    = 0;
  372.             tcrt_CDData -> TWFCD_Table.cda_AudioLen    = 0;
  373.             tcrt_CDData -> TWFCD_Table.cda_DiskId[0]   = '\0';
  374.             tcrt_CDData -> TWFCD_Track.cds_AudioStatus = TWFCD_AUDIOSTATUS_INVALID;
  375.             tcrt_CDData -> TWFCD_Track.cds_PlayTrack   = 0;
  376.         }
  377.  
  378.         return(TWFCD_FAIL);
  379.     }
  380.  
  381.     // Read the length of the TOC - encoded in first word
  382.     TOCLength = (tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[0] << 8) | tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[1];
  383.  
  384.     // Set track info...
  385.     tcrt_CDData -> TWFCD_Table.cda_FirstTrack = tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[2];
  386.     tcrt_CDData -> TWFCD_Table.cda_LastTrack  = tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[3];
  387.  
  388.     tcrt_CDData -> TWFCD_Table.cda_CDLength = 0;
  389.     tcrt_CDData -> TWFCD_Table.cda_AudioLen = 0;
  390.  
  391.     // Init [0] element to a safe state
  392.     tcrt_CDData -> TWFCD_Table.cda_TrackData[0].cdt_Audio = FALSE;
  393.  
  394.     // Init first audio to TWFCD_NOAUDIO
  395.     tcrt_CDData -> TWFCD_Table.cda_FirstAudio = TWFCD_NOAUDIO;
  396.  
  397.     // Account for First and last track numbers..
  398.     if(TOCLength >= 2) TOCLength -= 2;
  399.  
  400.     for(TOCPtr = (struct TOCData *)&tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[4]; (UBYTE *)TOCPtr < (&tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[4] + TOCLength); TOCPtr = (struct TOCData *)((UBYTE *)TOCPtr + 8)) {
  401.         NextTOCPtr = (struct TOCData *)((UBYTE *)TOCPtr + 8);
  402.  
  403.         // Check if track is audio or data
  404.         if((TOCPtr -> Track >= tcrt_CDData -> TWFCD_Table.cda_FirstTrack) &&
  405.            (TOCPtr -> Track <= tcrt_CDData -> TWFCD_Table.cda_LastTrack)) {
  406.             if(TOCPtr -> Flags & 0x04) {
  407.                 // Data
  408.                 tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Audio = FALSE;
  409.                 if(TOCPtr -> Track > 1) Enhanced = TRUE;
  410.             } else {
  411.                 // Audio track...
  412.                 tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Audio = TRUE;
  413.                 if(TOCPtr -> Track < tcrt_CDData -> TWFCD_Table.cda_FirstAudio) {
  414.                     tcrt_CDData -> TWFCD_Table.cda_FirstAudio = TOCPtr -> Track;
  415.                 }
  416.                 tcrt_CDData -> TWFCD_Table.cda_AudioLen += tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Length;
  417.             }
  418.             tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Address = TOCPtr -> Addr;
  419.             tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Length  = ((NextTOCPtr -> Addr - 1) - TOCPtr -> Addr); // / 75;
  420.  
  421.             tcrt_CDData -> TWFCD_Table.cda_CDLength += tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Length;
  422.         }
  423.  
  424.         if(TOCPtr -> Track == 170) {
  425.             TOC170Adr = (ULONG)TOCPtr -> Addr;
  426.         }
  427.  
  428.         DEBUGLOG(kprintf("TWFCD_ReadTOC: TOCPtr -> track = %ld, TOCPtr -> addr = %06lX\n", (ULONG)TOCPtr -> Track, (ULONG)TOCPtr -> Addr);)
  429.     }
  430.  
  431.     // Compose a MCDPlayer compatible CDID code.
  432.     if(tcrt_CDData -> TWFCD_Table.cda_LastTrack > 3) {
  433.         if(!Enhanced) {
  434.             sprintf(tcrt_CDData -> TWFCD_Table.cda_DiskId, "ID%02lu%06lX%06lX", (ULONG)(tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[3] - tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[2]) + 1, (ULONG)tcrt_CDData -> TWFCD_Table.cda_TrackData[3].cdt_Address, TOC170Adr);
  435.         } else {
  436.             sprintf(tcrt_CDData -> TWFCD_Table.cda_DiskId, "ID%02lu%06lX%06lX", (ULONG)(tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[3] - tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[2]) + 1, (ULONG)tcrt_CDData -> TWFCD_Table.cda_TrackData[2].cdt_Address, TOC170Adr);
  437.         }
  438.     } else {
  439.         sprintf(tcrt_CDData -> TWFCD_Table.cda_DiskId, "ID%02lu000000%06lX", (ULONG)(tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[3] - tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[2]) + 1, TOC170Adr);
  440.     }
  441.  
  442.     DEBUGLOG(kprintf("TWFCD_ReadTOC: ID is %s\n", tcrt_CDData -> TWFCD_Table.cda_DiskId););
  443.  
  444.     tcrt_CDData -> TWFCD_Table.cda_GotTOC = TRUE;
  445.  
  446.     // Check the status of the command
  447.     if (ReturnCode == 0) {
  448.         return(TWFCD_OK);
  449.     }
  450.     else {
  451.         return(TWFCD_FAIL);
  452.     }
  453. }/*GFE*/
  454.  
  455.  
  456. /* ULONG TWFCD_PlayTracks(TWFCDData *, TagItem *)                            */
  457. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=                            */
  458. /* This function starts the playback of one or more tracks on the CD referred*/
  459. /* to by the TWFCDData structure. It supports several playback command forms */
  460. /* specifically PLAYAUDIO10, PLAYAUDIO12 and PLAYAUDIO_TRACKINDEX. See the   */
  461. /* tags for more details of this. This automatically calls TWFCD_ReadTOC if  */
  462. /* the table of contents has not already been read.                          */
  463.  
  464. /*GFS*/  ULONG TWFCD_PlayTracks(REG(a0) struct TWFCDData *tcpt_CDData, REG(a1) struct TagItem *pt_Tags)
  465. {
  466.     struct TagItem *CountItem  = NULL;
  467.  
  468.            LONG     Start      = 0;
  469.            LONG     tcpt_Length     = 0;
  470.  
  471.            ULONG    ReturnCode = 0;
  472.            ULONG    Status     = 0;
  473.  
  474.            ULONG    CmdLength  = 10;
  475.  
  476.     UBYTE PlayCmd[12] =
  477.     {
  478.         SCSI_CMD_PLAYAUDIO_TRACKINDEX,
  479.         PAD,
  480.         PAD,
  481.         PAD,
  482.         0,   // Start track
  483.         1,   // Index
  484.         PAD,
  485.         0,   // End track
  486.         1,   // Index
  487.         PAD,
  488.         PAD,
  489.         PAD
  490.     };
  491.  
  492.     // Get the play mode
  493.     PlayCmd[0] = (UBYTE)GetTagData(TWFCD_Track_PlayMode, SCSI_CMD_PLAYAUDIO12, pt_Tags);
  494.  
  495.     // is the TOC valid?
  496.     if(!tcpt_CDData -> TWFCD_Table.cda_GotTOC) {
  497.         ReturnCode = TWFCD_ReadTOC(tcpt_CDData);
  498.         if(ReturnCode != TWFCD_OK) return(ReturnCode);
  499.     }
  500.  
  501.     // Bomb if nothing to play.
  502.     if(tcpt_CDData -> TWFCD_Table.cda_FirstAudio == TWFCD_NOAUDIO) return(TWFCD_NOAUDIO);
  503.  
  504.     // Start Track
  505.     Start = GetTagData(TWFCD_Track_Start, tcpt_CDData -> TWFCD_Table.cda_FirstAudio, pt_Tags);
  506.  
  507.     // End Track
  508.     if(CountItem = FindTagItem(TWFCD_Track_Count, pt_Tags)) {
  509.         tcpt_Length = Start + (CountItem -> ti_Data - 1);
  510.     } else {
  511.         tcpt_Length = GetTagData(TWFCD_Track_End, tcpt_CDData -> TWFCD_Table.cda_LastTrack, pt_Tags);
  512.     }
  513.  
  514.     // Make sure Start < End
  515.     if(Start > tcpt_Length) {
  516.         ReturnCode = Start;
  517.         Start = tcpt_Length;
  518.         tcpt_Length = ReturnCode;
  519.     }
  520.  
  521.     // sort out the command...
  522.     if(PlayCmd[0] == SCSI_CMD_PLAYAUDIO_TRACKINDEX) {
  523.         PlayCmd[4] = Start;
  524.         PlayCmd[7] = tcpt_Length;
  525.     } else {
  526.  
  527.         // Common to PLAY10 and PLAY12
  528.         Start = tcpt_CDData -> TWFCD_Table.cda_TrackData[Start].cdt_Address;
  529.         PlayCmd[2] = (Start & 0xFF000000) >> 24;
  530.         PlayCmd[3] = (Start & 0x00FF0000) >> 16;
  531.         PlayCmd[4] = (Start & 0x0000FF00) >>  8;
  532.         PlayCmd[5] = (Start & 0x000000FF);
  533.  
  534.         tcpt_Length = (tcpt_CDData -> TWFCD_Table.cda_TrackData[tcpt_Length].cdt_Address - Start) + tcpt_CDData -> TWFCD_Table.cda_TrackData[tcpt_Length].cdt_Length;
  535.  
  536.         if(PlayCmd[0] == SCSI_CMD_PLAYAUDIO12) {
  537.             PlayCmd[6] = (tcpt_Length & 0xFF000000) >> 24;
  538.             PlayCmd[7] = (tcpt_Length & 0x00FF0000) >> 16;
  539.             PlayCmd[8] = (tcpt_Length & 0x0000FF00) >>  8;
  540.             PlayCmd[9] = (tcpt_Length & 0x000000FF);
  541.  
  542.             CmdLength = 12;
  543.         } else {
  544.  
  545.             // PLAY10
  546.             PlayCmd[7] = (tcpt_Length & 0x0000FF00) >> 8;
  547.             PlayCmd[8] = (tcpt_Length & 0x000000FF);
  548.         }
  549.     }
  550.  
  551.     // Setup the direct SCSICmd
  552.     Status = TWFCD_DoSCSICmd(tcpt_CDData,
  553.                              tcpt_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
  554.                              PlayCmd, CmdLength,
  555.                              (SCSIF_READ | SCSIF_AUTOSENSE));
  556.  
  557.     if(Status != TWFCD_OK) return(TWFCD_FAIL);
  558.  
  559.     // Play the tracks - asynchronously (the play returns
  560.     // right after a successful start)
  561.     ReturnCode = DoIO((struct IORequest *) tcpt_CDData -> TWFCD_Cmd.cdc_IOStdReq);
  562.  
  563.     // Check the status of the direct cmd
  564.     if (tcpt_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) {
  565.         return(TWFCD_FAIL);
  566.     }
  567.  
  568.     // Check the status of the command
  569.     if(ReturnCode == 0) {
  570.         return(TWFCD_OK);
  571.     } else {
  572.         return(TWFCD_FAIL);
  573.     }
  574.  
  575. } /*GFE*/
  576.  
  577.  
  578. /* ULONG TWFCD_StopPlay(TWFCDData *)                                         */
  579. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-                                         */
  580. /* This halts playback on the CD identified by the CD data structure. Note   */
  581. /* that some CD players may not function correctly with this version, I am   */
  582. /* researching other methods of playback halting.                            */
  583.  
  584. /*GFS*/  ULONG TWFCD_StopPlay(REG(a0) struct TWFCDData *tcsp_CDData)
  585. {
  586.     ULONG ReturnCode = 0;
  587.     ULONG Status     = 0;
  588.  
  589.     UBYTE StopCmd[6] =
  590.     {
  591.         SCSI_CMD_START_STOP_UNIT,
  592.         PAD,
  593.         PAD,
  594.         PAD,
  595.         PAD,
  596.         PAD
  597.     };
  598.  
  599.     DEBUGLOG(kprintf("TWFCD_StopPlay: in stop, compiling command\n");)
  600.  
  601.     // Check for CD in drive
  602.     tcsp_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Command = TD_CHANGESTATE;
  603.     DoIO((struct IORequest *)tcsp_CDData -> TWFCD_Cmd.cdc_IOStdReq);
  604.     if(tcsp_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Actual != 0)
  605.         return(TWFCD_OK);
  606.  
  607.     // Setup the direct SCSICmd
  608.     Status = TWFCD_DoSCSICmd(tcsp_CDData,
  609.                              tcsp_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
  610.                              StopCmd, sizeof(StopCmd),
  611.                              (SCSIF_READ | SCSIF_AUTOSENSE));
  612.  
  613.     DEBUGLOG(kprintf("TWFCD_StopPlay: command ready\n");)
  614.  
  615.     if(Status != TWFCD_OK) return(TWFCD_FAIL);
  616.  
  617.     DEBUGLOG(kprintf("TWFCD_StopPlay: Sending command to device\n");)
  618.  
  619.     // Stop the CD (a play of length 0)
  620.     ReturnCode = DoIO((struct IORequest *)tcsp_CDData -> TWFCD_Cmd.cdc_IOStdReq);
  621.  
  622.     DEBUGLOG(kprintf("TWFCD_StopPlay: command sent, checking sense data\n");)
  623.  
  624.     // Check the status of the direct cmd
  625.     if(tcsp_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) return(TWFCD_FAIL);
  626.  
  627.     DEBUGLOG(kprintf("TWFCD_StopPlay: sense check passed ok, returning\n");)
  628.  
  629.     // Check the status of the command
  630.     if(ReturnCode == 0) {
  631.         // not paused any more!
  632.         tcsp_CDData -> TWFCD_Table.cda_Paused = FALSE;
  633.         return(TWFCD_OK);
  634.     } else {
  635.         return(TWFCD_FAIL);
  636.     }
  637. } /*GFE*/
  638.  
  639.  
  640. /* ULONG TWFCD_PausePlay(TWFCDData *)                                        */
  641. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=                                        */
  642. /* As you'd expect, this pauses the playback on the specified device. Call   */
  643. /* this again to resume the playback.                                        */
  644.  
  645. /*GFS*/  ULONG TWFCD_PausePlay(REG(a0) struct TWFCDData *tcpp_CDData)
  646. {
  647.  
  648.     ULONG ReturnCode = 0;
  649.     ULONG Status     = 0;
  650.  
  651.     UBYTE PauseCmd[10] = {
  652.         SCSI_CMD_PAUSE_RESUME,
  653.         PAD,
  654.         PAD,
  655.         PAD,
  656.         PAD,
  657.         PAD,
  658.         PAD,
  659.         PAD,
  660.         0,
  661.         PAD
  662.     };
  663.  
  664.     if(tcpp_CDData -> TWFCD_Table.cda_Paused) {
  665.         DEBUGLOG(kprintf("TWFCD_PausePlay: CD is paused, resuming playback\n");)
  666.         PauseCmd[8] = 1;
  667.         tcpp_CDData -> TWFCD_Table.cda_Paused = FALSE;
  668.     } else {
  669.         DEBUGLOG(kprintf("TWFCD_PausePlay: CD is playing, pausing playback\n");)
  670.         PauseCmd[8] = 0;
  671.         tcpp_CDData -> TWFCD_Table.cda_Paused = TRUE;
  672.     }
  673.  
  674.     // Setup the direct SCSICmd
  675.     Status = TWFCD_DoSCSICmd(tcpp_CDData,
  676.                              tcpp_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
  677.                              PauseCmd, sizeof(PauseCmd),
  678.                              (SCSIF_READ | SCSIF_AUTOSENSE));
  679.  
  680.     if(Status != TWFCD_OK) return(TWFCD_FAIL);
  681.  
  682.     // Send the command...
  683.     ReturnCode = DoIO((struct IORequest *)tcpp_CDData -> TWFCD_Cmd.cdc_IOStdReq);
  684.  
  685.     // Check the status of the direct cmd
  686.     if (tcpp_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) return(TWFCD_FAIL);
  687.  
  688.     DEBUGLOG(kprintf("TWFCD_PausePlay: return code is %d\n", ReturnCode);)
  689.  
  690.     // Check the status of the command
  691.     if (ReturnCode == 0) {
  692.         return(TWFCD_OK);
  693.     } else {
  694.         return(TWFCD_FAIL);
  695.     }
  696. } /*GFE*/
  697.  
  698.  
  699. /* ULONG TWFCD_MotorControl(TWFCDData *, UBYTE)                              */
  700. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=                              */
  701. /* This allows the motor and tray of the CD to be controlled. The value in   */
  702. /* newStatus determines the operation you wish to perform, valid parameters  */
  703. /* are:                                                                      */
  704. /*                                                                           */
  705. /* TWFCD_MOTOR_STOP     - Turn off the drive (stops playback automagically)  */
  706. /* TWFCD_MOTOR_START    - Turns the motor on (but does nothing else..)       */
  707. /* TWFCD_MOTOR_EJECT    - Opens the CD tray if it is not already open.       */
  708. /* TWFCD_MOTOR_LOAD     - Closes the CD tray.                                */
  709.  
  710. /*GFS*/  ULONG TWFCD_MotorControl(REG(a0) struct TWFCDData *tcmc_CDData, REG(d0) UBYTE newStatus)
  711. {
  712.     ULONG returnCode  = 0;
  713.     ULONG status      = 0;
  714.     UBYTE ejectCmd[6] =
  715.     {
  716.         SCSI_CMD_START_STOP_UNIT,
  717.         0,
  718.         PAD,
  719.         PAD,
  720.         0,                        // Byte 5 = start/stop eject/insert
  721.         PAD,
  722.     };
  723.  
  724.     ejectCmd[4] = newStatus;
  725.     status = TWFCD_DoSCSICmd(tcmc_CDData,
  726.                              tcmc_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
  727.                              &ejectCmd[0], sizeof(ejectCmd),
  728.                              (SCSIF_READ | SCSIF_AUTOSENSE));
  729.  
  730.     if(status != TWFCD_OK) {
  731.         return(TWFCD_FAIL);
  732.     }
  733.  
  734.     returnCode = DoIO((struct IORequest *) tcmc_CDData -> TWFCD_Cmd.cdc_IOStdReq);
  735.  
  736.     // Check the status of the direct cmd
  737.     if (tcmc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) {
  738.  
  739.         // Conceivably, someone may want to know why, so a debug
  740.         // call should interpret the field
  741.         return(TWFCD_FAIL);
  742.     }
  743.  
  744.     // Check the status of the command
  745.     if (returnCode == 0) {
  746.         return(TWFCD_OK);
  747.     }
  748.     else {
  749.         return(TWFCD_FAIL);
  750.     }
  751. }/*GFE*/
  752.  
  753.  
  754. /* ULONG TWFCD_ReadSubChannel(TWFCDData *)                                   */
  755. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-                                   */
  756. /* This reads the sub-Q channel data from the specified CD. It only reads in */
  757. /* format 1 (CD position information), although it could be extended to      */
  758. /* support all format modes.                                                 */
  759.  
  760. /*GFS*/  ULONG TWFCD_ReadSubChannel(REG(a0) struct TWFCDData *tcrs_CDData)
  761. {
  762.     struct SubQData *chanData = NULL;
  763.     ULONG returnCode = 0;
  764.     ULONG status     = 0;
  765.  
  766.     UBYTE readCmd[10] =
  767.     {
  768.         SCSI_CMD_READSUB_CHANNEL,
  769.         0,
  770.         0x40,
  771.         0x01,
  772.         0,
  773.         0,
  774.         0,
  775.         0,
  776.         CDBUFFER_SIZE,
  777.         0,
  778.     };
  779.  
  780.     // Setup the direct SCSICmd
  781.     status = TWFCD_DoSCSICmd(tcrs_CDData,
  782.                              tcrs_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
  783.                              &readCmd[0], sizeof(readCmd),
  784.                              (SCSIF_READ | SCSIF_AUTOSENSE));
  785.  
  786.     if (status != TWFCD_OK) {
  787.         return (TWFCD_FAIL);
  788.     }
  789.  
  790.     returnCode = DoIO((struct IORequest *) tcrs_CDData -> TWFCD_Cmd.cdc_IOStdReq);
  791.  
  792.     // Check the status of the direct cmd
  793.     if(tcrs_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) {
  794.  
  795.         // Conceivably, someone may want to know why, so a debug
  796.         // call should interpret the field
  797.         return(TWFCD_FAIL);
  798.     }
  799.  
  800.     chanData = (struct SubQData *)tcrs_CDData -> TWFCD_Cmd.cdc_SCSIData;
  801.  
  802.     // Copy the settings...
  803.     tcrs_CDData -> TWFCD_Track.cds_AudioStatus = chanData -> AudioStatus;
  804.  
  805.     // ... provided they are valid of course...
  806.     if((chanData -> AudioStatus != TWFCD_AUDIOSTATUS_INVALID) &&
  807.        (chanData -> AudioStatus != TWFCD_AUDIOSTATUS_NOSTATUS)){
  808.  
  809.         tcrs_CDData -> TWFCD_Track.cds_PlayTrack   = chanData -> TrackNum   ;
  810.         tcrs_CDData -> TWFCD_Track.cds_RelAddress  = chanData -> RelativeAdr;
  811.  
  812.         if(tcrs_CDData -> TWFCD_Table.cda_GotTOC) {
  813.             tcrs_CDData -> TWFCD_Track.cds_RelRemain = tcrs_CDData -> TWFCD_Table.cda_TrackData[chanData -> TrackNum].cdt_Length - chanData -> RelativeAdr;
  814.         } else {
  815.             tcrs_CDData -> TWFCD_Track.cds_RelRemain = 0;
  816.         }
  817.  
  818.         tcrs_CDData -> TWFCD_Track.cds_AbsAddress  = chanData -> AbsoluteAdr;
  819.         if(tcrs_CDData -> TWFCD_Table.cda_GotTOC) {
  820.             tcrs_CDData -> TWFCD_Track.cds_AbsRemain = tcrs_CDData -> TWFCD_Table.cda_CDLength - chanData -> AbsoluteAdr;
  821.         } else {
  822.             tcrs_CDData -> TWFCD_Track.cds_AbsRemain = 0;
  823.         }
  824.     }
  825.  
  826.     // Check the status of the command
  827.     if (returnCode == 0) {
  828.         return(TWFCD_OK);
  829.     }
  830.     else {
  831.         return(TWFCD_FAIL);
  832.     }
  833. } /*GFE*/
  834.  
  835.  
  836. /* ULONG TWFCD_doSCSICmd(TWFCDData *, UBYTE *, int, UBYTE *, int, UBYTE)     */
  837. /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-     */
  838. /* This compiles the scsi-direct command ready for sending to the device via */
  839. /* DoIO(). It ensures the SCSI command structure in the TWFCDData struct is  */
  840. /* set up correctly and the IORequest contains the command.                  */
  841.  
  842. /*GFS*/  static ULONG TWFCD_DoSCSICmd(struct TWFCDData *tcdc_CDData, UBYTE *buffer, int bufferSize, UBYTE *cmd, int cmdSize, UBYTE flags)
  843. {
  844.     // Set up the Play IOReq
  845.     tcdc_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Command = HD_SCSICMD;
  846.     tcdc_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Length = sizeof(struct SCSICmd);
  847.     tcdc_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Data = (APTR)&tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd;
  848.  
  849.     // Setup the direct SCSICmd
  850.     tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_Data = (APTR) buffer;
  851.     tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_Length = bufferSize;
  852.  
  853.     tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual = 0;
  854.     tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseData = tcdc_CDData -> TWFCD_Cmd.cdc_SCSISense;
  855.     tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseLength = CDSENSE_SIZE;
  856.  
  857.     tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_Command = (UBYTE *) cmd;
  858.     tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_CmdLength = cmdSize;
  859.     tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_Flags = flags;
  860.  
  861.     return (TWFCD_OK);
  862. }/*GFE*/
  863.  
  864.  
  865.  
  866.