;	/*\
;	|*|	cdrom.c
;	|*|	
;	|*|	routines for c-interface to mscdex audio functions
;	|*|
;	|*|	Copyright (c) 1992, Media Vision, Inc.  All Rights Reserved
;	|*|
;	\*/

#include "cdrom.h"

static int OURDISCSTATUS[26];

;	/*\
;	|*|	cdplay(int drive, long frame, long lframe)
;	|*|
;	|*|	begin audio play on drive from frame for lframe frames
;	|*|
;	|*|	Entry:	drive number, starting frame, count of frames to play
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

cdplay(int drive, long frame, long lframe)
{
	struct ioctlplay iop;

	iop.cdh.len= 13;
	iop.cdh.unit= (char) drive;
	iop.cdh.cmd= CD_CMD_PLAY;
	iop.cdh.stat= 0;

	iop.addrmode= 0;
	iop.startsector= frame- 150;
	iop.sectorcount= lframe;

	senddevreq(drive, &iop);

	if (!(iop.cdh.stat& 0x8000) || (iop.cdh.stat& 0x0100))
		{
		OURDISCSTATUS[drive]|= CDISPLAYING;
		OURDISCSTATUS[drive]&= (-1^ CDISPAUSED);
		}

	return(iop.cdh.stat);
}

;	/*\
;	|*|	cdstop(int drive)
;	|*|
;	|*|	stop audio play on drive
;	|*|
;	|*|	Entry:	drive number
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

cdstop(int drive)
{
	struct ioctlstop ios;

	if (cdstatus(drive)& CDISPLAYING)
		cdpause(drive);

	ios.cdh.len= 13;
	ios.cdh.unit= (char) drive;
	ios.cdh.cmd= CD_CMD_STOP;
	ios.cdh.stat= 0;

	senddevreq(drive, &ios);

	if (!(ios.cdh.stat& 0x8000) || (ios.cdh.stat& 0x0100))
		{
		OURDISCSTATUS[drive]&= (-1^ (CDISPAUSED| CDISPLAYING));
		}

	return(ios.cdh.stat);
}

;	/*\
;	|*|	cdpause(int drive)
;	|*|
;	|*|	pause audio play on drive
;	|*|
;	|*|	Entry:	drive number
;	|*|
;	|*|	Exit:	return 0 if not playing, else return driver status
;	|*|
;	\*/

cdpause(int drive)
{
	struct ioctlstop ios;

	if (!(cdstatus(drive)& CDISPLAYING))
		return(0);

	ios.cdh.len= 13;
	ios.cdh.unit= (char) drive;
	ios.cdh.cmd= CD_CMD_STOP;
	ios.cdh.stat= 0;

	senddevreq(drive, &ios);

	if (!(ios.cdh.stat& 0x8000))
		{
		OURDISCSTATUS[drive]|= CDISPAUSED;
		OURDISCSTATUS[drive]&= (-1^ CDISPLAYING);
		}

	return(ios.cdh.stat);
}

