1742 lines
45 KiB
C
1742 lines
45 KiB
C
/*
|
|
* player.c
|
|
*
|
|
* Description:
|
|
* Code to perform functions of a CD player using the CD-ROM driver
|
|
*
|
|
* History:
|
|
* cook 5/5/92 added CDatotime, CDframetotc, CDframetomsf
|
|
* msc 10/14/91 Changed names; added digital audio support
|
|
* rogerc 10/29/90 Created
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/scsi.h>
|
|
|
|
#include <stdio.h>
|
|
#include <dslib.h>
|
|
#include <fcntl.h>
|
|
#include <invent.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <mntent.h>
|
|
#include <alloca.h>
|
|
#include "cdaudio.h"
|
|
|
|
/*
|
|
* Buffer lengths
|
|
*/
|
|
|
|
#define CD_GRP0_CMDLEN 6
|
|
#define CD_GRP6_CMDLEN 10
|
|
#define CD_STATUSLEN 11
|
|
#define CD_SENSELEN 18
|
|
#define MSELPARMLEN 12
|
|
|
|
#define STA_RETRY (-1)
|
|
|
|
/*
|
|
* SCSI commands
|
|
*
|
|
* T_ for Toshiba
|
|
* S_ for Sony
|
|
* S2_ for SCSI II
|
|
* SGI_ for SGI specific firmware stuff
|
|
*/
|
|
|
|
#define T_TRSEARCH 0xc0
|
|
#define T_PLAY 0xc1
|
|
#define T_STILL 0xc2
|
|
#define T_EJECT 0xc4
|
|
#define T_SUBCODE 0xc6
|
|
#define T_READDISC 0xc7
|
|
|
|
#define S_SETADDRFMT 0xc0
|
|
#define S_TOC 0xc1
|
|
#define S_PLAY_MSF 0xc7
|
|
|
|
#define S2_SUBCODE 0x42
|
|
#define S2_TOC 0x43
|
|
#define S2_PLAYMSF 0x47
|
|
#define S2_PLAYTRACK 0x48
|
|
#define S2_PAUSE 0x4b
|
|
|
|
#define SGI_CDROM 0xc9
|
|
|
|
#define G0_REZERO 0x01
|
|
|
|
/*
|
|
* While we've got the device, we'll use CDROM_BLKSIZE. When
|
|
* we're done, we'll return it to UNIX_BLKSIZE
|
|
*/
|
|
#define CDROM_BLKSIZE 2048
|
|
#define UNIX_BLKSIZE 512
|
|
|
|
#define CD_PSTATUS_PLAYING 0x00
|
|
#define CD_PSTATUS_STILL 0x01
|
|
#define CD_PSTATUS_PAUSE 0x02
|
|
#define CD_PSTATUS_OTHER 0x03
|
|
|
|
/*
|
|
* Filling command buffers for vendor-specific commands
|
|
*/
|
|
#define fillg6cmd fillg1cmd
|
|
|
|
/*
|
|
* Filling command buffers for SCSI-2 commands
|
|
*/
|
|
#define fills2cmd fillg1cmd
|
|
|
|
/*
|
|
* Filling command buffers for SGI specific commands
|
|
*/
|
|
#define fillsgicmd fillg1cmd
|
|
|
|
/*
|
|
* Convert to and from Binary Coded Decimal
|
|
*/
|
|
#define TWODIGITBCDTOINT( bcd ) (((bcd) >> 4) * 10 + ((bcd) & 0xf))
|
|
#define INTTOTWODIGITBCD( i ) ((((i) / 10) << 4) | ((i) % 10))
|
|
|
|
#ifdef DEBUG
|
|
#define CDDBG(x) {if (cddbg) {x;}}
|
|
#define PRINTSENSE(dsp) { \
|
|
int i;\
|
|
if (STATUS(dsp) == STA_CHECK) {\
|
|
for (i = 0; i < CD_SENSELEN; i++)\
|
|
fprintf( stderr, "%02x ", SENSEBUF(dsp)[i] );\
|
|
fprintf( stderr, "\n" );\
|
|
}}
|
|
static int cddbg = 1;
|
|
#else
|
|
#define CDDBG(x)
|
|
#define PRINTSENSE(dsp)
|
|
#endif
|
|
|
|
enum drivetype { UNKNOWN, TOSHIBA, SONY, TOSHIBA_SCSI2 };
|
|
|
|
typedef struct {
|
|
int min, sec, frame;
|
|
} TRACK;
|
|
|
|
/*
|
|
* private data structure representing a cd player and its state
|
|
*/
|
|
|
|
struct cdplayer {
|
|
unsigned int paused : 1; /* For Sony; drive automatically pauses,
|
|
* so we use this to tell if we should set
|
|
* pause state or not
|
|
*/
|
|
int track; /* Current track being played */
|
|
int index;
|
|
int first; /* Number of first track on CD */
|
|
int last; /* Number of last track on CD */
|
|
int min; /* Minutes of current track */
|
|
int sec; /* Seconds of current track */
|
|
int frame; /* Frame of current track */
|
|
int abs_min; /* Minutes of CD */
|
|
int abs_sec; /* Seconds of CD */
|
|
int abs_frame; /* Frames of CD */
|
|
int total_min; /* Total minutes on CD */
|
|
int total_sec; /* Total seconds on CD */
|
|
int total_frame;/* Total frames on CD */
|
|
int scsi_audio; /* Player can do SCSI audio transfer */
|
|
unsigned long cur_block; /* Logical block no. of current location */ struct dsreq *dsp; /* Struct for doing SCSI requests */
|
|
int excl_id; /* Exclusive use id from mediad */
|
|
int state; /* State of player */
|
|
enum drivetype type; /* Type of the CDROM drive */
|
|
TRACK *toc; /* Table of contents */
|
|
char *dev; /* name of dev scsi device */
|
|
};
|
|
|
|
|
|
/*
|
|
* Convert from the CD six-bit format to ASCII.
|
|
*/
|
|
static unchar sbtoa[] = {
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', 0, 0, 0, 0, 0, 0,
|
|
'0', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
|
'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
|
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
|
'X', 'Y', 'Z', 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
};
|
|
|
|
/*
|
|
* private function declarations
|
|
*/
|
|
|
|
static int read_disc( CDPLAYER *cdplayer );
|
|
static void act_like_a_cdrom( struct dsreq *dsp );
|
|
static int read_toc( CDPLAYER *cd );
|
|
static int set_blksize( CDPLAYER *cd, int blksize );
|
|
static void toshiba_modesense( struct dsreq *dsp, char *params, int size );
|
|
static void scsi2_modesense( struct dsreq *dsp, char *params, int size );
|
|
|
|
/*
|
|
* Modesense is a little different between Sony and Toshiba
|
|
*/
|
|
static void (*modesense[])( struct dsreq *, char *, int ) =
|
|
{ NULL, toshiba_modesense, scsi2_modesense, scsi2_modesense };
|
|
|
|
priv_modeselect15(dsp, data, datalen, save, vu)
|
|
struct dsreq *dsp;
|
|
caddr_t data;
|
|
long datalen;
|
|
char save, vu;
|
|
{
|
|
fillg0cmd(dsp, CMDBUF(dsp), G0_MSEL, save, 0, 0, B1(datalen), B1(vu<<6));
|
|
filldsreq(dsp, data, datalen, DSRQ_WRITE|DSRQ_SENSE);
|
|
return(doscsireq(getfd(dsp), dsp));
|
|
}
|
|
|
|
/*
|
|
* CDPLAYER *CDopen( dev, dir )
|
|
*
|
|
* Description:
|
|
* Intitializes a data structure for a cdplayer, and returns a pointer to
|
|
* it.
|
|
*
|
|
* Parameters:
|
|
* dev Device to use for playing CD's; if NULL, attempts to locate
|
|
* a CD-ROM drive in /dev/scsi
|
|
*
|
|
* dir "r", "w" or "rw" indicating what you will do with the drive.
|
|
* This is for future writable CD devices
|
|
*
|
|
* Returns:
|
|
* Pointer to a CDPLAYER struct if successful, NULL if error
|
|
*/
|
|
|
|
CDPLAYER *CDopen( char const *devscsi, char const *dir )
|
|
{
|
|
struct dsreq *dsp = 0;
|
|
#define INQUIRY_SIZE 98
|
|
unsigned char *inquiry = (unsigned char *)alloca(INQUIRY_SIZE);
|
|
CDPLAYER *cdplayer = 0;
|
|
char devbuf[300];
|
|
inventory_t *inv;
|
|
static char *toshiba = "TOSHIBA CD";
|
|
static char *sony = "SONY CD";
|
|
enum drivetype type;
|
|
#define MSELCMD_SIZE 28
|
|
unsigned char *mselcmd = (unsigned char *)alloca(MSELCMD_SIZE);
|
|
CDTRACKINFO info;
|
|
int excl_id, changed;
|
|
|
|
|
|
if (devscsi) {
|
|
excl_id = 1;
|
|
/* Mediad_Get_Exclusiveuse(Devscsi, "Libcdaudio"); */
|
|
/* fprintf(stderr,"grabbing mediad_exclusiveuse\n"); */
|
|
|
|
if (excl_id >= 0) {
|
|
dsp = dsopen( devscsi, O_RDONLY);
|
|
} else {
|
|
setoserror(EACCES);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
setinvent( );
|
|
for (inv = getinvent( ); inv; inv = getinvent( ))
|
|
if (inv->inv_class == INV_SCSI
|
|
&& inv->inv_type == INV_CDROM)
|
|
break;
|
|
|
|
if (inv) {
|
|
sprintf(devbuf, "/dev/scsi/sc%dd%dl%d",
|
|
inv->inv_controller, inv->inv_unit,
|
|
(inv->inv_state & 0xff00) >> 8 );
|
|
devscsi = devbuf;
|
|
excl_id = 1;
|
|
/* this was messing up mediad */
|
|
/* mediad_get_exclusiveuse(devscsi, "libcdaudio"); */
|
|
/* fprintf(stderr,"grabbing mediad_exclusiveuse\n"); */
|
|
|
|
if (excl_id < 0) {
|
|
setoserror(EACCES);
|
|
endinvent();
|
|
return(NULL);
|
|
}
|
|
dsp = dsopen( devscsi, O_RDONLY );
|
|
} else {
|
|
setoserror(ENODEV); /* Can't locate a CD-ROM drive */
|
|
endinvent( );
|
|
return (NULL);
|
|
}
|
|
endinvent( );
|
|
}
|
|
|
|
/* if dsopen failed, it should have set errno. */
|
|
if (dsp) {
|
|
if (inquiry12( dsp, inquiry, INQUIRY_SIZE, 0 ) == 0) {
|
|
if (strncmp( inquiry + 8, toshiba, strlen( toshiba ) ) == 0)
|
|
|
|
type = ((inquiry[2] & 0x7) == 2) ? TOSHIBA_SCSI2 : TOSHIBA;
|
|
else if (strncmp( inquiry + 8, sony, strlen( sony ) ) == 0)
|
|
type = SONY;
|
|
else {
|
|
/*
|
|
* Default to TOSHIBA_SCSI2, so that
|
|
* users connecting non-SGI CD-ROM
|
|
* drives to our systems have some
|
|
* chance of working. Note that the
|
|
* modeselect command for scsi audio
|
|
* below will probably fail.
|
|
*/
|
|
type = TOSHIBA_SCSI2;
|
|
}
|
|
} else {
|
|
setoserror(ENODEV); /* Device didn't respond to inquiry */
|
|
return (NULL);
|
|
}
|
|
|
|
if (!(SENSEBUF( dsp ) = (char *)malloc( CD_SENSELEN ) )) {
|
|
setoserror(ENOMEM);
|
|
return (NULL);
|
|
}
|
|
SENSELEN( dsp ) = CD_SENSELEN;
|
|
|
|
act_like_a_cdrom( dsp );
|
|
if (!(cdplayer = (CDPLAYER *)malloc( sizeof(CDPLAYER) ))) {
|
|
free( SENSEBUF( dsp ) );
|
|
setoserror(ENOMEM);
|
|
return (NULL);
|
|
}
|
|
memset( cdplayer, 0, sizeof (CDPLAYER) );
|
|
cdplayer->dsp = dsp;
|
|
cdplayer->type = type;
|
|
cdplayer->excl_id = excl_id;
|
|
cdplayer->dev = strdup(devscsi);
|
|
CDtestready( cdplayer, &changed );
|
|
cdplayer->state = STATUS(cdplayer->dsp) == STA_GOOD ? CD_READY :
|
|
STATUS(cdplayer->dsp) == STA_CHECK ? CD_NODISC : CD_ERROR;
|
|
|
|
CDupdatestatus( (CDPLAYER *)cdplayer );
|
|
if (cdplayer->state == CD_READY) CDpreventremoval( cdplayer );
|
|
if (type == SONY) {
|
|
set_blksize( cdplayer, CDROM_BLKSIZE );
|
|
/* Position DA read address at start of disc */
|
|
cdplayer->cur_block = 0;
|
|
} else if (type == TOSHIBA_SCSI2) {
|
|
/*
|
|
* Switch the drive to reading audio data
|
|
*/
|
|
bzero(mselcmd, MSELCMD_SIZE);
|
|
mselcmd[3] = 8;
|
|
mselcmd[4] = 0x82;
|
|
mselcmd[9] = CDDA_BLOCKSIZE >> 16;
|
|
mselcmd[10] = (CDDA_BLOCKSIZE & 0x0000ff00) >> 8;
|
|
mselcmd[11] = CDDA_BLOCKSIZE & 0x000000ff;
|
|
mselcmd[12] = 2; /* modeselect page 2 */
|
|
mselcmd[13] = 0xe; /* length of page 2 */
|
|
mselcmd[14] = 0x6f; /* buffer full ratio - reconnect after
|
|
/* 16 blocks of data */
|
|
priv_modeselect15(cdplayer->dsp, mselcmd, MSELCMD_SIZE, 0x10, 0);
|
|
/* modeselect15(cdplayer->dsp, mselcmd, MSELCMD_SIZE, 0x10, 0); */
|
|
if (STATUS( dsp ) == STA_GOOD) {
|
|
cdplayer->scsi_audio++;
|
|
if (cdplayer->state == CD_READY) {
|
|
/* Position DA read address at start of disc */
|
|
CDgettrackinfo( cdplayer, 1, &info);
|
|
cdplayer->cur_block = CDmsftoblock(cdplayer,
|
|
info.start_min,
|
|
info.start_sec,
|
|
info.start_frame);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (cdplayer);
|
|
}
|
|
|
|
/*
|
|
* Return the CD-ROM's file descriptor for use with select.
|
|
*/
|
|
int CDgetfd( CDPLAYER *cd )
|
|
{
|
|
getfd( cd->dsp );
|
|
}
|
|
|
|
/*
|
|
* Return the ideal number of frames to read to get continuous
|
|
* streaming data. This is related to the buffer full ratio
|
|
* used in CDopen above.
|
|
*/
|
|
int CDbestreadsize( CDPLAYER *cd )
|
|
{
|
|
return 12;
|
|
}
|
|
|
|
/*
|
|
* Convert minutes, seconds and frames of to a CD frame number.
|
|
*/
|
|
unsigned long
|
|
CDmsftoframe(int m, int s, int f)
|
|
{
|
|
return (m*60*75 + s*75 + f);
|
|
}
|
|
|
|
unsigned long
|
|
CDtctoframe(struct cdtimecode* tc)
|
|
{
|
|
return CDmsftoframe(tc->mhi * 10 + tc->mlo,
|
|
tc->shi * 10 + tc->slo,
|
|
tc->fhi * 10 + tc->flo);
|
|
}
|
|
|
|
/*
|
|
* Convert an ASCII string to a struct {cd,mt}timecode which can then be
|
|
* used as an argument to a search ioctl.
|
|
*
|
|
* This returns 0 if the string does not represent a valid timecode,
|
|
* non-zero otherwise.
|
|
*/
|
|
int CDatotime(struct cdtimecode *tp, const char *loc)
|
|
{
|
|
int m, s, f;
|
|
|
|
if (!CDatomsf(loc, &m, &s, &f)) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* put the fields into the timecode struct
|
|
*/
|
|
tp->mhi = m / 10;
|
|
tp->mlo = m % 10;
|
|
tp->shi = s / 10;
|
|
tp->slo = s % 10;
|
|
tp->fhi = f/ 10;
|
|
tp->flo = f % 10;
|
|
return 1; /* valid timecode */
|
|
}
|
|
|
|
int CDatomsf(const char *loc, int *m, int *s, int *f)
|
|
{
|
|
char buf[80];
|
|
char *tok;
|
|
int field[3];
|
|
int fno = 0;
|
|
char *str = buf;
|
|
|
|
/*
|
|
* zero out the fields. Fields which are not specified thus default
|
|
* to zero.
|
|
*/
|
|
bzero(field, 3 * sizeof(int));
|
|
/*
|
|
* copy to a temporary buffer; strtok is destructive
|
|
* our token separators are arbitrary here; likely most people will
|
|
* use the ':'
|
|
*/
|
|
strncpy(buf, loc, sizeof(buf));
|
|
buf[sizeof(buf) - 1] = '\0';
|
|
while ((tok = strtok(str,":-/#|,.;*")) && fno < 3) {
|
|
int i;
|
|
str = 0;
|
|
field[fno++] = atoi(tok);
|
|
}
|
|
|
|
/*
|
|
* check fields for validity
|
|
*/
|
|
if (field[0] < 0 || field[0] > 119 || /* check minutes */
|
|
field[1] < 0 || field[1] > 59 || /* check seconds */
|
|
field[2] < 0 || field[2] > 74) { /* check frames */
|
|
return 0;
|
|
}
|
|
/*
|
|
* put the fields into msf
|
|
*/
|
|
*m = field[0];
|
|
*s = field[1];
|
|
*f = field[2];
|
|
return 1; /* valid timecode */
|
|
}
|
|
|
|
|
|
|
|
void
|
|
CDframetotc(unsigned long fr, struct cdtimecode* tc)
|
|
{
|
|
int m,s,f;
|
|
CDframetomsf(fr,&m,&s,&f);
|
|
/*
|
|
* now set the msf fields in the timecode. These are in BCD;
|
|
*/
|
|
tc->mhi = m / 10;
|
|
tc->mlo = m % 10;
|
|
tc->shi = s / 10;
|
|
tc->slo = s % 10;
|
|
tc->fhi = f / 10;
|
|
tc->flo = f % 10;
|
|
}
|
|
|
|
void
|
|
CDframetomsf(unsigned long fr, int *m, int *s, int *f)
|
|
{
|
|
unsigned long mrem;
|
|
*m = fr / (60*75);
|
|
mrem = fr % (60*75);
|
|
*s = mrem/75;
|
|
*f = mrem % 75;
|
|
}
|
|
|
|
/*
|
|
* Convert minutes, seconds and frames of absolute time code to
|
|
* a logical block number on the device.
|
|
*/
|
|
unsigned long
|
|
CDmsftoblock(CDPLAYER *cd, int m, int s, int f)
|
|
{
|
|
if (cd->type == TOSHIBA_SCSI2)
|
|
return (m*60*75 + s*75 + f - 150);
|
|
else
|
|
return (m*60*75 + s*75 + f);
|
|
}
|
|
|
|
void
|
|
CDblocktomsf(CDPLAYER *cd, unsigned long fr, int *m, int *s, int *f)
|
|
{
|
|
unsigned long mrem;
|
|
if (cd->type == TOSHIBA_SCSI2)
|
|
fr += 150;
|
|
*m = fr / (60*75);
|
|
mrem = fr % (60*75);
|
|
*s = mrem/75;
|
|
*f = mrem % 75;
|
|
}
|
|
|
|
/*
|
|
* Seek to a given sub code frame
|
|
*/
|
|
unsigned long CDseek( CDPLAYER *cd, int m, int s, int f)
|
|
{
|
|
CDTRACKINFO info;
|
|
unsigned long newblock;
|
|
|
|
if (!cd->scsi_audio) {
|
|
setoserror(ENXIO);
|
|
return (-1);
|
|
}
|
|
newblock = CDmsftoblock(cd, m, s, f);
|
|
CDgettrackinfo(cd, cd->first, &info);
|
|
if (CDmsftoblock(cd, info.start_min, info.start_sec, info.start_frame)
|
|
<= newblock) {
|
|
CDgettrackinfo(cd, cd->last, &info);
|
|
if (CDmsftoblock(cd,
|
|
info.start_min + info.total_min,
|
|
info.start_sec + info.total_sec,
|
|
info.start_frame + info.total_frame)
|
|
>= newblock) {
|
|
cd->cur_block = newblock;
|
|
return newblock;
|
|
}
|
|
}
|
|
setoserror(EINVAL);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Seek to a given block
|
|
*/
|
|
unsigned long CDseekblock( CDPLAYER *cd, unsigned long block)
|
|
{
|
|
CDTRACKINFO info;
|
|
|
|
if (!cd->scsi_audio) {
|
|
setoserror(ENXIO);
|
|
return (-1);
|
|
}
|
|
CDgettrackinfo(cd, cd->first, &info);
|
|
if (CDmsftoblock(cd, info.start_min, info.start_sec, info.start_frame)
|
|
<= block) {
|
|
CDgettrackinfo(cd, cd->last, &info);
|
|
if (CDmsftoblock(cd,
|
|
info.start_min + info.total_min,
|
|
info.start_sec + info.total_sec,
|
|
info.start_frame + info.total_frame)
|
|
>= block) {
|
|
cd->cur_block = block;
|
|
return block;
|
|
}
|
|
}
|
|
setoserror(EINVAL);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Seek to the start of the given program number (track) on the CD.
|
|
*/
|
|
int CDseektrack( CDPLAYER *cd, int t)
|
|
{
|
|
CDTRACKINFO info;
|
|
|
|
if (!cd->scsi_audio) {
|
|
setoserror(ENXIO);
|
|
return (-1);
|
|
}
|
|
if (CDgettrackinfo( cd, t, &info )) {
|
|
cd->cur_block = CDmsftoblock(cd,
|
|
info.start_min,
|
|
info.start_sec,
|
|
info.start_frame);
|
|
return (cd->cur_block);
|
|
}
|
|
setoserror(EIO);
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Read num_frames subcode frames of digital audio data starting at current
|
|
* position. Update position after the read.
|
|
*/
|
|
int CDreadda( CDPLAYER *cd, CDFRAME *buf, int num_frames )
|
|
{
|
|
int changed;
|
|
unsigned char sense[CD_SENSELEN], *cmd;
|
|
|
|
if (!cd->scsi_audio) {
|
|
setoserror(ENXIO);
|
|
return (-1);
|
|
}
|
|
if (!cd->dsp) {
|
|
setoserror(EBADF);
|
|
return (-1);
|
|
}
|
|
|
|
CDtestready(cd, &changed );
|
|
|
|
if (STATUS(cd->dsp) != STA_GOOD) {
|
|
setoserror(EAGAIN);
|
|
return (-1);
|
|
}
|
|
|
|
if (cd->state != CD_READY) {
|
|
setoserror(EIO);
|
|
return (-1);
|
|
}
|
|
|
|
cmd = CMDBUF( cd->dsp );
|
|
CMDLEN( cd->dsp ) = 12; /* 12 byte vendor specific command */
|
|
filldsreq( cd->dsp, (char *)buf,
|
|
num_frames * (cd->type == SONY ? CDDA_DATASIZE : CDDA_BLOCKSIZE),
|
|
DSRQ_READ | DSRQ_SENSE );
|
|
cmd[0] = cd->type == SONY ? 0xd8 : 0xa8;
|
|
cmd[1] = 0;
|
|
cmd[2] = cd->cur_block >> 24;
|
|
cmd[3] = (cd->cur_block & 0x00ff0000) >> 16;
|
|
cmd[4] = (cd->cur_block & 0x0000ff00) >> 8;
|
|
cmd[5] = cd->cur_block & 0xff;
|
|
if (cd->type == SONY) {
|
|
cmd[6] = 0;
|
|
cmd[7] = num_frames >> 24;
|
|
cmd[8] = (num_frames & 0x00ff0000) >> 16;
|
|
cmd[9] = (num_frames & 0x0000ff00) >> 8;
|
|
cmd[10] = num_frames & 0xff;
|
|
cmd[11] = 0;
|
|
/* Sony drive currently doesn't deliver subcodes */
|
|
bzero(&buf->subcode, sizeof(buf->subcode));
|
|
} else {
|
|
cmd[6] = num_frames >> 24;
|
|
cmd[7] = (num_frames & 0x00ff0000) >> 16;
|
|
cmd[8] = (num_frames & 0x0000ff00) >> 8;
|
|
cmd[9] = num_frames & 0xff;
|
|
cmd[10] = 0;
|
|
cmd[11] = 0;
|
|
}
|
|
doscsireq( getfd( cd->dsp ), cd->dsp );
|
|
if (STATUS(cd->dsp) != STA_GOOD)
|
|
return (0); /* EOF */
|
|
cd->cur_block += num_frames;
|
|
return (DATASENT(cd->dsp)
|
|
/ (cd->type == SONY ? CDDA_DATASIZE : CDDA_BLOCKSIZE));
|
|
}
|
|
|
|
/*
|
|
* int CDplaytrack( CDPLAYER *cd, int track )
|
|
*
|
|
* Description:
|
|
* Play one track of the CD. Play stops at end of track
|
|
*
|
|
* Parameters:
|
|
* cd pointer to CDPLAYER struct
|
|
* track the track number of the track to play
|
|
*
|
|
* Returns:
|
|
* non-zero if successful, 0 otherwise
|
|
*/
|
|
|
|
int CDplaytrack( CDPLAYER *cdplayer, int track, int play )
|
|
{
|
|
register struct dsreq *dsp = cdplayer->dsp;
|
|
int stop, bcd_track, changed;
|
|
CDTRACKINFO info, first_info;
|
|
|
|
CDtestready( cdplayer, &changed);
|
|
|
|
if (STATUS(cdplayer->dsp) != STA_GOOD)
|
|
return (0);
|
|
|
|
if (cdplayer->first > track || cdplayer->last < track)
|
|
return (0);
|
|
|
|
if (cdplayer->type == TOSHIBA) {
|
|
bcd_track = INTTOTWODIGITBCD( track );
|
|
fillg6cmd( dsp, CMDBUF(dsp), T_TRSEARCH, 0, bcd_track,
|
|
0, 0, 0, 0, 0, 0, 0x80 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
|
|
if (STATUS(dsp) != STA_GOOD) {
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cdplayer->state = CD_ERROR;
|
|
return (0);
|
|
}
|
|
|
|
stop = track < cdplayer->last ? INTTOTWODIGITBCD( track + 1 ) : 0;
|
|
fillg6cmd( dsp, CMDBUF(dsp), T_PLAY, 4, stop, 0, 0, 0, 0, 0, 0, 0x80 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
}
|
|
else {
|
|
fills2cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), S2_PLAYTRACK, 0, 0, 0,
|
|
track, 1, 0, track, 1, 0 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
}
|
|
|
|
if (STATUS(dsp) != STA_GOOD) {
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cdplayer->state = CD_ERROR;
|
|
return (0);
|
|
}
|
|
cdplayer->state = CD_PLAYING;
|
|
|
|
if (!play)
|
|
CDtogglepause( cdplayer );
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* int
|
|
* CDplaytrackabs( CDPLAYER *cdplayer, int track, int min, int sec,
|
|
* int frame, int play )
|
|
*
|
|
* Description:
|
|
* Play one track, starting at min:sec:frame within that track
|
|
*
|
|
* Parameters:
|
|
* cdplayer
|
|
* track
|
|
* min
|
|
* sec
|
|
* frame
|
|
* play if non-zero, play; if 0, pause here.
|
|
*
|
|
* Returns:
|
|
* non-zero if successful, 0 if error
|
|
*/
|
|
|
|
int
|
|
CDplaytrackabs( CDPLAYER *cdplayer, int track, int min, int sec,
|
|
int frame, int play )
|
|
{
|
|
register struct dsreq *dsp = cdplayer->dsp;
|
|
CDTRACKINFO info;
|
|
int block, stop, stop_min, stop_sec, stop_frame, changed;
|
|
|
|
CDtestready( cdplayer, &changed );
|
|
|
|
if (STATUS(cdplayer->dsp) != STA_GOOD)
|
|
return (0);
|
|
|
|
CDgettrackinfo( cdplayer, track, &info );
|
|
frame = info.start_frame + frame + 75 *
|
|
(info.start_sec + sec + 60 * (info.start_min + min));
|
|
sec = frame / 75;
|
|
min = sec / 60;
|
|
sec %= 60;
|
|
frame %= 75;
|
|
|
|
if (cdplayer->type == TOSHIBA) {
|
|
min = INTTOTWODIGITBCD( min );
|
|
sec = INTTOTWODIGITBCD( sec );
|
|
frame = INTTOTWODIGITBCD( frame );
|
|
|
|
fillg6cmd( dsp, CMDBUF(dsp), T_TRSEARCH, 0, min, sec,
|
|
frame, 0, 0, 0, 0, 0x40 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
|
|
stop = track < cdplayer->last ? INTTOTWODIGITBCD( track + 1 ) : 0;
|
|
fillg6cmd( dsp, CMDBUF(dsp), T_PLAY, 4, stop, 0, 0, 0, 0, 0, 0, 0x80 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
|
|
if (!play)
|
|
CDtogglepause( cdplayer );
|
|
|
|
}
|
|
else {
|
|
stop_frame = info.start_frame + info.total_frame + 75 *
|
|
(info.start_sec + info.total_sec + 60 *
|
|
(info.start_min + info.total_min));
|
|
stop_sec = stop_frame / 75;
|
|
stop_min = stop_sec / 60;
|
|
stop_sec %= 60;
|
|
stop_frame %= 75;
|
|
|
|
/*
|
|
* Goto pause state if we're playing
|
|
*/
|
|
if (cdplayer->state == CD_PLAYING) {
|
|
fills2cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), S2_PAUSE, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
}
|
|
|
|
fills2cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), S2_PLAYMSF, 0,
|
|
0, min, sec, frame, stop_min, stop_sec, stop_frame, 0 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
|
|
PRINTSENSE( cdplayer-> dsp );
|
|
|
|
/*
|
|
* Stop playing if play == 0
|
|
*/
|
|
if (STATUS( cdplayer->dsp ) == STA_GOOD && !play) {
|
|
fills2cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp),
|
|
S2_PAUSE, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
}
|
|
}
|
|
|
|
if (STATUS(dsp) != STA_GOOD) {
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cdplayer->state = CD_ERROR;
|
|
return (0);
|
|
}
|
|
|
|
cdplayer->state = play ? CD_PLAYING : CD_PAUSED;
|
|
cdplayer->paused = cdplayer->state == CD_PAUSED;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* int CDplayabs( CDPLAYER *cd, int min, int sec, int frame, int play )
|
|
*
|
|
* Description:
|
|
* Start playing at an absolute time on the CD
|
|
*
|
|
* Parameters:
|
|
* cd Pointer to a CDPLAYER struct
|
|
* min absolute time to start playing
|
|
* sec absolute seconds to start playing
|
|
* frame absolute frames to start playing
|
|
* play start playing if 1, pause if 0
|
|
*
|
|
* Returns:
|
|
* 1 if successful, 0 if error
|
|
*/
|
|
|
|
int CDplayabs( CDPLAYER *cdplayer, int min, int sec, int frame, int play )
|
|
{
|
|
register struct dsreq *dsp = cdplayer->dsp;
|
|
CDTRACKINFO info;
|
|
int block, changed;
|
|
|
|
CDtestready( cdplayer, &changed );
|
|
|
|
if (STATUS(cdplayer->dsp) != STA_GOOD)
|
|
return (0);
|
|
|
|
if (cdplayer->type == TOSHIBA || cdplayer->type == TOSHIBA_SCSI2) {
|
|
min = INTTOTWODIGITBCD( min );
|
|
sec = INTTOTWODIGITBCD( sec );
|
|
frame = INTTOTWODIGITBCD( frame );
|
|
|
|
fillg6cmd( dsp, CMDBUF(dsp), T_TRSEARCH, play ? 1 : 0, min, sec,
|
|
frame, 0, 0, 0, 0, 0x40 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
}
|
|
else {
|
|
/*
|
|
* Goto pause state if we're playing
|
|
*/
|
|
if (cdplayer->state == CD_PLAYING) {
|
|
fills2cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), S2_PAUSE, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
}
|
|
|
|
/*
|
|
* Seek to the right place
|
|
*/
|
|
block = frame +
|
|
(75 * (sec + 60 * min));
|
|
|
|
/*
|
|
* Subtract the address of the first track. I'm not sure
|
|
* why this is necessary, but it is.
|
|
*/
|
|
if (CDgettrackinfo( cdplayer, cdplayer->first, &info ))
|
|
block -= info.start_frame +
|
|
(75 * (info.start_sec + 60 * info.start_min));
|
|
fillg1cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), G1_SEEK, 0, B4( block ),
|
|
0, 0, 0, 0 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
|
|
/*
|
|
* Start playing if play != 0
|
|
*/
|
|
if (STATUS( cdplayer->dsp ) == STA_GOOD && play) {
|
|
fills2cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), S2_PAUSE, 0, 0, 0, 0, 0,
|
|
0, 0, 1, 0 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
}
|
|
}
|
|
|
|
if (STATUS(dsp) != STA_GOOD) {
|
|
PRINTSENSE(cdplayer->dsp);
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cdplayer->state = CD_ERROR;
|
|
return (0);
|
|
}
|
|
|
|
cdplayer->state = play ? CD_PLAYING : CD_PAUSED;
|
|
cdplayer->paused = cdplayer->state == CD_PAUSED;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* int CDstop( CDPLAYER *cd )
|
|
*
|
|
* Description:
|
|
* Stops play of the CD
|
|
*
|
|
* Parameters:
|
|
* cd pointer to a CDPLAYER struct
|
|
*
|
|
* Returns:
|
|
* non-zero if successful, 0 otherwise
|
|
*/
|
|
|
|
int CDstop( CDPLAYER *cdplayer )
|
|
{
|
|
register struct dsreq *dsp = cdplayer->dsp;
|
|
|
|
cdplayer->paused = 0;
|
|
if (cdplayer->type == TOSHIBA) {
|
|
fillg0cmd( dsp, CMDBUF(dsp), G0_REZERO, 0, 0, 0, 0, 0 );
|
|
filldsreq( dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
}
|
|
else {
|
|
fillg0cmd( dsp, CMDBUF(dsp), G0_STOP, 0, 0, 0, 0, 0 );
|
|
filldsreq( dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
|
|
fillg0cmd( dsp, CMDBUF(dsp), G0_SEEK, 0, B2( 0 ), 0, 0 );
|
|
filldsreq( dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
}
|
|
|
|
if (STATUS(dsp) != STA_GOOD) {
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cdplayer->state = CD_ERROR;
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* int CDtogglepause( CDPLAYER *cd )
|
|
*
|
|
* Description:
|
|
* if CD is playing, pause it. If CD is paused, resume play
|
|
*
|
|
* Parameters:
|
|
* cd pointer to a CDPLAYER struct
|
|
*
|
|
* Returns:
|
|
* non-zero if successful, 0 if not
|
|
*/
|
|
|
|
int CDtogglepause( CDPLAYER *cdplayer )
|
|
{
|
|
int changed;
|
|
register struct dsreq *dsp = cdplayer->dsp;
|
|
|
|
CDtestready( cdplayer, &changed );
|
|
|
|
if (STATUS(cdplayer->dsp) != STA_GOOD)
|
|
return (0);
|
|
|
|
if (cdplayer->state == CD_ERROR)
|
|
return (0);
|
|
|
|
if (cdplayer->type == TOSHIBA)
|
|
fillg6cmd( dsp, CMDBUF(dsp), cdplayer->state == CD_PLAYING ?
|
|
T_STILL : T_PLAY, cdplayer->state == CD_PLAYING ? 0 : 4,
|
|
0, 0, 0, 0, 0, 0, 0, 0xc0 );
|
|
else
|
|
fills2cmd( dsp, CMDBUF(dsp), S2_PAUSE, 0, 0, 0, 0, 0, 0, 0,
|
|
cdplayer->state == CD_PLAYING ? 0 : 1, 0 );
|
|
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
|
|
if (STATUS(dsp) == STA_GOOD) {
|
|
cdplayer->state = cdplayer->state == CD_PLAYING ?
|
|
CD_PAUSED : CD_PLAYING;
|
|
cdplayer->paused = cdplayer->state == CD_PAUSED;
|
|
}
|
|
else {
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cdplayer->state = CD_ERROR;
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* int CDeject( CDPLAYER *cd )
|
|
*
|
|
* Description:
|
|
* Ejects the CD caddy from the CD-ROM drive
|
|
*
|
|
* Parameters:
|
|
* cd pointer to a CDPLAYER struct
|
|
*
|
|
* Returns:
|
|
* non-zero if successful, 0 otherwise
|
|
*/
|
|
|
|
int CDeject( CDPLAYER *cdplayer )
|
|
{
|
|
int open = 0;
|
|
int status, changed;
|
|
|
|
CDtestready( cdplayer, &changed );
|
|
|
|
if (STATUS(cdplayer->dsp) != STA_GOOD) {
|
|
setoserror(EIO);
|
|
return (0);
|
|
}
|
|
|
|
if (cdplayer->type == TOSHIBA) {
|
|
fillg6cmd(cdplayer->dsp, CMDBUF(cdplayer->dsp),
|
|
T_EJECT, 1, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
filldsreq(cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE);
|
|
doscsireq(getfd( cdplayer->dsp ), cdplayer->dsp);
|
|
status = STATUS(cdplayer->dsp) == STA_GOOD;
|
|
}
|
|
else {
|
|
CDallowremoval( cdplayer );
|
|
fillg0cmd(cdplayer->dsp, CMDBUF(cdplayer->dsp),
|
|
G0_LOAD, 1, 0, 0, 2, 0);
|
|
filldsreq(cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE);
|
|
doscsireq(getfd(cdplayer->dsp), cdplayer->dsp);
|
|
status = STATUS(cdplayer->dsp) == STA_GOOD;
|
|
CDpreventremoval(cdplayer);
|
|
}
|
|
|
|
cdplayer->paused = 0;
|
|
|
|
if (!status) {
|
|
setoserror(EIO);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* int CDclose( CDPLAYER *cd )
|
|
*
|
|
* Description:
|
|
* Closes the dsp's file descriptor, and frees previously allocated
|
|
* memory
|
|
*
|
|
* Parameters:
|
|
* cd Pointer to a CDPLAYER struct
|
|
*
|
|
* Returns:
|
|
* non-zero if successful, 0 otherwise
|
|
*/
|
|
|
|
int CDclose( CDPLAYER *cdplayer )
|
|
{
|
|
|
|
if (cdplayer->dsp) {
|
|
CDallowremoval( cdplayer );
|
|
if (cdplayer->type == SONY || cdplayer->type == TOSHIBA_SCSI2)
|
|
set_blksize( cdplayer, UNIX_BLKSIZE );
|
|
free( SENSEBUF( cdplayer->dsp ) );
|
|
dsclose( cdplayer->dsp );
|
|
}
|
|
/* if (cdplayer->excl_id) { */
|
|
/* fprintf(stderr,"releasing mediad_exclusiveuse\n"); */
|
|
/* mediad_release_exclusiveuse(cdplayer->excl_id); */
|
|
/* } */
|
|
free(cdplayer->dev);
|
|
free(cdplayer);
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* static int CDupdatestatus( CDPLAYER *cd )
|
|
*
|
|
* Description:
|
|
* Check the status of the CD-ROM drive, updating our data structures
|
|
*
|
|
* Parameters:
|
|
* cd pointer to a CDPLAYER structure
|
|
*
|
|
* Returns:
|
|
* non-zero if successful, 0 if error
|
|
*/
|
|
|
|
int CDupdatestatus( CDPLAYER *cdplayer )
|
|
{
|
|
#define TRACK_INFO_SIZE 4
|
|
int min;
|
|
int sec;
|
|
int changed;
|
|
unsigned char *track_info = (unsigned char *)alloca(TRACK_INFO_SIZE);
|
|
|
|
CDtestready( cdplayer, &changed );
|
|
if (STATUS(cdplayer->dsp) != STA_GOOD) {
|
|
cdplayer->state = CD_NODISC;
|
|
return (1);
|
|
}
|
|
|
|
if (!read_disc( cdplayer )) {
|
|
if (cdplayer->state == CD_CDROM)
|
|
return (1);
|
|
else {
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cdplayer->state = CD_ERROR;
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
if (cdplayer->type == TOSHIBA) {
|
|
unsigned char *status = (unsigned char *)alloca(CD_STATUSLEN);
|
|
|
|
fillg6cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), T_SUBCODE, CD_STATUSLEN,
|
|
0, 0, 0, 0, 0, 0, 0, 0 );
|
|
filldsreq( cdplayer->dsp, status, CD_STATUSLEN,
|
|
DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
|
|
if (STATUS(cdplayer->dsp) == STA_GOOD) {
|
|
switch (status[0]) {
|
|
|
|
case CD_PSTATUS_PLAYING:
|
|
cdplayer->state = CD_PLAYING;
|
|
break;
|
|
|
|
case CD_PSTATUS_STILL:
|
|
cdplayer->state = CD_STILL;
|
|
break;
|
|
|
|
case CD_PSTATUS_PAUSE:
|
|
cdplayer->state = CD_PAUSED;
|
|
break;
|
|
|
|
case CD_PSTATUS_OTHER:
|
|
cdplayer->state = CD_READY; /* just a guess for now */
|
|
break;
|
|
}
|
|
|
|
cdplayer->track = TWODIGITBCDTOINT( status[2] );
|
|
cdplayer->min = TWODIGITBCDTOINT( status[4] );
|
|
cdplayer->sec = TWODIGITBCDTOINT( status[5] );
|
|
cdplayer->frame = TWODIGITBCDTOINT( status[6] );
|
|
cdplayer->abs_min = TWODIGITBCDTOINT( status[7] );
|
|
cdplayer->abs_sec = TWODIGITBCDTOINT( status[8] );
|
|
cdplayer->abs_frame = TWODIGITBCDTOINT( status[9] );
|
|
}
|
|
fillg6cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), T_READDISC, 1, 0, 0, 0,
|
|
0, 0, 0, 0, 0 );
|
|
filldsreq( cdplayer->dsp, track_info, TRACK_INFO_SIZE,
|
|
DSRQ_SENSE | DSRQ_READ );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
|
|
if (STATUS(cdplayer->dsp) == STA_GOOD) {
|
|
cdplayer->total_min = TWODIGITBCDTOINT( track_info[0] );
|
|
cdplayer->total_sec = TWODIGITBCDTOINT( track_info[1] );
|
|
cdplayer->total_frame = TWODIGITBCDTOINT( track_info[2] );
|
|
}
|
|
}
|
|
else { /* SCSI-2 && SONY */
|
|
#define TOC_INFO_SIZE 12
|
|
unsigned char *toc_info = (unsigned char *)alloca(TOC_INFO_SIZE);
|
|
#define SUB_CHANNEL_SIZE 16
|
|
unsigned char *sub_channel = (unsigned char *)alloca(SUB_CHANNEL_SIZE);
|
|
int lead_out;
|
|
|
|
fills2cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), S2_SUBCODE, 2, 0x40, 1, 0, 0, 0,
|
|
B2( SUB_CHANNEL_SIZE ), 0 );
|
|
filldsreq( cdplayer->dsp, sub_channel, SUB_CHANNEL_SIZE,
|
|
DSRQ_SENSE | DSRQ_READ );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
|
|
if (STATUS( cdplayer->dsp ) != STA_GOOD)
|
|
return (0);
|
|
|
|
switch (sub_channel[1]) {
|
|
|
|
case 0x11:
|
|
cdplayer->state = CD_PLAYING;
|
|
break;
|
|
|
|
case 0x12:
|
|
/*
|
|
* The Sony drive likes to always report that it's in
|
|
* pause mode; therefore, it is necessary to make a
|
|
* distinction between "the drive is in paused mode"
|
|
* and "the user pressed the pause button". The
|
|
* paused field gets turned on in situations where
|
|
* the user pressed the pause button.
|
|
*/
|
|
cdplayer->state = cdplayer->paused ? CD_PAUSED : CD_READY;
|
|
break;
|
|
|
|
case 0:
|
|
case 0x13:
|
|
case 0x15:
|
|
cdplayer->state = CD_READY;
|
|
break;
|
|
|
|
case 0x14:
|
|
default:
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cdplayer->state = CD_ERROR;
|
|
break;
|
|
}
|
|
|
|
cdplayer->track = sub_channel[6];
|
|
cdplayer->min = sub_channel[13];
|
|
cdplayer->sec = sub_channel[14];
|
|
cdplayer->frame = sub_channel[15];
|
|
cdplayer->abs_min = sub_channel[9];
|
|
cdplayer->abs_sec = sub_channel[10];
|
|
cdplayer->abs_frame = sub_channel[11];
|
|
|
|
fills2cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), S2_TOC, 2, 0, 0, 0, 0, 0xaa,
|
|
B2( TOC_INFO_SIZE ), 0 );
|
|
filldsreq( cdplayer->dsp, toc_info, TOC_INFO_SIZE,
|
|
DSRQ_SENSE | DSRQ_READ );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
if (STATUS( cdplayer->dsp ) != STA_GOOD)
|
|
return (0);
|
|
if (toc_info[5] & 0x04) {
|
|
cdplayer->state = CD_CDROM;
|
|
} else {
|
|
cdplayer->total_min = toc_info[9];
|
|
cdplayer->total_sec = toc_info[10];
|
|
cdplayer->total_frame = toc_info[11];
|
|
}
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* int CDgetstatus( CDPLAYER *cd, CDSTATUS *status )
|
|
*
|
|
* Description:
|
|
* Copy some of the fields from the CDPLAYER structure into the
|
|
* CDSTATUS structure supplied by the caller. This allows the caller
|
|
* see many of the values of CDPLAYER without being able to access
|
|
* them directly
|
|
*
|
|
* Parameters:
|
|
* cd Pointer to a CDPLAYER struct
|
|
* status Pointer to a CDSTATUS struct, to be filled in so caller
|
|
* can see the values
|
|
*
|
|
* Returns:
|
|
* non-zero if successful, 0 otherwise
|
|
*/
|
|
|
|
int CDgetstatus( CDPLAYER *cdplayer, CDSTATUS *status )
|
|
{
|
|
CDTRACKINFO info;
|
|
|
|
CDupdatestatus( cdplayer );
|
|
|
|
status->state = cdplayer->state;
|
|
status->scsi_audio = cdplayer->scsi_audio;
|
|
/*
|
|
* This is pretty gross, but seems to be required by the behavior
|
|
* of the Sony drive.
|
|
*
|
|
* When you tell the drive to play the beginning of a track and
|
|
* then immediately pause, it backs off a little bit, so that
|
|
* when you read subcodes you get data from the last little bit
|
|
* of the previous track. This looks pretty ugly when we're trying
|
|
* to give the user feedback about tracks and time, so if we're
|
|
* paused at the very end of a track, we report that we're actually
|
|
* at the beginning of the next track.
|
|
*/
|
|
if (cdplayer->type == SONY && cdplayer->track < cdplayer->last &&
|
|
CDgettrackinfo( cdplayer, cdplayer->track, &info ) &&
|
|
cdplayer->min == info.total_min && cdplayer->sec >= info.total_sec - 1) {
|
|
status->track = cdplayer->track + 1;
|
|
status->min = status->sec = status->frame = 0;
|
|
}
|
|
else {
|
|
status->track = cdplayer->track;
|
|
status->min = cdplayer->min;
|
|
status->sec = cdplayer->sec;
|
|
status->frame = cdplayer->frame;
|
|
}
|
|
status->abs_min = cdplayer->abs_min;
|
|
status->abs_sec = cdplayer->abs_sec;
|
|
status->abs_frame = cdplayer->abs_frame;
|
|
status->total_min = cdplayer->total_min;
|
|
status->total_sec = cdplayer->total_sec;
|
|
status->total_frame = cdplayer->total_frame;
|
|
status->first = cdplayer->first;
|
|
status->last = cdplayer->last;
|
|
status->cur_block = cdplayer->cur_block;
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* int
|
|
* CDgettrackinfo( CDPLAYER *cd, int track, CDTRACKINFO *info )
|
|
*
|
|
* Description:
|
|
* Get relevant information about track on cd
|
|
*
|
|
* Parameters:
|
|
* cd The CD to get info about
|
|
* track Number of track to get info about
|
|
* info Pointer to buffer to receive the information
|
|
*
|
|
* Returns:
|
|
* 1 if successful, 0 otherwise
|
|
*/
|
|
|
|
int
|
|
CDgettrackinfo( CDPLAYER *cd, int track, CDTRACKINFO *info )
|
|
{
|
|
register struct dsreq *dsp = cd->dsp;
|
|
int frames, this_track, next_track, changed;
|
|
TRACK *trackp;
|
|
|
|
CDtestready( cd, &changed );
|
|
|
|
if (STATUS(cd->dsp) != STA_GOOD)
|
|
return (0);
|
|
|
|
if (!cd->toc && !read_toc( cd ))
|
|
return (0);
|
|
|
|
if (track < cd->first || track > cd->last)
|
|
return (0);
|
|
|
|
trackp = &cd->toc[track - cd->first];
|
|
info->start_min = trackp->min;
|
|
info->start_sec = trackp->sec;
|
|
info->start_frame = trackp->frame;
|
|
|
|
trackp++;
|
|
frames = ((trackp->min * 60) + trackp->sec) * 75 + trackp->frame;
|
|
frames -= ((info->start_min * 60) + info->start_sec) * 75 +
|
|
info->start_frame;
|
|
|
|
info->total_frame = frames % 75;
|
|
frames /= 75;
|
|
info->total_sec = frames % 60;
|
|
info->total_min = frames / 60;
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* int CDsetvolume( CDPLAYER *cd, CDVOLUME *vol )
|
|
*
|
|
* Description:
|
|
* Set the volume of the output
|
|
*
|
|
* Parameters:
|
|
* cd pointer to a CDPLAYER struct
|
|
* vol volume structure
|
|
*
|
|
* Returns:
|
|
* non-zero iff successful
|
|
*/
|
|
|
|
int CDsetvolume( CDPLAYER *cd, CDVOLUME *vol )
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* int CDgetvolume( CDPLAYER *cd, CDVOLUME *vol )
|
|
*
|
|
* Description:
|
|
* Get the volume of the output
|
|
*
|
|
* Parameters:
|
|
* cd pointer to a CDPLAYER struct
|
|
* vol volume structure
|
|
*
|
|
* Returns:
|
|
* non-zero iff successful
|
|
*/
|
|
|
|
int CDgetvolume( CDPLAYER *cd, CDVOLUME *vol )
|
|
{
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* static int read_disc( CDPLAYER *cdplayer )
|
|
*
|
|
* Description:
|
|
* Read parameters from the CD-ROM drive
|
|
*
|
|
* Parameters:
|
|
* cdplayer pointer to a CDPLAYER struct
|
|
*
|
|
* Returns:
|
|
* non-zero if successful, 0 otherwise
|
|
*/
|
|
|
|
static int read_disc( CDPLAYER *cdplayer )
|
|
{
|
|
#define TRACK_INFO_SIZE 4
|
|
unsigned char *trackInfo = (unsigned char *)alloca(TRACK_INFO_SIZE);
|
|
#define S2_TRACK_INFO_SIZE 12
|
|
unsigned char *s2trackInfo =
|
|
(unsigned char *)alloca(S2_TRACK_INFO_SIZE);
|
|
int i;
|
|
struct dsreq *dsp = cdplayer->dsp;
|
|
|
|
if (cdplayer->type == TOSHIBA) {
|
|
fillg6cmd( dsp, CMDBUF(dsp), T_READDISC, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
|
|
filldsreq( dsp, trackInfo, TRACK_INFO_SIZE,
|
|
DSRQ_SENSE | DSRQ_READ );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
if (STATUS(dsp) != STA_GOOD)
|
|
return (0);
|
|
if (trackInfo[3] & 0x04) {
|
|
cdplayer->state = CD_CDROM;
|
|
return 0;
|
|
} else {
|
|
cdplayer->first = TWODIGITBCDTOINT( trackInfo[0] );
|
|
cdplayer->last = TWODIGITBCDTOINT( trackInfo[1] );
|
|
}
|
|
}
|
|
else {
|
|
fillg6cmd( dsp, CMDBUF(dsp), S2_TOC, 0, 0, 0, 0, 0, 1,
|
|
B2( S2_TRACK_INFO_SIZE ), 0 );
|
|
filldsreq( dsp, s2trackInfo, S2_TRACK_INFO_SIZE,
|
|
DSRQ_SENSE | DSRQ_READ );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
if (STATUS(dsp) != STA_GOOD)
|
|
return (0);
|
|
if (s2trackInfo[5] & 0x04) {
|
|
cdplayer->state = CD_CDROM;
|
|
return 0;
|
|
} else {
|
|
cdplayer->first = s2trackInfo[2];
|
|
cdplayer->last = s2trackInfo[3];
|
|
}
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
void CDtestready( CDPLAYER *cd, int* changed )
|
|
{
|
|
int keep_trying;
|
|
register dsreq_t *dsp = cd->dsp;
|
|
|
|
*changed = 0;
|
|
|
|
fillg0cmd( dsp, CMDBUF(dsp), G0_TEST, 0, 0, 0, 0, 0 );
|
|
filldsreq( dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
|
|
if (STATUS( dsp ) == STA_CHECK
|
|
&& (SENSEBUF( dsp )[2] & 0xf) == 6) {
|
|
*changed = 1;
|
|
if (SENSEBUF( dsp )[12] == 0x29) {
|
|
act_like_a_cdrom( dsp );
|
|
}
|
|
}
|
|
|
|
if ((*changed || STATUS( dsp ) != STA_GOOD) && cd->toc) {
|
|
free (cd->toc);
|
|
cd->toc = 0;
|
|
}
|
|
}
|
|
|
|
int CDsbtoa(char *s, const unchar *sb, int count)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* XXX Error checking ?
|
|
*/
|
|
for (i = 0; i < count; i++)
|
|
*s++ = sbtoa[sb[i]];
|
|
*s = '\0';
|
|
return i;
|
|
}
|
|
|
|
void CDtimetoa(char *s, struct cdtimecode *tp)
|
|
{
|
|
s[0] = tp->mhi == 0xA ? '-' : '0' + tp->mhi;
|
|
s[1] = tp->mlo == 0xA ? '-' : '0' + tp->mlo;
|
|
s[3] = tp->shi == 0xA ? '-' : '0' + tp->shi;
|
|
s[4] = tp->slo == 0xA ? '-' : '0' + tp->slo;
|
|
s[6] = tp->fhi == 0xA ? '-' : '0' + tp->fhi;
|
|
s[7] = tp->flo == 0xA ? '-' : '0' + tp->flo;
|
|
}
|
|
|
|
void CDpreventremoval( CDPLAYER *cdplayer )
|
|
{
|
|
fillg0cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), G0_PREV, 0, 0, 0, 1, 0 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
}
|
|
|
|
void CDallowremoval( CDPLAYER *cdplayer )
|
|
{
|
|
fillg0cmd( cdplayer->dsp, CMDBUF(cdplayer->dsp), G0_PREV, 0, 0, 0, 0, 0 );
|
|
filldsreq( cdplayer->dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( cdplayer->dsp ), cdplayer->dsp );
|
|
}
|
|
|
|
/*
|
|
* static int act_like_a_cdrom( struct dsreq *dsp )
|
|
*
|
|
* Description:
|
|
* This command, which must be called after a SCSI reset occurs,
|
|
* instructs the CD-ROM drive to quit acting like a disk drive and
|
|
* start acting like a CD-ROM drive.
|
|
*
|
|
* Parameters:
|
|
* dsp devscsi
|
|
*/
|
|
|
|
static void act_like_a_cdrom( struct dsreq *dsp )
|
|
{
|
|
fillsgicmd( dsp, CMDBUF(dsp), SGI_CDROM, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
|
|
filldsreq( dsp, NULL, 0, DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
}
|
|
|
|
struct toc_header {
|
|
unsigned short length;
|
|
unsigned char first;
|
|
unsigned char last;
|
|
};
|
|
|
|
struct toc_entry {
|
|
unsigned char reserved;
|
|
unsigned char control;
|
|
unsigned char track;
|
|
unsigned char reserved2;
|
|
unsigned char reserved3;
|
|
unsigned char min;
|
|
unsigned char sec;
|
|
unsigned char frame;
|
|
};
|
|
|
|
struct toc {
|
|
struct toc_header head;
|
|
struct toc_entry tracks[1];
|
|
};
|
|
|
|
#define MAXTOCLEN (100 * sizeof (struct toc_entry) \
|
|
+ sizeof (struct toc_header))
|
|
|
|
static int
|
|
read_toc( CDPLAYER *cd )
|
|
{
|
|
dsreq_t *dsp = cd->dsp;
|
|
unsigned char *track_info = (unsigned char *)alloca(TRACK_INFO_SIZE);
|
|
unsigned char *sony_track_info = (unsigned char *)alloca(MAXTOCLEN);
|
|
struct toc *tocp;
|
|
int track, this_track, frames;
|
|
|
|
cd->toc = (TRACK *)malloc( sizeof (TRACK) * (cd->last - cd->first + 2) );
|
|
if (!cd->toc)
|
|
return (0);
|
|
if (cd->type == TOSHIBA) {
|
|
this_track = INTTOTWODIGITBCD( track );
|
|
for (track = cd->first; track <= cd->last; track++) {
|
|
this_track = INTTOTWODIGITBCD( track );
|
|
fillg6cmd( dsp, CMDBUF(dsp), T_READDISC, 2, this_track,
|
|
0, 0, 0, 0, 0, 0, 0 );
|
|
filldsreq( dsp, track_info, TRACK_INFO_SIZE, DSRQ_SENSE |
|
|
DSRQ_READ );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
if (STATUS(dsp) != STA_GOOD) {
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cd->state = CD_ERROR;
|
|
free( cd->toc );
|
|
cd->toc = 0;
|
|
return (0);
|
|
}
|
|
if (track_info[3] & 0x04) {
|
|
cd->state = CD_CDROM;
|
|
return 0;
|
|
}
|
|
cd->toc[track - cd->first].min =
|
|
TWODIGITBCDTOINT( track_info[0] );
|
|
cd->toc[track - cd->first].sec =
|
|
TWODIGITBCDTOINT( track_info[1] );
|
|
cd->toc[track - cd->first].frame =
|
|
TWODIGITBCDTOINT( track_info[2] );
|
|
}
|
|
cd->toc[cd->last + 1 - cd->first].min = cd->total_min;
|
|
cd->toc[cd->last + 1 - cd->first].sec = cd->total_sec;
|
|
cd->toc[cd->last + 1 - cd->first].frame = cd->total_frame;
|
|
}
|
|
else { /* SCSI-2 and SONY */
|
|
fills2cmd( dsp, CMDBUF(dsp), S2_TOC, 2, 0, 0, 0, 0, 0,
|
|
B2( MAXTOCLEN ), 0 );
|
|
filldsreq( dsp, sony_track_info, MAXTOCLEN,
|
|
DSRQ_SENSE | DSRQ_READ );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
if (STATUS(dsp) != STA_GOOD) {
|
|
CDDBG(fprintf(stderr, "%s(%d): CD_ERROR\n", __FILE__, __LINE__ ));
|
|
cd->state = CD_ERROR;
|
|
free( cd->toc );
|
|
cd->toc = 0;
|
|
return (0);
|
|
}
|
|
if (sony_track_info[5] & 0x04) {
|
|
cd->state = CD_CDROM;
|
|
return 0;
|
|
} else {
|
|
tocp = (struct toc *)sony_track_info;
|
|
for (track = cd->first; track <= cd->last + 1; track++) {
|
|
cd->toc[track - cd->first].min =
|
|
tocp->tracks[track - cd->first].min;
|
|
cd->toc[track - cd->first].sec =
|
|
tocp->tracks[track - cd->first].sec;
|
|
cd->toc[track - cd->first].frame =
|
|
tocp->tracks[track - cd->first].frame;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* This is because specifying the starting address of the lead-out
|
|
* track as the end-of-play address (for Sony) causes an error; we
|
|
* must back off by one frame. We do it for Toshiba as well to
|
|
* make the same database work for both.
|
|
*/
|
|
frames = cd->toc[cd->last - cd->first + 1].frame + 75 *
|
|
(cd->toc[cd->last - cd->first + 1].sec + 60 *
|
|
(cd->toc[cd->last - cd->first + 1].min));
|
|
frames--;
|
|
cd->toc[cd->last - cd->first + 1].frame = frames % 75;
|
|
frames /= 75;
|
|
cd->toc[cd->last - cd->first + 1].sec = frames % 60;
|
|
cd->toc[cd->last - cd->first + 1].min = frames / 60;
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
set_blksize( CDPLAYER *cd, int blksize )
|
|
{
|
|
char params[12];
|
|
int retries;
|
|
register struct dsreq *dsp = cd->dsp;
|
|
|
|
|
|
retries = 10;
|
|
while (retries--) {
|
|
(*modesense[cd->type])( dsp, params, sizeof (params) );
|
|
|
|
if (STATUS(dsp) == STA_GOOD) {
|
|
if (*(int *)¶ms[8] == blksize)
|
|
return (1);
|
|
else
|
|
break;
|
|
}
|
|
else if (STATUS(dsp) == STA_CHECK || STATUS(dsp) == STA_RETRY)
|
|
continue;
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
retries = 10;
|
|
while (retries--) {
|
|
memset( params, '\0', sizeof (params) );
|
|
params[3] = 0x08;
|
|
*(int *)¶ms[8] = blksize;
|
|
|
|
modeselect15( dsp, params, sizeof (params), 0, 0 );
|
|
|
|
if (STATUS(dsp) == STA_GOOD)
|
|
return (1);
|
|
else if (STATUS(dsp) == STA_CHECK || STATUS(dsp) == STA_RETRY)
|
|
continue;
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
scsi2_modesense( struct dsreq *dsp, char *params, int size )
|
|
{
|
|
#define SONY_PARAMS_SIZE (MSELPARMLEN + 8)
|
|
char *sony_params = (char *)alloca(SONY_PARAMS_SIZE);
|
|
char *databuf;
|
|
int datalen;
|
|
|
|
databuf = DATABUF( dsp );
|
|
datalen = DATALEN( dsp );
|
|
/*
|
|
* Amazingly enough, modesense fails if you don't request any
|
|
* page code information
|
|
*/
|
|
fillg0cmd( dsp, CMDBUF( dsp ), G0_MSEN, 0, 1, 0,
|
|
SONY_PARAMS_SIZE , 0 );
|
|
filldsreq( dsp, sony_params, SONY_PARAMS_SIZE,
|
|
DSRQ_READ | DSRQ_SENSE );
|
|
doscsireq( getfd( dsp ), dsp );
|
|
|
|
if (STATUS( dsp ) == STA_GOOD)
|
|
bcopy( sony_params, params, size );
|
|
|
|
DATABUF( dsp ) = databuf;
|
|
DATALEN( dsp ) = datalen;
|
|
}
|
|
|
|
static void
|
|
toshiba_modesense( struct dsreq *dsp, char *params, int size )
|
|
{
|
|
modesense1a( dsp, params, size, 0, 0, 0 );
|
|
}
|