home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Enigma Amiga Life 113
/
EnigmaAmiga113CD.iso
/
software
/
sviluppo
/
quake_src
/
twfsound_cd.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-06-17
|
33KB
|
866 lines
// ___ ___
// _/ /_______\ \_ ___ ___ __ _ _ __ ___ ___
//__// / _______ \ \\___/ \___
//_/ | ' \__ __/ ` | \_/ © Copyright 1998, Christopher Page \__
// \ | | | |__ | | / \ All Rights Reserved /
// >| . | _/ . |< >--- --- -- - - -- --- ---<
// / \ \ | | / / \ / This file may not be distributed, reproduced or \
// \ \ \_/ \_/ / / \ altered, in full or in part, without written /
// \ \ / / \ permission from Christopher Page. Legal /
// //\ \_________/ /\\ //\ action will be taken in cases where /
//¯ ¯¯\ _______ /¯¯ ¯ ¯¯\ this notice is not obeyed. /¯¯¯¯¯
//¯¯¯¯¯\_/ \_/¯¯¯¯¯¯¯¯¯\ ___________________________________ /¯¯¯¯¯¯
// \_/ \_/
//
// Description:
//
// CD playback functions
//
// Functions:
//
// struct TWFCDData * TWFCD_Setup(REG(a0) STRPTR DeviceName, REG(d0) UBYTE UnitNum)
// ULONG TWFCD_Shutdown(REG(a0) struct TWFCDData *tcs_OldData)
// ULONG TWFCD_ReadTOC(REG(a0) struct TWFCDData *tcrt_CDData)
// ULONG TWFCD_PlayTracks(REG(a0) struct TWFCDData *tcpt_CDData, REG(a1) struct TagItem *pt_Tags)
// ULONG TWFCD_StopPlay(REG(a0) struct TWFCDData *tcsp_CDData)
// ULONG TWFCD_PausePlay(REG(a0) struct TWFCDData *tcpp_CDData)
// ULONG TWFCD_MotorControl(REG(a0) struct TWFCDData *tcet_CDData, REG(d0) UBYTE newStatus)
// ULONG TWFCD_ReadSubChannel(REG(a0) struct TWFCDData *tcrs_CDData)
// static ULONG TWFCD_DoSCSICmd(struct TWFCDData *tcdc_CDData, UBYTE *buffer, int bufferSize, UBYTE *cmd, int cmdSize, UBYTE flags)
//
// Detail:
//
// This code provides a simple method to query the contents of a CD and start, stop
// and pause playback of audio tracks on that CD. This is still work in progress and
// likely to have problems on some non-standard/ older CD-ROM models.
//
//
// Version:
//
// $VER: twfsound_cd.c 3.18 (17/10/1999)
//
// Fold Markers:
//
// Start: /*GFS*/
// End: /*GFE*/
//
// Coded by and received from "The World Foundry",
// (chris@worldfoundry.demon.co.uk)
// This code is *not* under GPL, but it can be used for Freeware and
// commercial projects. It is allowed to include the source-code, but
// it is not required (Unless the game in question - like Quake -
// requires to release the source). The release of this code inside
// a GPL project like Quake does not put other projects which use
// this code GPL, as this CD Audio Replay Code is not GPL, despite the
// fact that the source code is allowed to be included.
// Despite being freeware, source code modifications in this source
// require the agreement of TWF. This especially includes also
// recompiles to different Kernels.
/*
Portability
===========
This file includes support for WarpUP and 68k. In case of
PowerUP the AllocVecPPC/FreeVecPPC calls would be needed
to be replaced by alternative functions. I am not sure
if a modification to compile this with PowerUP would be
agreed by the original authors. It is possible that this
might be a problem. But if someone wants to compile Quake
for PowerUP - i personally doubt that this is worth the
effort for anyone - he has to check this out with TWF,
or look for a different CD Audio Replay Solution. I did
not ask TWF about this, as I personally don't care if a
PowerUP version will be done or not. Some includes would
need to be changed for different compilers, like usual.
Steffen Haeuser (MagicSN@Birdland.es.bawue.de)
*/
/*
* Additional changes, as required for our Quake port, and
* PowerUp support by Frank Wille <frank@phoenix.owl.de> (phx)
*/
#pragma amiga-align
#include <exec/exec.h>
#include <devices/trackdisk.h>
#include "twfsound_cd.h"
#ifdef __PPC__
#ifdef WOS
#include <clib/powerpc_protos.h>
#else /* PowerUp */
#include <powerup/gcclib/powerup_protos.h>
#endif
#endif
#pragma default-align
#include <stdio.h>
#if 0 /* initialized in sys_amiga.c (phx) */
struct Library *UtilityBase;
#endif
// Non-standard, and a bit of a cheat, but it makes the code easier to read
struct TOCData
{
UBYTE Empty1; // Padding
UBYTE Flags ;
UBYTE Track ;
UBYTE Empty2;
ULONG Addr ;
};
struct SubQData
{
UBYTE Reserved ; // Header: bytes 0 to 3
UBYTE AudioStatus;
UWORD DataLength ;
UBYTE Formatcode ; // Data: byte 4
UBYTE AdrControl ; // Byte 5
UBYTE TrackNum ; // Byte 6
UBYTE IndexNum ; // Byte 7
ULONG AbsoluteAdr; // Bytes 8, 9, 10, 11
ULONG RelativeAdr; // Bytes 12, 13, 14, 15
};
/* (phx) @@@? use -DNDEBUG instead! */
/* #define kprintf */
static ULONG TWFCD_DoSCSICmd(struct TWFCDData *, UBYTE *, int , UBYTE *, int, UBYTE);
/* ULONG TWFCD_Setup(STRPTR, UBYTE) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* This routine creates the message port and IO structure needed by the */
/* device, then it attepmts to open the device.. nothig really groundbreaking*/
/* in here you know... */
/*GFS*/ struct TWFCDData * TWFCD_Setup(STRPTR DeviceName, UBYTE UnitNum)
{
struct TWFCDData *NewData = NULL;
BYTE DeviceCode = 0;
#if 0 /* this should be done in sys_amiga.c (phx) */
UtilityBase=(struct Library *)OpenLibrary("utility.library",0);
#endif
// create the table...
#ifdef __PPC__
#ifdef WOS
if(!(NewData = AllocVecPPC(sizeof(struct TWFCDData), MEMF_ANY|MEMF_CLEAR,0))) return(NULL);
(NewData->TWFCD_Cmd).cdc_SCSIData=AllocVecPPC(CDBUFFER_SIZE,MEMF_CHIP|MEMF_CLEAR,0);
#else /* PowerUp */
if(!(NewData = PPCAllocVec(sizeof(struct TWFCDData), MEMF_ANY|MEMF_CLEAR))) return(NULL);
(NewData->TWFCD_Cmd).cdc_SCSIData=PPCAllocVec(CDBUFFER_SIZE,MEMF_CHIP|MEMF_CLEAR);
#endif
#else
if(!(NewData = AllocVec(sizeof(struct TWFCDData), MEMF_ANY|MEMF_CLEAR))) return(NULL);
(NewData->TWFCD_Cmd).cdc_SCSIData=AllocVec(CDBUFFER_SIZE,MEMF_CHIP|MEMF_CLEAR);
#endif
if (!((NewData->TWFCD_Cmd).cdc_SCSIData))
{
#ifdef __PPC__
#ifdef WOS
FreeVecPPC(NewData);
#else /* PowerUp */
PPCFreeVec(NewData);
#endif
#else
FreeVec(NewData);
#endif
return 0;
}
#ifdef __PPC__
#ifdef WOS
(NewData->TWFCD_Cmd).cdc_SCSISense=AllocVecPPC(CDSENSE_SIZE,MEMF_CHIP|MEMF_CLEAR,0);
if (!((NewData->TWFCD_Cmd).cdc_SCSISense))
{
FreeVecPPC((NewData->TWFCD_Cmd).cdc_SCSIData);
FreeVecPPC(NewData);
return 0;
}
(NewData->TWFCD_Cmd).cdc_TOCBuffer=AllocVecPPC(CDTOC_SIZE,MEMF_CHIP|MEMF_CLEAR,0);
if (!((NewData->TWFCD_Cmd).cdc_TOCBuffer))
{
FreeVecPPC((NewData->TWFCD_Cmd).cdc_SCSIData);
FreeVecPPC((NewData->TWFCD_Cmd).cdc_SCSISense);
FreeVecPPC(NewData);
}
#else /* PowerUp */
(NewData->TWFCD_Cmd).cdc_SCSISense=PPCAllocVec(CDSENSE_SIZE,MEMF_CHIP|MEMF_CLEAR);
if (!((NewData->TWFCD_Cmd).cdc_SCSISense))
{
PPCFreeVec((NewData->TWFCD_Cmd).cdc_SCSIData);
PPCFreeVec(NewData);
return 0;
}
(NewData->TWFCD_Cmd).cdc_TOCBuffer=PPCAllocVec(CDTOC_SIZE,MEMF_CHIP|MEMF_CLEAR);
if (!((NewData->TWFCD_Cmd).cdc_TOCBuffer))
{
PPCFreeVec((NewData->TWFCD_Cmd).cdc_SCSIData);
PPCFreeVec((NewData->TWFCD_Cmd).cdc_SCSISense);
PPCFreeVec(NewData);
}
#endif
#else
(NewData->TWFCD_Cmd).cdc_SCSISense=AllocVec(CDSENSE_SIZE,MEMF_CHIP|MEMF_CLEAR);
if (!((NewData->TWFCD_Cmd).cdc_SCSISense))
{
FreeVec((NewData->TWFCD_Cmd).cdc_SCSIData);
FreeVec(NewData);
return 0;
}
(NewData->TWFCD_Cmd).cdc_TOCBuffer=AllocVec(CDTOC_SIZE,MEMF_CHIP|MEMF_CLEAR);
if (!((NewData->TWFCD_Cmd).cdc_TOCBuffer))
{
FreeVec((NewData->TWFCD_Cmd).cdc_SCSIData);
FreeVec((NewData->TWFCD_Cmd).cdc_SCSISense);
FreeVec(NewData);
}
#endif
// Create the reply MsgPort for the device
if(!(NewData -> TWFCD_Cmd.cdc_MsgPort = (struct MsgPort *)CreateMsgPort())) return(NULL);
// Create our IORequest, to send Play IO messages to the CD device
NewData -> TWFCD_Cmd.cdc_IOStdReq = (struct IOStdReq *)CreateIORequest(NewData -> TWFCD_Cmd.cdc_MsgPort, sizeof(struct IOStdReq));
if(NewData -> TWFCD_Cmd.cdc_IOStdReq == NULL) return (NULL);
// Open the CD device for the Play commands
DeviceCode = OpenDevice(DeviceName, UnitNum, (struct IORequest *)NewData -> TWFCD_Cmd.cdc_IOStdReq, 0);
if(DeviceCode != 0) return(NULL);
((NewData->TWFCD_Cmd).cdc_IOStdReq)->io_Command = CMD_START;
DoIO((struct IORequest *)((NewData->TWFCD_Cmd).cdc_IOStdReq));
// Device is now open, CD play available.
return(NewData);
} /*GFE*/
/* ULONG TWFCD_Shutdown(TWFCDData *) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/* Call this to close the CD playback device, IO struct and message port */
/* allocated by the TWFCD_Setup routine. */
/*GFS*/ ULONG TWFCD_Shutdown(struct TWFCDData *tcs_OldData)
{
if(!tcs_OldData) return(TWFCD_FAIL);
// Halt playback (so few people bother doing this.....)
if(tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq) TWFCD_StopPlay(tcs_OldData);
// Kill memory allocation
if(tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq) CloseDevice ((struct IORequest*)tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq);
if(tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq) {
DeleteIORequest((struct IORequest*)tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq);
tcs_OldData -> TWFCD_Cmd.cdc_IOStdReq = NULL;
}
if(tcs_OldData -> TWFCD_Cmd.cdc_MsgPort ) {
DeleteMsgPort (tcs_OldData -> TWFCD_Cmd.cdc_MsgPort);
tcs_OldData -> TWFCD_Cmd.cdc_MsgPort = NULL;
}
#ifdef __PPC__
#ifdef WOS
if (tcs_OldData->TWFCD_Cmd.cdc_SCSIData) FreeVecPPC(tcs_OldData->TWFCD_Cmd.cdc_SCSIData);
tcs_OldData->TWFCD_Cmd.cdc_SCSIData=0;
if (tcs_OldData->TWFCD_Cmd.cdc_SCSISense) FreeVecPPC(tcs_OldData->TWFCD_Cmd.cdc_SCSISense);
tcs_OldData->TWFCD_Cmd.cdc_SCSISense=0;
if (tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer) FreeVecPPC(tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer);
tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer=0;
if (tcs_OldData) FreeVecPPC(tcs_OldData);
#else /* PowerUp */
if (tcs_OldData->TWFCD_Cmd.cdc_SCSIData) PPCFreeVec(tcs_OldData->TWFCD_Cmd.cdc_SCSIData);
tcs_OldData->TWFCD_Cmd.cdc_SCSIData=0;
if (tcs_OldData->TWFCD_Cmd.cdc_SCSISense) PPCFreeVec(tcs_OldData->TWFCD_Cmd.cdc_SCSISense);
tcs_OldData->TWFCD_Cmd.cdc_SCSISense=0;
if (tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer) PPCFreeVec(tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer);
tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer=0;
if (tcs_OldData) PPCFreeVec(tcs_OldData);
#endif
#else
if (tcs_OldData->TWFCD_Cmd.cdc_SCSIData) FreeVec(tcs_OldData->TWFCD_Cmd.cdc_SCSIData);
tcs_OldData->TWFCD_Cmd.cdc_SCSIData=0;
if (tcs_OldData->TWFCD_Cmd.cdc_SCSISense) FreeVec(tcs_OldData->TWFCD_Cmd.cdc_SCSISense);
tcs_OldData->TWFCD_Cmd.cdc_SCSISense=0;
if (tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer) FreeVec(tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer);
tcs_OldData->TWFCD_Cmd.cdc_TOCBuffer=0;
if (tcs_OldData) FreeVec(tcs_OldData);
#endif
tcs_OldData=0;
return(TWFCD_OK);
} /*GFE*/
/* ULONG TWFCD_ReadTOC(void) */
/* -=-=-=-=-=-=-=-=-=-=-=-=- */
/* This routine loads the table of contents off a CD and calculates a unique */
/* disc identifier. out of range track identifiers are ignored (except by the*/
/* ID generation) as most disks seem to have a mysterious track 170..?!! */
/*GFS*/ ULONG TWFCD_ReadTOC(REG(a0) struct TWFCDData *tcrt_CDData)
{
ULONG ReturnCode = 0;
BYTE Position = 0;
ULONG Status = 0;
ULONG TOCLength = 0;
ULONG TOC170Adr = 0; // This records the address of track 170
BOOL Enhanced = FALSE; // TRUE if a data track above track 1.
struct TOCData *TOCPtr = NULL;
struct TOCData *NextTOCPtr = NULL;
ULONG LastStart = 0;
ULONG ThisStart = 0;
UBYTE ReadTOCCmd[10] = {
SCSI_CMD_READTOC,
0,
PAD,
PAD,
PAD,
PAD,
0, // Starting track
0x03, 0x24, // Max. TOC data on current CDs is 0x324 bytes
// or 100 track descriptors
PAD
};
// Check for CD in drive
tcrt_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Command = TD_CHANGESTATE;
DoIO((struct IORequest *)tcrt_CDData -> TWFCD_Cmd.cdc_IOStdReq);
if(tcrt_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Actual != 0)
{
tcrt_CDData -> TWFCD_Table.cda_FirstTrack = 0;
tcrt_CDData -> TWFCD_Table.cda_LastTrack = 0;
tcrt_CDData -> TWFCD_Table.cda_FirstAudio = 0;
tcrt_CDData -> TWFCD_Table.cda_CDLength = 0;
tcrt_CDData -> TWFCD_Table.cda_AudioLen = 0;
tcrt_CDData -> TWFCD_Table.cda_DiskId[0] = '\0';
tcrt_CDData -> TWFCD_Track.cds_AudioStatus = TWFCD_AUDIOSTATUS_INVALID;
tcrt_CDData -> TWFCD_Track.cds_PlayTrack = 0;
return(TWFCD_FAIL);
}
// Setup the direct SCSICmd
Status = TWFCD_DoSCSICmd(tcrt_CDData,
tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer, CDTOC_SIZE,
ReadTOCCmd, sizeof(ReadTOCCmd),
(SCSIF_READ | SCSIF_AUTOSENSE));
if(Status != TWFCD_OK) {
DEBUGLOG(kprintf("TWFCD_ReadTOC(): Status is not OK\n");)
return(TWFCD_FAIL);
}
// Read the TOC in!
ReturnCode = DoIO((struct IORequest *)tcrt_CDData -> TWFCD_Cmd.cdc_IOStdReq);
// Check the status of the direct cmd
if(tcrt_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) {
DEBUGLOG(kprintf("TWFCD_ReadTOC(): Command failed\n");)
if(tcrt_CDData -> TWFCD_Table.cda_DiskId[0] != '\0') {
tcrt_CDData -> TWFCD_Table.cda_FirstTrack = 0;
tcrt_CDData -> TWFCD_Table.cda_LastTrack = 0;
tcrt_CDData -> TWFCD_Table.cda_FirstAudio = 0;
tcrt_CDData -> TWFCD_Table.cda_CDLength = 0;
tcrt_CDData -> TWFCD_Table.cda_AudioLen = 0;
tcrt_CDData -> TWFCD_Table.cda_DiskId[0] = '\0';
tcrt_CDData -> TWFCD_Track.cds_AudioStatus = TWFCD_AUDIOSTATUS_INVALID;
tcrt_CDData -> TWFCD_Track.cds_PlayTrack = 0;
}
return(TWFCD_FAIL);
}
// Read the length of the TOC - encoded in first word
TOCLength = (tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[0] << 8) | tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[1];
// Set track info...
tcrt_CDData -> TWFCD_Table.cda_FirstTrack = tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[2];
tcrt_CDData -> TWFCD_Table.cda_LastTrack = tcrt_CDData -> TWFCD_Cmd.cdc_TOCBuffer[3];
tcrt_CDData -> TWFCD_Table.cda_CDLength = 0;
tcrt_CDData -> TWFCD_Table.cda_AudioLen = 0;
// Init [0] element to a safe state
tcrt_CDData -> TWFCD_Table.cda_TrackData[0].cdt_Audio = FALSE;
// Init first audio to TWFCD_NOAUDIO
tcrt_CDData -> TWFCD_Table.cda_FirstAudio = TWFCD_NOAUDIO;
// Account for First and last track numbers..
if(TOCLength >= 2) TOCLength -= 2;
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)) {
NextTOCPtr = (struct TOCData *)((UBYTE *)TOCPtr + 8);
// Check if track is audio or data
if((TOCPtr -> Track >= tcrt_CDData -> TWFCD_Table.cda_FirstTrack) &&
(TOCPtr -> Track <= tcrt_CDData -> TWFCD_Table.cda_LastTrack)) {
if(TOCPtr -> Flags & 0x04) {
// Data
tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Audio = FALSE;
if(TOCPtr -> Track > 1) Enhanced = TRUE;
} else {
// Audio track...
tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Audio = TRUE;
if(TOCPtr -> Track < tcrt_CDData -> TWFCD_Table.cda_FirstAudio) {
tcrt_CDData -> TWFCD_Table.cda_FirstAudio = TOCPtr -> Track;
}
tcrt_CDData -> TWFCD_Table.cda_AudioLen += tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Length;
}
tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Address = TOCPtr -> Addr;
tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Length = ((NextTOCPtr -> Addr - 1) - TOCPtr -> Addr); // / 75;
tcrt_CDData -> TWFCD_Table.cda_CDLength += tcrt_CDData -> TWFCD_Table.cda_TrackData[TOCPtr -> Track].cdt_Length;
}
if(TOCPtr -> Track == 170) {
TOC170Adr = (ULONG)TOCPtr -> Addr;
}
DEBUGLOG(kprintf("TWFCD_ReadTOC: TOCPtr -> track = %ld, TOCPtr -> addr = %06lX\n", (ULONG)TOCPtr -> Track, (ULONG)TOCPtr -> Addr);)
}
// Compose a MCDPlayer compatible CDID code.
if(tcrt_CDData -> TWFCD_Table.cda_LastTrack > 3) {
if(!Enhanced) {
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);
} else {
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);
}
} else {
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);
}
DEBUGLOG(kprintf("TWFCD_ReadTOC: ID is %s\n", tcrt_CDData -> TWFCD_Table.cda_DiskId););
tcrt_CDData -> TWFCD_Table.cda_GotTOC = TRUE;
// Check the status of the command
if (ReturnCode == 0) {
return(TWFCD_OK);
}
else {
return(TWFCD_FAIL);
}
}/*GFE*/
/* ULONG TWFCD_PlayTracks(TWFCDData *, TagItem *) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* This function starts the playback of one or more tracks on the CD referred*/
/* to by the TWFCDData structure. It supports several playback command forms */
/* specifically PLAYAUDIO10, PLAYAUDIO12 and PLAYAUDIO_TRACKINDEX. See the */
/* tags for more details of this. This automatically calls TWFCD_ReadTOC if */
/* the table of contents has not already been read. */
/*GFS*/ ULONG TWFCD_PlayTracks(REG(a0) struct TWFCDData *tcpt_CDData, REG(a1) struct TagItem *pt_Tags)
{
struct TagItem *CountItem = NULL;
LONG Start = 0;
LONG tcpt_Length = 0;
ULONG ReturnCode = 0;
ULONG Status = 0;
ULONG CmdLength = 10;
UBYTE PlayCmd[12] =
{
SCSI_CMD_PLAYAUDIO_TRACKINDEX,
PAD,
PAD,
PAD,
0, // Start track
1, // Index
PAD,
0, // End track
1, // Index
PAD,
PAD,
PAD
};
// Get the play mode
PlayCmd[0] = (UBYTE)GetTagData(TWFCD_Track_PlayMode, SCSI_CMD_PLAYAUDIO12, pt_Tags);
// is the TOC valid?
if(!tcpt_CDData -> TWFCD_Table.cda_GotTOC) {
ReturnCode = TWFCD_ReadTOC(tcpt_CDData);
if(ReturnCode != TWFCD_OK) return(ReturnCode);
}
// Bomb if nothing to play.
if(tcpt_CDData -> TWFCD_Table.cda_FirstAudio == TWFCD_NOAUDIO) return(TWFCD_NOAUDIO);
// Start Track
Start = GetTagData(TWFCD_Track_Start, tcpt_CDData -> TWFCD_Table.cda_FirstAudio, pt_Tags);
// End Track
if(CountItem = FindTagItem(TWFCD_Track_Count, pt_Tags)) {
tcpt_Length = Start + (CountItem -> ti_Data - 1);
} else {
tcpt_Length = GetTagData(TWFCD_Track_End, tcpt_CDData -> TWFCD_Table.cda_LastTrack, pt_Tags);
}
// Make sure Start < End
if(Start > tcpt_Length) {
ReturnCode = Start;
Start = tcpt_Length;
tcpt_Length = ReturnCode;
}
// sort out the command...
if(PlayCmd[0] == SCSI_CMD_PLAYAUDIO_TRACKINDEX) {
PlayCmd[4] = Start;
PlayCmd[7] = tcpt_Length;
} else {
// Common to PLAY10 and PLAY12
Start = tcpt_CDData -> TWFCD_Table.cda_TrackData[Start].cdt_Address;
PlayCmd[2] = (Start & 0xFF000000) >> 24;
PlayCmd[3] = (Start & 0x00FF0000) >> 16;
PlayCmd[4] = (Start & 0x0000FF00) >> 8;
PlayCmd[5] = (Start & 0x000000FF);
tcpt_Length = (tcpt_CDData -> TWFCD_Table.cda_TrackData[tcpt_Length].cdt_Address - Start) + tcpt_CDData -> TWFCD_Table.cda_TrackData[tcpt_Length].cdt_Length;
if(PlayCmd[0] == SCSI_CMD_PLAYAUDIO12) {
PlayCmd[6] = (tcpt_Length & 0xFF000000) >> 24;
PlayCmd[7] = (tcpt_Length & 0x00FF0000) >> 16;
PlayCmd[8] = (tcpt_Length & 0x0000FF00) >> 8;
PlayCmd[9] = (tcpt_Length & 0x000000FF);
CmdLength = 12;
} else {
// PLAY10
PlayCmd[7] = (tcpt_Length & 0x0000FF00) >> 8;
PlayCmd[8] = (tcpt_Length & 0x000000FF);
}
}
// Setup the direct SCSICmd
Status = TWFCD_DoSCSICmd(tcpt_CDData,
tcpt_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
PlayCmd, CmdLength,
(SCSIF_READ | SCSIF_AUTOSENSE));
if(Status != TWFCD_OK) return(TWFCD_FAIL);
// Play the tracks - asynchronously (the play returns
// right after a successful start)
ReturnCode = DoIO((struct IORequest *) tcpt_CDData -> TWFCD_Cmd.cdc_IOStdReq);
// Check the status of the direct cmd
if (tcpt_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) {
return(TWFCD_FAIL);
}
// Check the status of the command
if(ReturnCode == 0) {
return(TWFCD_OK);
} else {
return(TWFCD_FAIL);
}
} /*GFE*/
/* ULONG TWFCD_StopPlay(TWFCDData *) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/* This halts playback on the CD identified by the CD data structure. Note */
/* that some CD players may not function correctly with this version, I am */
/* researching other methods of playback halting. */
/*GFS*/ ULONG TWFCD_StopPlay(REG(a0) struct TWFCDData *tcsp_CDData)
{
ULONG ReturnCode = 0;
ULONG Status = 0;
UBYTE StopCmd[6] =
{
SCSI_CMD_START_STOP_UNIT,
PAD,
PAD,
PAD,
PAD,
PAD
};
DEBUGLOG(kprintf("TWFCD_StopPlay: in stop, compiling command\n");)
// Check for CD in drive
tcsp_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Command = TD_CHANGESTATE;
DoIO((struct IORequest *)tcsp_CDData -> TWFCD_Cmd.cdc_IOStdReq);
if(tcsp_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Actual != 0)
return(TWFCD_OK);
// Setup the direct SCSICmd
Status = TWFCD_DoSCSICmd(tcsp_CDData,
tcsp_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
StopCmd, sizeof(StopCmd),
(SCSIF_READ | SCSIF_AUTOSENSE));
DEBUGLOG(kprintf("TWFCD_StopPlay: command ready\n");)
if(Status != TWFCD_OK) return(TWFCD_FAIL);
DEBUGLOG(kprintf("TWFCD_StopPlay: Sending command to device\n");)
// Stop the CD (a play of length 0)
ReturnCode = DoIO((struct IORequest *)tcsp_CDData -> TWFCD_Cmd.cdc_IOStdReq);
DEBUGLOG(kprintf("TWFCD_StopPlay: command sent, checking sense data\n");)
// Check the status of the direct cmd
if(tcsp_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) return(TWFCD_FAIL);
DEBUGLOG(kprintf("TWFCD_StopPlay: sense check passed ok, returning\n");)
// Check the status of the command
if(ReturnCode == 0) {
// not paused any more!
tcsp_CDData -> TWFCD_Table.cda_Paused = FALSE;
return(TWFCD_OK);
} else {
return(TWFCD_FAIL);
}
} /*GFE*/
/* ULONG TWFCD_PausePlay(TWFCDData *) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* As you'd expect, this pauses the playback on the specified device. Call */
/* this again to resume the playback. */
/*GFS*/ ULONG TWFCD_PausePlay(REG(a0) struct TWFCDData *tcpp_CDData)
{
ULONG ReturnCode = 0;
ULONG Status = 0;
UBYTE PauseCmd[10] = {
SCSI_CMD_PAUSE_RESUME,
PAD,
PAD,
PAD,
PAD,
PAD,
PAD,
PAD,
0,
PAD
};
if(tcpp_CDData -> TWFCD_Table.cda_Paused) {
DEBUGLOG(kprintf("TWFCD_PausePlay: CD is paused, resuming playback\n");)
PauseCmd[8] = 1;
tcpp_CDData -> TWFCD_Table.cda_Paused = FALSE;
} else {
DEBUGLOG(kprintf("TWFCD_PausePlay: CD is playing, pausing playback\n");)
PauseCmd[8] = 0;
tcpp_CDData -> TWFCD_Table.cda_Paused = TRUE;
}
// Setup the direct SCSICmd
Status = TWFCD_DoSCSICmd(tcpp_CDData,
tcpp_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
PauseCmd, sizeof(PauseCmd),
(SCSIF_READ | SCSIF_AUTOSENSE));
if(Status != TWFCD_OK) return(TWFCD_FAIL);
// Send the command...
ReturnCode = DoIO((struct IORequest *)tcpp_CDData -> TWFCD_Cmd.cdc_IOStdReq);
// Check the status of the direct cmd
if (tcpp_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) return(TWFCD_FAIL);
DEBUGLOG(kprintf("TWFCD_PausePlay: return code is %d\n", ReturnCode);)
// Check the status of the command
if (ReturnCode == 0) {
return(TWFCD_OK);
} else {
return(TWFCD_FAIL);
}
} /*GFE*/
/* ULONG TWFCD_MotorControl(TWFCDData *, UBYTE) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/* This allows the motor and tray of the CD to be controlled. The value in */
/* newStatus determines the operation you wish to perform, valid parameters */
/* are: */
/* */
/* TWFCD_MOTOR_STOP - Turn off the drive (stops playback automagically) */
/* TWFCD_MOTOR_START - Turns the motor on (but does nothing else..) */
/* TWFCD_MOTOR_EJECT - Opens the CD tray if it is not already open. */
/* TWFCD_MOTOR_LOAD - Closes the CD tray. */
/*GFS*/ ULONG TWFCD_MotorControl(REG(a0) struct TWFCDData *tcmc_CDData, REG(d0) UBYTE newStatus)
{
ULONG returnCode = 0;
ULONG status = 0;
UBYTE ejectCmd[6] =
{
SCSI_CMD_START_STOP_UNIT,
0,
PAD,
PAD,
0, // Byte 5 = start/stop eject/insert
PAD,
};
ejectCmd[4] = newStatus;
status = TWFCD_DoSCSICmd(tcmc_CDData,
tcmc_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
&ejectCmd[0], sizeof(ejectCmd),
(SCSIF_READ | SCSIF_AUTOSENSE));
if(status != TWFCD_OK) {
return(TWFCD_FAIL);
}
returnCode = DoIO((struct IORequest *) tcmc_CDData -> TWFCD_Cmd.cdc_IOStdReq);
// Check the status of the direct cmd
if (tcmc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) {
// Conceivably, someone may want to know why, so a debug
// call should interpret the field
return(TWFCD_FAIL);
}
// Check the status of the command
if (returnCode == 0) {
return(TWFCD_OK);
}
else {
return(TWFCD_FAIL);
}
}/*GFE*/
/* ULONG TWFCD_ReadSubChannel(TWFCDData *) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/* This reads the sub-Q channel data from the specified CD. It only reads in */
/* format 1 (CD position information), although it could be extended to */
/* support all format modes. */
/*GFS*/ ULONG TWFCD_ReadSubChannel(REG(a0) struct TWFCDData *tcrs_CDData)
{
struct SubQData *chanData = NULL;
ULONG returnCode = 0;
ULONG status = 0;
UBYTE readCmd[10] =
{
SCSI_CMD_READSUB_CHANNEL,
0,
0x40,
0x01,
0,
0,
0,
0,
CDBUFFER_SIZE,
0,
};
// Setup the direct SCSICmd
status = TWFCD_DoSCSICmd(tcrs_CDData,
tcrs_CDData -> TWFCD_Cmd.cdc_SCSIData, CDBUFFER_SIZE,
&readCmd[0], sizeof(readCmd),
(SCSIF_READ | SCSIF_AUTOSENSE));
if (status != TWFCD_OK) {
return (TWFCD_FAIL);
}
returnCode = DoIO((struct IORequest *) tcrs_CDData -> TWFCD_Cmd.cdc_IOStdReq);
// Check the status of the direct cmd
if(tcrs_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual != 0) {
// Conceivably, someone may want to know why, so a debug
// call should interpret the field
return(TWFCD_FAIL);
}
chanData = (struct SubQData *)tcrs_CDData -> TWFCD_Cmd.cdc_SCSIData;
// Copy the settings...
tcrs_CDData -> TWFCD_Track.cds_AudioStatus = chanData -> AudioStatus;
// ... provided they are valid of course...
if((chanData -> AudioStatus != TWFCD_AUDIOSTATUS_INVALID) &&
(chanData -> AudioStatus != TWFCD_AUDIOSTATUS_NOSTATUS)){
tcrs_CDData -> TWFCD_Track.cds_PlayTrack = chanData -> TrackNum ;
tcrs_CDData -> TWFCD_Track.cds_RelAddress = chanData -> RelativeAdr;
if(tcrs_CDData -> TWFCD_Table.cda_GotTOC) {
tcrs_CDData -> TWFCD_Track.cds_RelRemain = tcrs_CDData -> TWFCD_Table.cda_TrackData[chanData -> TrackNum].cdt_Length - chanData -> RelativeAdr;
} else {
tcrs_CDData -> TWFCD_Track.cds_RelRemain = 0;
}
tcrs_CDData -> TWFCD_Track.cds_AbsAddress = chanData -> AbsoluteAdr;
if(tcrs_CDData -> TWFCD_Table.cda_GotTOC) {
tcrs_CDData -> TWFCD_Track.cds_AbsRemain = tcrs_CDData -> TWFCD_Table.cda_CDLength - chanData -> AbsoluteAdr;
} else {
tcrs_CDData -> TWFCD_Track.cds_AbsRemain = 0;
}
}
// Check the status of the command
if (returnCode == 0) {
return(TWFCD_OK);
}
else {
return(TWFCD_FAIL);
}
} /*GFE*/
/* ULONG TWFCD_doSCSICmd(TWFCDData *, UBYTE *, int, UBYTE *, int, UBYTE) */
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */
/* This compiles the scsi-direct command ready for sending to the device via */
/* DoIO(). It ensures the SCSI command structure in the TWFCDData struct is */
/* set up correctly and the IORequest contains the command. */
/*GFS*/ static ULONG TWFCD_DoSCSICmd(struct TWFCDData *tcdc_CDData, UBYTE *buffer, int bufferSize, UBYTE *cmd, int cmdSize, UBYTE flags)
{
// Set up the Play IOReq
tcdc_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Command = HD_SCSICMD;
tcdc_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Length = sizeof(struct SCSICmd);
tcdc_CDData -> TWFCD_Cmd.cdc_IOStdReq->io_Data = (APTR)&tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd;
// Setup the direct SCSICmd
tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_Data = (APTR) buffer;
tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_Length = bufferSize;
tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseActual = 0;
tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseData = tcdc_CDData -> TWFCD_Cmd.cdc_SCSISense;
tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_SenseLength = CDSENSE_SIZE;
tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_Command = (UBYTE *) cmd;
tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_CmdLength = cmdSize;
tcdc_CDData -> TWFCD_Cmd.cdc_SCSICmd.scsi_Flags = flags;
return (TWFCD_OK);
}/*GFE*/