;	/*\
;	|*|	cdresume(int drive)
;	|*|
;	|*|	resume paused audio play on drive
;	|*|
;	|*|	Entry:	drive number
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

cdresume(int drive)
{
	struct ioctlresume ior;

	ior.cdh.len= 13;
	ior.cdh.unit= (char) drive;
	ior.cdh.cmd= CD_CMD_RESUME;
	ior.cdh.stat= 0;

	senddevreq(drive, &ior);

	if (!(ior.cdh.stat& 0x8000))
		{
		if ((cdstatus(drive)& CDISPLAYING))
			OURDISCSTATUS[drive]|= CDISPLAYING;
		OURDISCSTATUS[drive]&= (-1^ CDISPAUSED);
		}

	return(ior.cdh.stat);
}

;	/*\
;	|*|	cdseek(int drive, long frame)
;	|*|
;	|*|	move head on drive to specified frame
;	|*|
;	|*|	Entry:	drive number, frame number
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

cdseek(int drive, long frame)
{
	struct ioctlseek ios;

	ios.cdh.len= 13;
	ios.cdh.unit= (char) drive;
	ios.cdh.cmd= CD_CMD_SEEK;
	ios.cdh.stat= 0;

	ios.addrmode= 0;
	ios.buffer= (void far *) 0;
	ios.sectorcount= 0;
	ios.startsector= frame- 150;

	senddevreq(drive, &ios);

	if (!(ios.cdh.stat& 0x8000))
		{
		OURDISCSTATUS[drive]&= (-1^ (CDISPLAYING| CDISPAUSED));
		}

	return(ios.cdh.stat);
}

;	/*\
;	|*|	cdreset(int drive)
;	|*|
;	|*|	reset the driver for specified drive
;	|*|
;	|*|	Entry:	drive number
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

cdreset(int drive)
{
	struct ioctlwrite iow;
	char dummy= CD_CMD_RESET;

	iow.cdh.len= 13;
	iow.cdh.unit= (char) drive;
	iow.cdh.cmd= IOCTL_WRITE;
	iow.cdh.stat= 0;

	iow.mdb= 0;
	iow.buffer= (void far *) &dummy;
	iow.size= sizeof(dummy);
	iow.ssn= 0;
	iow.errbuf= (void far *) 0;

	senddevreq(drive, &iow);

	if (!(iow.cdh.stat& 0x8000))
		OURDISCSTATUS[drive]&= (-1^ (CDISPAUSED| CDISPLAYING| CDISHERE));

	return(iow.cdh.stat);
}

;	/*\
;	|*|	cdeject(int drive)
;	|*|
;	|*|	eject disc in drive
;	|*|
;	|*|	Entry:	drive number
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

cdeject(int drive)
{
	struct ioctlwrite iow;
	char dummy= CD_CMD_EJECT;

	iow.cdh.len= 13;
	iow.cdh.unit= (char) drive;
	iow.cdh.cmd= IOCTL_WRITE;
	iow.cdh.stat= 0;

	iow.mdb= 0;
	iow.buffer= (void far *) &dummy;
	iow.size= sizeof(dummy);
	iow.ssn= 0;
	iow.errbuf= (void far *) 0;

	senddevreq(drive, &iow);

	if (!(iow.cdh.stat& 0x8000))
		OURDISCSTATUS[drive]&= (-1^ (CDISPAUSED| CDISPLAYING| CDISHERE));

	return(iow.cdh.stat);
}

;	/*\
;	|*|	cdstatus(int drive)
;	|*|
;	|*|	get audio status of drive number, update internal status variable
;	|*|
;	|*|	Entry:	drive number
;	|*|
;	|*|	Exit:	internal status variable
;	|*|
;	\*/

cdstatus(int drive)
{
	int status= 0;
	long d1, d2;

	status= cdaudiostatus(drive, &d1, &d2);

	return(OURDISCSTATUS[drive]);
}

;	/*\
;	|*|	cdaudiostatus(int drive, long *nextstart, long *nextend)
;	|*|
;	|*|	get audio status of drive number, update internal status variable
;	|*|
;	|*|	Entry:	drive number, pointers to nextstart and nextend frames
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

cdaudiostatus(int drive, long *nextstart, long *nextend)
{
	int status= 0;
	struct ioctlread ior;
	struct ioctlstat ios;
	struct qchaninfo sqi;

	cdqchaninfo(drive, &sqi);

	ior.cdh.len= 13;
	ior.cdh.unit= (char) drive;
	ior.cdh.cmd= IOCTL_READ;
	ior.cdh.stat= 0;

	ior.mdb= 0;
	ior.buffer= (void far *) &ios;
	ior.size= sizeof(ios);
	ior.ssn= 0;
	ior.errbuf= (void far *) 0;

	ios.cmd= 15;
	ios.status= 0;
	ios.startloc= 0;
	ios.endloc= 0;

	senddevreq(drive, &ior);

	if (ior.cdh.stat& 0x0200)	status|= CDISPLAYING;
	if (ios.status& 0x0001) 	status|= CDISPAUSED;

	OURDISCSTATUS[drive]&= CDISHERE;
	if (OURDISCSTATUS[drive]& CDISHERE)
		if (!status)
			{
			int i= 0;
			long d;
			long s;
			struct qchaninfo dqi;
			s= msftolong(* ((long *) &sqi.min));

			do
				{
				long *pd= (long *) &dqi.min;
				cdqchaninfo(drive, &dqi);
				d= msftolong(*pd);
				}
			while (++i < 5 && d - s < 75);

			if (i < 5) 	status|= CDISPLAYING;
			}

	OURDISCSTATUS[drive]|= status;

	*nextstart= ios.startloc;
	*nextend= ios.endloc;

	return(ios.status);
}

;	/*\
;	|*|	cdmediachanged(int drive, int *yesorno)
;	|*|
;	|*|	check if media has changed for drive
;	|*|
;	|*|	Entry:	drive number, pointer to flag
;	|*|
;	|*|	Exit:	return 0 if successful, else return -1 if error occurred
;	|*|
;	\*/

cdmediachanged(int drive, int *yesorno)
{
	int status= 0;
	struct ioctlread ior;
	struct {
		char cmd;
		char changed;
		} dummy;

	ior.cdh.len= sizeof(struct cdreqheader);
	ior.cdh.unit= (char) drive;
	ior.cdh.cmd= IOCTL_READ;
	ior.cdh.stat= 0;
	
	ior.mdb= 0;
	ior.buffer= (void far *) &dummy;
	ior.size= sizeof(dummy);
	ior.ssn= 0;
	ior.errbuf= (void far *) 0;

	dummy.cmd= 9;
	dummy.changed= 0;

	senddevreq(drive, &ior);
	status= ior.cdh.stat;

	if (status& 0x8000)
		return(-1);

	*yesorno= dummy.changed;

	return(0);
}

