* Copyright 1993 by Holger Veit (data part)
* Copyright 1993 by Brian Moore (audio part)
* Changes Copyright 1993 by Gary Clark II
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This software was developed by Holger Veit and Brian Moore
* for use with "386BSD" and similar operating systems.
* "Similar operating systems" includes mainly non-profit oriented
* systems for research and education, including but not restricted to
* "NetBSD", "FreeBSD", "Mach" (by CMU).
* 4. Neither the name of the developer(s) nor the name "386BSD"
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* $Id: mcd.c,v 1.6 1994/01/18 02:20:15 nate Exp $
static char COPYRIGHT
[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
#include "i386/isa/isa.h"
#include "i386/isa/isa_device.h"
/* user definable options */
/*#define MCD_TO_WARNING_ON*/ /* define to get timeout messages */
/*#define MCDMINI*/ /* define for a mini configuration for boot kernel */
#define MCD_TRACE(fmt,a,b,c,d)
#define MCD_TRACE(fmt,a,b,c,d) {if (mcd_data[unit].debug) {printf("mcd%d st=%02x: ",unit,mcd_data[unit].status); printf(fmt,a,b,c,d);}}
#define mcd_part(dev) ((minor(dev)) & 7)
#define mcd_unit(dev) (((minor(dev)) & 0x38) >> 3)
#define mcd_phys(dev) (((minor(dev)) & 0x40) >> 6)
#define MCDOPEN 0x0001 /* device opened */
#define MCDVALID 0x0002 /* parameters loaded */
#define MCDINIT 0x0004 /* device is init'd */
#define MCDWAIT 0x0008 /* waiting for something */
#define MCDLABEL 0x0010 /* label is read */
#define MCDPROBING 0x0020 /* probing */
#define MCDREADRAW 0x0040 /* read raw mode (2352 bytes) */
#define MCDVOLINFO 0x0080 /* already read volinfo */
#define MCDTOC 0x0100 /* already read toc */
#define MCDMBXBSY 0x0200 /* local mbx is busy */
#define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */
#define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */
#define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */
#define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */
#define MCD_MAXTOCS 104 /* from the Linux driver */
#define MCD_LASTPLUS1 170 /* special toc entry */
int partflags
[MAXPARTITIONS
];
struct mcd_volinfo volinfo
;
struct mcd_qchninfo toc
[MCD_MAXTOCS
];
struct buf head
; /* head of buf queue */
/* reader state machine */
void mcdstrategy(struct buf
*bp
);
int mcdioctl(dev_t dev
, int cmd
, caddr_t addr
, int flags
);
static void mcd_done(struct mcd_mbx
*mbx
);
static void mcd_start(int unit
);
static int mcd_getdisklabel(int unit
);
static void mcd_configure(struct mcd_data
*cd
);
static int mcd_get(int unit
, char *buf
, int nmax
);
static void mcd_setflags(int unit
,struct mcd_data
*cd
);
static int mcd_getstat(int unit
,int sflg
);
static int mcd_send(int unit
, int cmd
,int nretrys
);
static int bcd2bin(bcd_t b
);
static bcd_t
bin2bcd(int b
);
static void hsg2msf(int hsg
, bcd_t
*msf
);
static int msf2hsg(bcd_t
*msf
);
static int mcd_volinfo(int unit
);
static int mcd_waitrdy(int port
,int dly
);
static void mcd_doread(int state
, struct mcd_mbx
*mbxin
);
static int mcd_setmode(int unit
, int mode
);
static int mcd_getqchan(int unit
, struct mcd_qchninfo
*q
);
static int mcd_subchan(int unit
, struct ioc_read_subchannel
*sc
);
static int mcd_toc_header(int unit
, struct ioc_toc_header
*th
);
static int mcd_read_toc(int unit
);
static int mcd_toc_entry(int unit
, struct ioc_read_toc_entry
*te
);
static int mcd_stop(int unit
);
static int mcd_playtracks(int unit
, struct ioc_play_track
*pt
);
static int mcd_play(int unit
, struct mcd_read2
*pb
);
static int mcd_pause(int unit
);
static int mcd_resume(int unit
);
extern int mcd_probe(struct isa_device
*dev
);
extern int mcd_attach(struct isa_device
*dev
);
struct isa_driver mcddriver
= { mcd_probe
, mcd_attach
, "mcd" };
#define mcd_put(port,byte) outb(port,byte)
#define MCDBLK 2048 /* for cooked mode */
#define MCDRBLK 2352 /* for raw mode */
#define RDELAY_WAITSTAT 300
#define RDELAY_WAITMODE 300
#define RDELAY_WAITREAD 800
#define DELAY_STATUS 10000l /* 10000 * 1us */
#define DELAY_GETREPLY 200000l /* 200000 * 2us */
#define DELAY_SEEKREAD 20000l /* 20000 * 1us */
int mcd_attach(struct isa_device
*dev
)
struct mcd_data
*cd
= mcd_data
+ dev
->id_unit
;
cd
->iobase
= dev
->id_iobase
;
for (i
=0; i
<MAXPARTITIONS
; i
++) cd
->partflags
[i
] = 0;
/* wire controller for interrupts and dma */
if (!(cd
->flags
& MCDINIT
))
/* invalidated in the meantime? mark all open part's invalid */
if (!(cd
->flags
& MCDVALID
) && cd
->openflags
)
if (mcd_getstat(unit
,1) < 0)
/* XXX get a default disklabel */
printf("mcd%d: failed to get disk size\n",unit
);
MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
part
,cd
->disksize
,cd
->blksize
,0);
(part
< cd
->dlabel
.d_npartitions
&&
cd
->dlabel
.d_partitions
[part
].p_fstype
!= FS_UNUSED
)) {
cd
->partflags
[part
] |= MCDOPEN
;
cd
->openflags
|= (1<<part
);
if (part
== RAW_PART
&& phys
!= 0)
cd
->partflags
[part
] |= MCDREADRAW
;
if (!(cd
->flags
& MCDINIT
))
mcd_getstat(unit
,1); /* get status */
cd
->partflags
[part
] &= ~(MCDOPEN
|MCDREADRAW
);
cd
->openflags
&= ~(1<<part
);
MCD_TRACE("close: partition=%d\n",part
,0,0,0);
mcdstrategy(struct buf
*bp
)
int unit
= mcd_unit(bp
->b_dev
);
/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
bp,unit,bp->b_blkno,bp->b_bcount);*/
if (unit
>= NMCD
|| bp
->b_blkno
< 0) {
printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
unit
, bp
->b_blkno
, bp
->b_bcount
);
pg("mcd: mcdstratregy failure");
/* if device invalidated (e.g. media change, door open), error */
if (!(cd
->flags
& MCDVALID
)) {
MCD_TRACE("strategy: drive not valid\n",0,0,0,0);
if (!(bp
->b_flags
& B_READ
)) {
/* for non raw access, check partition limits */
if (mcd_part(bp
->b_dev
) != RAW_PART
) {
if (!(cd
->flags
& MCDLABEL
)) {
/* adjust transfer if necessary */
if (bounds_check_with_label(bp
,&cd
->dlabel
,1) <= 0) {
/* now check whether we can perform processing */
bp
->b_resid
= bp
->b_bcount
;
static void mcd_start(int unit
)
struct mcd_data
*cd
= mcd_data
+ unit
;
struct buf
*bp
, *qp
= &cd
->head
;
if (cd
->flags
& MCDMBXBSY
)
if ((bp
= qp
->b_actf
) != 0) {
/* block found to process, dequeue */
/*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
qp
->b_actf
= bp
->av_forw
;
if (!(cd
->flags
& MCDVALID
)) {
MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
p
= cd
->dlabel
.d_partitions
+ mcd_part(bp
->b_dev
);
cd
->mbx
.port
= cd
->iobase
;
cd
->mbx
.retry
= MCD_RETRYS
;
cd
->mbx
.p_offset
= p
->p_offset
;
/* calling the read routine */
mcd_doread(MCD_S_BEGIN
,&(cd
->mbx
));
/* triggers mcd_start, when successful finished */
int mcdioctl(dev_t dev
, int cmd
, caddr_t addr
, int flags
)
if (!(cd
->flags
& MCDVALID
))
MCD_TRACE("ioctl called 0x%x\n",cmd
,0,0,0);
return mcd_playtracks(unit
, (struct ioc_play_track
*) addr
);
return mcd_play(unit
, (struct mcd_read2
*) addr
);
case CDIOCREADSUBCHANNEL
:
return mcd_subchan(unit
, (struct ioc_read_subchannel
*) addr
);
return mcd_toc_header(unit
, (struct ioc_toc_header
*) addr
);
return mcd_toc_entry(unit
, (struct ioc_read_toc_entry
*) addr
);
/* this could have been taken from scsi/cd.c, but it is not clear
* whether the scsi cd driver is linked in
static int mcd_getdisklabel(int unit
)
struct mcd_data
*cd
= mcd_data
+ unit
;
if (cd
->flags
& MCDLABEL
)
bzero(&cd
->dlabel
,sizeof(struct disklabel
));
strncpy(cd
->dlabel
.d_typename
,"Mitsumi CD ROM ",16);
strncpy(cd
->dlabel
.d_packname
,"unknown ",16);
cd
->dlabel
.d_secsize
= cd
->blksize
;
cd
->dlabel
.d_nsectors
= 100;
cd
->dlabel
.d_ntracks
= 1;
cd
->dlabel
.d_ncylinders
= (cd
->disksize
/100)+1;
cd
->dlabel
.d_secpercyl
= 100;
cd
->dlabel
.d_secperunit
= cd
->disksize
;
cd
->dlabel
.d_interleave
= 1;
cd
->dlabel
.d_flags
= D_REMOVABLE
;
cd
->dlabel
.d_npartitions
= 1;
cd
->dlabel
.d_partitions
[0].p_offset
= 0;
cd
->dlabel
.d_partitions
[0].p_size
= cd
->disksize
;
cd
->dlabel
.d_partitions
[0].p_fstype
= 9;
cd
->dlabel
.d_magic
= DISKMAGIC
;
cd
->dlabel
.d_magic2
= DISKMAGIC
;
cd
->dlabel
.d_checksum
= dkcksum(&cd
->dlabel
);
int unit
= mcd_unit(dev
);
struct mcd_data
*cd
= mcd_data
+ unit
;
if (mcd_volinfo(unit
) >= 0) {
size
= msf2hsg(cd
->volinfo
.vol_msf
);
cd
->disksize
= size
* (MCDBLK
/DEV_BSIZE
);
/***************************************************************
* lower level of driver starts here
**************************************************************/
0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
static void mcd_configure(struct mcd_data
*cd
)
outb(cd
->iobase
+mcd_config
,cd
->config
);
/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
int mcd_probe(struct isa_device
*dev
)
int port
= dev
->id_iobase
;
mcd_data
[unit
].flags
= MCDPROBING
;
/* get irq/drq configuration word */
mcd_data
[unit
].config
= irqs
[dev
->id_irq
]; /* | drqs[dev->id_drq];*/
mcd_data
[unit
].config
= 0;
outb(port
+MCD_FLAGS
,M_RESET
);
/* get any pending garbage (old data) and throw away...*/
for (i
=10; i
!= 0; i
--) {
outb(port
+MCD_DATA
,MCD_CMDCONTINFO
);
for (i
= 0; i
< 30000; i
++) {
if ((inb(port
+MCD_FLAGS
) & M_STATUS_AVAIL
) == M_STATUS_AVAIL
)
check
= inb(port
+MCD_DATA
);
junk
= inb(port
+MCD_DATA
); /* What is byte used for?!?!? */
printf("Mitsumi drive detected\n");
printf("Mitsumi drive NOT detected\n");
static int mcd_waitrdy(int port
,int dly
)
/* wait until xfer port senses data ready */
if ((inb(port
+mcd_xfer
) & MCD_ST_BUSY
)==0)
static int mcd_getreply(int unit
,int dly
)
struct mcd_data
*cd
= mcd_data
+ unit
;
/* wait data to become ready */
if (mcd_waitrdy(port
,dly
)<0) {
printf("mcd%d: timeout getreply\n",unit
);
return inb(port
+mcd_status
) & 0xFF;
static int mcd_getstat(int unit
,int sflg
)
struct mcd_data
*cd
= mcd_data
+ unit
;
outb(port
+mcd_command
, MCD_CMDGETSTAT
);
i
= mcd_getreply(unit
,DELAY_GETREPLY
);
static void mcd_setflags(int unit
, struct mcd_data
*cd
)
if (cd
->status
& (MCDDSKCHNG
|MCDDOOROPEN
)) {
MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
if (cd
->status
& MCDAUDIOBSY
)
cd
->audio_status
= CD_AS_PLAY_IN_PROGRESS
;
else if (cd
->audio_status
== CD_AS_PLAY_IN_PROGRESS
)
cd
->audio_status
= CD_AS_PLAY_COMPLETED
;
static int mcd_get(int unit
, char *buf
, int nmax
)
int port
= mcd_data
[unit
].iobase
;
if ((k
= mcd_getreply(unit
,DELAY_GETREPLY
)) < 0) {
printf("mcd%d: timeout mcd_get\n",unit
);
static int mcd_send(int unit
, int cmd
,int nretrys
)
int port
= mcd_data
[unit
].iobase
;
/*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
for (i
=0; i
<nretrys
; i
++) {
outb(port
+mcd_command
, cmd
);
if ((k
=mcd_getstat(unit
,0)) != -1)
printf("mcd%d: mcd_send retry cnt exceeded\n",unit
);
/*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
static int bcd2bin(bcd_t b
)
return (b
>> 4) * 10 + (b
& 15);
static bcd_t
bin2bcd(int b
)
return ((b
/ 10) << 4) | (b
% 10);
static void hsg2msf(int hsg
, bcd_t
*msf
)
M_msf(msf
) = bin2bcd(hsg
/ 4500);
S_msf(msf
) = bin2bcd(hsg
/ 75);
F_msf(msf
) = bin2bcd(hsg
% 75);
static int msf2hsg(bcd_t
*msf
)
return (bcd2bin(M_msf(msf
)) * 60 +
bcd2bin(S_msf(msf
))) * 75 +
bcd2bin(F_msf(msf
)) - 150;
static int mcd_volinfo(int unit
)
struct mcd_data
*cd
= mcd_data
+ unit
;
/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
/* Get the status, in case the disc has been changed */
if (mcd_getstat(unit
, 1) < 0) return EIO
;
/* Just return if we already have it */
if (cd
->flags
& MCDVOLINFO
) return 0;
/* send volume info command */
if (mcd_send(unit
,MCD_CMDGETVOLINFO
,MCD_RETRYS
) < 0)
if (mcd_get(unit
,(char*) &cd
->volinfo
,sizeof(struct mcd_volinfo
)) < 0) {
printf("mcd%d: mcd_volinfo: error read data\n",unit
);
if (cd
->volinfo
.trk_low
!= 0 || cd
->volinfo
.trk_high
!= 0) {
cd
->flags
|= MCDVOLINFO
; /* volinfo is OK */
int port
= mcd_data
[unit
].iobase
;
MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port
+mcd_xfer
),0,0,0);
/* just read out status and ignore the rest */
if ((inb(port
+mcd_xfer
)&0xFF) != 0xFF) {
i
= inb(port
+mcd_status
);
/* state machine to process read requests
* initialize with MCD_S_BEGIN: calculate sizes, and read status
* MCD_S_WAITSTAT: wait for status reply, set mode
* MCD_S_WAITMODE: waits for status reply from set mode, set read command
* MCD_S_WAITREAD: wait for read ready, read data
static struct mcd_mbx
*mbxsave
;
static void mcd_doread(int state
, struct mcd_mbx
*mbxin
)
struct mcd_mbx
*mbx
= (state
!=MCD_S_BEGIN
) ? mbxsave
: mbxin
;
struct buf
*bp
= mbx
->bp
;
struct mcd_data
*cd
= mcd_data
+ unit
;
outb(port
+mcd_command
, MCD_CMDGETSTAT
);
mbx
->count
= RDELAY_WAITSTAT
;
timeout((timeout_func_t
)mcd_doread
,(caddr_t
)MCD_S_WAITSTAT
,hz
/100); /* XXX */
untimeout((timeout_func_t
)mcd_doread
,(caddr_t
)MCD_S_WAITSTAT
);
if (inb(port
+mcd_xfer
) & MCD_ST_BUSY
) {
timeout((timeout_func_t
)mcd_doread
,(caddr_t
)MCD_S_WAITSTAT
,hz
/100); /* XXX */
MCD_TRACE("got WAITSTAT delay=%d\n",RDELAY_WAITSTAT
-mbx
->count
,0,0,0);
/* reject, if audio active */
if (cd
->status
& MCDAUDIOBSY
) {
printf("mcd%d: audio is active\n",unit
);
/* to check for raw/cooked mode */
if (cd
->flags
& MCDREADRAW
) {
mbx
->count
= RDELAY_WAITMODE
;
mcd_put(port
+mcd_command
, MCD_CMDSETMODE
);
mcd_put(port
+mcd_command
, rm
);
timeout((timeout_func_t
)mcd_doread
,(caddr_t
)MCD_S_WAITMODE
,hz
/100); /* XXX */
printf("mcd%d: timeout getstatus\n",unit
);
untimeout((timeout_func_t
)mcd_doread
,(caddr_t
)MCD_S_WAITMODE
);
printf("mcd%d: timeout set mode\n",unit
);
if (inb(port
+mcd_xfer
) & MCD_ST_BUSY
) {
timeout((timeout_func_t
)mcd_doread
,(caddr_t
)MCD_S_WAITMODE
,hz
/100);
MCD_TRACE("got WAITMODE delay=%d\n",RDELAY_WAITMODE
-mbx
->count
,0,0,0);
mbx
->nblk
= (bp
->b_bcount
+ (mbx
->sz
-1)) / mbx
->sz
;
blknum
= (bp
->b_blkno
/ (mbx
->sz
/DEV_BSIZE
))
+ mbx
->p_offset
+ mbx
->skip
/mbx
->sz
;
MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",blknum
,bp
,0,0);
/* build parameter block */
hsg2msf(blknum
,rbuf
.start_msf
);
/* send the read command */
mcd_put(port
+mcd_command
,MCD_CMDREAD2
);
mcd_put(port
+mcd_command
,rbuf
.start_msf
[0]);
mcd_put(port
+mcd_command
,rbuf
.start_msf
[1]);
mcd_put(port
+mcd_command
,rbuf
.start_msf
[2]);
mcd_put(port
+mcd_command
,0);
mcd_put(port
+mcd_command
,0);
mcd_put(port
+mcd_command
,1);
mbx
->count
= RDELAY_WAITREAD
;
timeout((timeout_func_t
)mcd_doread
,(caddr_t
)MCD_S_WAITREAD
,hz
/100); /* XXX */
untimeout((timeout_func_t
)mcd_doread
,(caddr_t
)MCD_S_WAITREAD
);
MCD_TRACE("got data delay=%d\n",RDELAY_WAITREAD
-mbx
->count
,0,0,0);
addr
= bp
->b_un
.b_addr
+ mbx
->skip
;
outb(port
+mcd_ctl2
,0x04); /* XXX */
for (i
=0; i
<mbx
->sz
; i
++)
*addr
++ = inb(port
+mcd_rdata
);
outb(port
+mcd_ctl2
,0x0c); /* XXX */
timeout((timeout_func_t
)mcd_doread
,(caddr_t
)MCD_S_WAITREAD
,hz
/100); /* XXX */
printf("mcd%d: timeout read data\n",unit
);
printf("mcd%d: retrying\n",unit
);
/* invalidate the buffer */
bp
->b_resid
= bp
->b_bcount
;
printf("mcd%d: unit timeout, resetting\n",mbx
->unit
);
outb(mbx
->port
+mcd_reset
,MCD_CMDRESET
);
(void)mcd_getstat(mbx
->unit
,1);
(void)mcd_getstat(mbx
->unit
,1);
/*cd->status &= ~MCDDSKCHNG; */
cd
->debug
= 1; /* preventive set debug mode */
static int mcd_setmode(int unit
, int mode
)
struct mcd_data
*cd
= mcd_data
+ unit
;
printf("mcd%d: setting mode to %d\n", unit
, mode
);
for(retry
=0; retry
<MCD_RETRYS
; retry
++)
outb(port
+mcd_command
, MCD_CMDSETMODE
);
outb(port
+mcd_command
, mode
);
if (mcd_getstat(unit
, 0) != -1) return 0;
static int mcd_toc_header(int unit
, struct ioc_toc_header
*th
)
struct mcd_data
*cd
= mcd_data
+ unit
;
if (mcd_volinfo(unit
) < 0)
th
->len
= msf2hsg(cd
->volinfo
.vol_msf
);
th
->starting_track
= bcd2bin(cd
->volinfo
.trk_low
);
th
->ending_track
= bcd2bin(cd
->volinfo
.trk_high
);
static int mcd_read_toc(int unit
)
struct mcd_data
*cd
= mcd_data
+ unit
;
struct ioc_toc_header th
;
/* Only read TOC if needed */
if (cd
->flags
& MCDTOC
) return 0;
printf("mcd%d: reading toc header\n", unit
);
if (mcd_toc_header(unit
, &th
) != 0)
printf("mcd%d: stopping play\n", unit
);
if ((rc
=mcd_stop(unit
)) != 0)
/* try setting the mode twice */
if (mcd_setmode(unit
, MCD_MD_TOC
) != 0)
if (mcd_setmode(unit
, MCD_MD_TOC
) != 0)
printf("mcd%d: get_toc reading qchannel info\n",unit
);
for(trk
=th
.starting_track
; trk
<=th
.ending_track
; trk
++)
trk
= th
.ending_track
- th
.starting_track
+ 1;
for(retry
=0; retry
<300 && trk
>0; retry
++)
if (mcd_getqchan(unit
, &q
) < 0) break;
if (idx
>0 && idx
< MCD_MAXTOCS
&& q
.trk_no
==0)
if (cd
->toc
[idx
].idx_no
== 0)
if (mcd_setmode(unit
, MCD_MD_COOKED
) != 0)
if (trk
!= 0) return ENXIO
;
idx
= th
.ending_track
+ 1;
cd
->toc
[idx
].ctrl_adr
= cd
->toc
[idx
-1].ctrl_adr
;
cd
->toc
[idx
].idx_no
= 0xAA;
cd
->toc
[idx
].hd_pos_msf
[0] = cd
->volinfo
.vol_msf
[0];
cd
->toc
[idx
].hd_pos_msf
[1] = cd
->volinfo
.vol_msf
[1];
cd
->toc
[idx
].hd_pos_msf
[2] = cd
->volinfo
.vol_msf
[2];
static int mcd_toc_entry(int unit
, struct ioc_read_toc_entry
*te
)
struct mcd_data
*cd
= mcd_data
+ unit
;
struct ioc_toc_header th
;
struct ioc_toc_header th
;
/* Make sure we have a valid toc */
if ((rc
=mcd_read_toc(unit
)) != 0)
/* find the toc to copy*/
i
= bcd2bin(cd
->volinfo
.trk_high
) + 1;
/* verify starting track */
if (i
< bcd2bin(cd
->volinfo
.trk_low
) ||
i
> bcd2bin(cd
->volinfo
.trk_high
)+1)
if (te
->data_len
< sizeof(struct ioc_toc_header
) +
sizeof(struct cd_toc_entry
)) return EINVAL
;
/* Copy the toc header */
if (mcd_toc_header(unit
, &th
) < 0) return EIO
;
ret_toc
.rt
.control
= cd
->toc
[i
].ctrl_adr
;
ret_toc
.rt
.addr_type
= te
->address_format
;
if (te
->address_format
== CD_MSF_FORMAT
)
ret_toc
.rt
.addr
[1] = cd
->toc
[i
].hd_pos_msf
[0];
ret_toc
.rt
.addr
[2] = cd
->toc
[i
].hd_pos_msf
[1];
ret_toc
.rt
.addr
[3] = cd
->toc
[i
].hd_pos_msf
[2];
copyout(&ret_toc
, te
->data
, sizeof(struct cd_toc_entry
)
+ sizeof(struct ioc_toc_header
));
static int mcd_stop(int unit
)
struct mcd_data
*cd
= mcd_data
+ unit
;
if (mcd_send(unit
, MCD_CMDSTOPAUDIO
, MCD_RETRYS
) < 0)
cd
->audio_status
= CD_AS_PLAY_COMPLETED
;
static int mcd_getqchan(int unit
, struct mcd_qchninfo
*q
)
struct mcd_data
*cd
= mcd_data
+ unit
;
if (mcd_send(unit
, MCD_CMDGETQCHN
, MCD_RETRYS
) < 0)
if (mcd_get(unit
, (char *) q
, sizeof(struct mcd_qchninfo
)) < 0)
printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
q
->ctrl_adr
, q
->trk_no
, q
->idx_no
,
q
->trk_size_msf
[0], q
->trk_size_msf
[1], q
->trk_size_msf
[2],
q
->trk_size_msf
[0], q
->trk_size_msf
[1], q
->trk_size_msf
[2]);
static int mcd_subchan(int unit
, struct ioc_read_subchannel
*sc
)
struct mcd_data
*cd
= mcd_data
+ unit
;
struct cd_sub_channel_info data
;
printf("mcd%d: subchan af=%d, df=%d\n", unit
,
if (sc
->address_format
!= CD_MSF_FORMAT
) return EIO
;
if (sc
->data_format
!= CD_CURRENT_POSITION
) return EIO
;
if (mcd_getqchan(unit
, &q
) < 0) return EIO
;
data
.header
.audio_status
= cd
->audio_status
;
data
.what
.position
.data_format
= CD_MSF_FORMAT
;
data
.what
.position
.track_number
= bcd2bin(q
.trk_no
);
if (copyout(&data
, sc
->data
, sizeof(struct cd_sub_channel_info
))!=0)
static int mcd_playtracks(int unit
, struct ioc_play_track
*pt
)
struct mcd_data
*cd
= mcd_data
+ unit
;
if ((rc
= mcd_read_toc(unit
)) != 0) return rc
;
printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit
,
a
, pt
->start_index
, z
, pt
->end_index
);
if (a
< cd
->volinfo
.trk_low
|| a
> cd
->volinfo
.trk_high
|| a
> z
||
z
< cd
->volinfo
.trk_low
|| z
> cd
->volinfo
.trk_high
)
pb
.start_msf
[0] = cd
->toc
[a
].hd_pos_msf
[0];
pb
.start_msf
[1] = cd
->toc
[a
].hd_pos_msf
[1];
pb
.start_msf
[2] = cd
->toc
[a
].hd_pos_msf
[2];
pb
.end_msf
[0] = cd
->toc
[z
+1].hd_pos_msf
[0];
pb
.end_msf
[1] = cd
->toc
[z
+1].hd_pos_msf
[1];
pb
.end_msf
[2] = cd
->toc
[z
+1].hd_pos_msf
[2];
return mcd_play(unit
, &pb
);
static int mcd_play(int unit
, struct mcd_read2
*pb
)
struct mcd_data
*cd
= mcd_data
+ unit
;
for(retry
=0; retry
<MCD_RETRYS
; retry
++)
outb(port
+mcd_command
, MCD_CMDREAD2
);
outb(port
+mcd_command
, pb
->start_msf
[0]);
outb(port
+mcd_command
, pb
->start_msf
[1]);
outb(port
+mcd_command
, pb
->start_msf
[2]);
outb(port
+mcd_command
, pb
->end_msf
[0]);
outb(port
+mcd_command
, pb
->end_msf
[1]);
outb(port
+mcd_command
, pb
->end_msf
[2]);
if ((st
=mcd_getstat(unit
, 0)) != -1) break;
printf("mcd%d: mcd_play retry=%d, status=%d\n", unit
, retry
, st
);
if (st
== -1) return ENXIO
;
cd
->audio_status
= CD_AS_PLAY_IN_PROGRESS
;
static int mcd_pause(int unit
)
struct mcd_data
*cd
= mcd_data
+ unit
;
/* Verify current status */
if (cd
->audio_status
!= CD_AS_PLAY_IN_PROGRESS
)
printf("mcd%d: pause attempted when not playing\n", unit
);
/* Get the current position */
if (mcd_getqchan(unit
, &q
) < 0) return EIO
;
/* Copy it into lastpb */
cd
->lastpb
.start_msf
[0] = q
.hd_pos_msf
[0];
cd
->lastpb
.start_msf
[1] = q
.hd_pos_msf
[1];
cd
->lastpb
.start_msf
[2] = q
.hd_pos_msf
[2];
if ((rc
=mcd_stop(unit
)) != 0) return rc
;
/* Set the proper status and exit */
cd
->audio_status
= CD_AS_PLAY_PAUSED
;
static int mcd_resume(int unit
)
struct mcd_data
*cd
= mcd_data
+ unit
;
if (cd
->audio_status
!= CD_AS_PLAY_PAUSED
) return EINVAL
;
return mcd_play(unit
, &cd
->lastpb
);