;	/*\
;	|*|	int cddiscinfo(int drive, struct discinfo *di)
;	|*|
;	|*|	get information on disc in drive
;	|*|
;	|*|	Entry:	drive number, address of buffer
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

int cddiscinfo(int drive, struct discinfo *di)
{
	int status= 0;
	struct ioctlread ior;

	ior.cdh.len= sizeof(struct cdreqheader);
	ior.cdh.unit= (char) drive;
	ior.cdh.cmd= IOCTL_READ;
	ior.cdh.stat= 0;

	ior.mdb= 0;
	ior.buffer= (void far *) di;
	ior.size= sizeof(struct discinfo);
	ior.ssn= 0;
	ior.errbuf= (void far *) 0;

	di->cmd= CD_GETDISCINFO;
	di->strk= 0;
	di->ltrk= 0;
	di->eodisc= 0;

	senddevreq(drive, &ior);
	status= ior.cdh.stat;

	if (status& 0x8000 || !(status& 0x0100))
		OURDISCSTATUS[drive]&= (-1^ CDISHERE);
	else
		OURDISCSTATUS[drive]|= CDISHERE;

	return(status);
}

;	/*\
;	|*|	int cdtrackinfo(int drive, int track, struct trackinfo *ti)
;	|*|
;	|*|	get information on track of disc in drive
;	|*|
;	|*|	Entry:	drive number, track number, address of buffer
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

int cdtrackinfo(int drive, int track, struct trackinfo *ti)
{
	int status= 0;
	struct ioctlread ior;

	ior.cdh.len= 13;
	ior.cdh.unit= (char) drive;
	ior.cdh.cmd= IOCTL_READ;
	ior.cdh.stat= 0;

	ior.mdb= 0;
	ior.buffer= (void far *) ti;
	ior.size= sizeof(struct trackinfo);
	ior.ssn= 0;
	ior.errbuf= (void far *) 0;

	ti->cmd= CD_GETTRACKINFO;
	ti->track= (char) track;
	ti->min= 0;
	ti->sec= 0;
	ti->frame= 0;
	ti->control= 0;

	senddevreq(drive, &ior);
	status|= ior.cdh.stat;

	return(status);
}


;	/*\
;	|*|	int cdqchaninfo(int drive, struct qchaninfo *qi)
;	|*|
;	|*|	get status from q-channel for drive
;	|*|
;	|*|	Entry:	drive number, address of buffer
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

int cdqchaninfo(int drive, struct qchaninfo *qi)
{
	int status= 0;
	struct ioctlread ior;

	ior.cdh.len= sizeof(struct cdreqheader);
	ior.cdh.unit= (char) drive;
	ior.cdh.cmd= IOCTL_READ;
	ior.cdh.stat= 0;

	ior.mdb= 0;
	ior.buffer= (void far *) qi;
	ior.size= sizeof(struct qchaninfo);
	ior.ssn= 0;
	ior.errbuf= (void far *) 0;

	qi->cmd= CD_GETQCHANINFO;
	qi->caa= 0;
	qi->track= 0;
	qi->index= 0;
	qi->min= 0;
	qi->sec= 0;
	qi->frame= 0;
	qi->reserved1= 0;
	qi->amin= 0;
	qi->asec= 0;
	qi->aframe= 0;

	senddevreq(drive, &ior);
	status= ior.cdh.stat;

	if (status& 0x8000 || !(status& 0x0100))
		OURDISCSTATUS[drive]&= (-1^ CDISHERE);
	else
		OURDISCSTATUS[drive]|= CDISHERE;

	return(status);
}

;	/*\
;	|*|	isanaudiocd(int drive)
;	|*|
;	|*|	check if disc in drive is an audio cd
;	|*|
;	|*|	Entry:	drive number
;	|*|
;	|*|	Exit:	return 1 if is an audio cd, else return 0
;	|*|
;	\*/

isanaudiocd(int drive)
{
	int failcount= 0;
	struct discinfo di;

	do
		{
		if (!((cddiscinfo(drive, &di))& 0x8000))
			return(1);
		}
	while (++failcount < 5);

	return(0);

}

;	/*\
;	|*|	cdseekmsf(int drive, int min, int sec, int frame)
;	|*|
;	|*|	move head on drive to frame specified by min:sec:frame
;	|*|
;	|*|	Entry:	drive number, minutes, seconds, frames
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

cdseekmsf(int drive, int min, int sec, int frame)
{
	long f;

	f= (long) min* 60;
	f+= (long) sec;
	f*= (long) 75;
	f+= (long) frame;

	return(cdseek(drive, f));
}

;	/*\
;	|*|	cdplaymsf(int drive, int min, int sec, int frame, int lmin, int lsec, int lframe)
;	|*|
;	|*|	begin play for drive according at min:sec:frame for min:sec:frame
;	|*|
;	|*|	Entry:	drive number, minutes:seconds:frames to start and
;	|*|				minutes:seconds:frames for length
;	|*|
;	|*|	Exit:	return driver status
;	|*|
;	\*/

cdplaymsf(int drive, int min, int sec, int frame, int lmin, int lsec, int lframe)
{
	long f;
	long lf;

	f= (long) min* 60;
	f+= (long) sec;
	f*= (long) 75;
	f+= (long) frame;
	lf= (long) lmin* 60;
	lf+= (long) lsec;
	lf*= (long) 75;
	lf+= (long) lframe;

	return(cdplay(drive, f, lf));
}

;	/*\
;	|*|	long redtolong(long redaddress)
;	|*|
;	|*|	convert redbook address to frame number
;	|*|
;	|*|	Entry:	redbook address (0x00MMSSFF)
;	|*|
;	|*|	Exit:	frame number
;	|*|
;	\*/

long redtolong(long redaddress)
{
	long longval= 0;
	union 
		{
		struct {
			char frame;
			char sec;
			char min;
			char dead;
			} rtl;
		long l;
		} d;

	d.l= redaddress;

	longval+= d.rtl.min;
	longval*= 60;
	longval+= d.rtl.sec;
	longval*= 75;
	longval+= d.rtl.frame;

	return(longval);
}


;	/*\
;	|*|	long longtored(long longval)
;	|*|
;	|*|	convert frame number to redbook address   
;	|*|
;	|*|	Entry:	frame value 
;	|*|
;	|*|	Exit:		redbook address (0x00MMSSFF)
;	|*|
;	\*/

long longtored(long longval)
{
	union 
		{
		struct {
			char frame;
			char sec;
			char min;
			char dead;
			} rtl;
		long l;
		} d;

	d.rtl.min= longval/ 4500;
	d.rtl.sec= (longval% 4500)/ 75;
	d.rtl.frame= (longval% 4500)% 75;

	return(d.l);
}

;	/*\
;	|*|	long msftolong(long msfvalue)
;	|*|
;	|*|	convert FFSSMM00 value to frame number
;	|*|
;	|*|	Entry:	msf value (0xFFSSMM00)
;	|*|
;	|*|	Exit:	frame number
;	|*|
;	\*/

long msftolong(long msfvalue)
{
	long longval= 0;
	union 
		{
		struct {
			char min;
			char sec;
			char frame;
			char dead;
			} mtl;
		long l;
		} d;

	d.l= msfvalue;

	longval+= d.mtl.min;
	longval*= 60;
	longval+= d.mtl.sec;
	longval*= 75;
	longval+= d.mtl.frame;

	return(longval);
}

;	/*\
;	|*|	inttobcd(int data)
;	|*|
;	|*|	convert an integer to BCD representation
;	|*|
;	|*|	Entry:	integer
;	|*|
;	|*|	Exit:	value as BCD
;	|*|
;	\*/

inttobcd(int data)
{
	int val;

	val= (data/ 10)<< 4;
	val+= (data% 10);

	return(val);
}

;	/*\
;	|*|	bcdtoint(int data)
;	|*|
;	|*|	convert a BCD number to an integer
;	|*|
;	|*|	Entry:	BCD number
;	|*|
;	|*|	Exit:	integer value
;	|*|
;	\*/

bcdtoint(int data)
{
	int val;

	val= data& 0x0F;
	val+= ((data&0xF0)>> 4)* 10;

	return(val);
}

;	/*\
;	|*|	fixmsf(int *min, int *sec, int *frame)
;	|*|
;	|*|	boundarize elements of msf number
;	|*|
;	|*|	Entry:	int pointers to minutes, seconds and frame variables
;	|*|
;	|*|	Exit:	return -1 if minute ends up negative, 0 if not and
;	|*|			affect pointed to numbers
;	|*|
;	\*/

fixmsf(int *min, int *sec, int *frame)
{
	int f= *frame;
	int s= *sec;
	int m= *min;

	while (f >= 75)
		{
		f-= 75;
		s+= 1;
		}

	while (s >= 60)
		{
		s-= 60;
		m+= 1;
		}

	while (f < 0)
		{
		f+= 75;
		s-= 1;
		}

	while (s < 0)
		{
		s+= 60;
		m-= 1;
		}

	if (m < 0)
		return(-1);

	*frame= f;
	*sec= s;
	*min= m;

	return(0);
}


