Home | History | Annotate | Line # | Download | only in isa
mcd.c revision 1.4
      1 /*
      2  * Copyright 1993 by Holger Veit (data part)
      3  * Copyright 1993 by Brian Moore (audio part)
      4  * CHANGES Copyright 1993 Gary Clark II (gclarkii (at) freefall.cdrom.com)
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This software was developed by Holger Veit and Brian Moore
     18  *      for use with "386BSD" and similar operating systems.
     19  *    "Similar operating systems" includes mainly non-profit oriented
     20  *    systems for research and education, including but not restricted to
     21  *    "NetBSD", "FreeBSD", "Mach" (by CMU).
     22  * 4. Neither the name of the developer(s) nor the name "386BSD"
     23  *    may be used to endorse or promote products derived from this
     24  *    software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
     27  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER(S) BE
     30  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
     31  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
     32  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     33  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     34  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     35  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     36  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     37  *
     38  *	$Id: mcd.c,v 1.4 1994/02/06 10:04:47 mycroft Exp $
     39  */
     40 
     41 static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
     42 
     43 #include "mcd.h"
     44 #if NMCD > 0
     45 #include <sys/types.h>
     46 #include <sys/param.h>
     47 #include <sys/systm.h>
     48 #include <sys/conf.h>
     49 #include <sys/file.h>
     50 #include <sys/buf.h>
     51 #include <sys/stat.h>
     52 #include <sys/uio.h>
     53 #include <sys/ioctl.h>
     54 #include <sys/cdio.h>
     55 #include <sys/errno.h>
     56 #include <sys/dkbad.h>
     57 #include <sys/disklabel.h>
     58 
     59 #include <machine/pio.h>
     60 
     61 #include <i386/isa/isa.h>
     62 #include <i386/isa/isa_device.h>
     63 #include <i386/isa/mcdreg.h>
     64 
     65 /* user definable options */
     66 /*#define MCD_TO_WARNING_ON*/	/* define to get timeout messages */
     67 /*#define MCDMINI*/ 		/* define for a mini configuration for boot kernel */
     68 
     69 
     70 #ifdef MCDMINI
     71 #define MCD_TRACE(fmt,a,b,c,d)
     72 #ifdef MCD_TO_WARNING_ON
     73 #undef MCD_TO_WARNING_ON
     74 #endif
     75 #else
     76 #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);}}
     77 #endif
     78 
     79 #define mcd_part(dev)	((minor(dev)) & 7)
     80 #define mcd_unit(dev)	(((minor(dev)) & 0x38) >> 3)
     81 #define mcd_phys(dev)	(((minor(dev)) & 0x40) >> 6)
     82 #define RAW_PART	3
     83 
     84 /* flags */
     85 #define MCDOPEN		0x0001	/* device opened */
     86 #define MCDVALID	0x0002	/* parameters loaded */
     87 #define MCDINIT		0x0004	/* device is init'd */
     88 #define MCDWAIT		0x0008	/* waiting for something */
     89 #define MCDLABEL	0x0010	/* label is read */
     90 #define	MCDPROBING	0x0020	/* probing */
     91 #define	MCDREADRAW	0x0040	/* read raw mode (2352 bytes) */
     92 #define	MCDVOLINFO	0x0080	/* already read volinfo */
     93 #define	MCDTOC		0x0100	/* already read toc */
     94 #define	MCDMBXBSY	0x0200	/* local mbx is busy */
     95 
     96 /* status */
     97 #define	MCDAUDIOBSY	MCD_ST_AUDIOBSY		/* playing audio */
     98 #define MCDDSKCHNG	MCD_ST_DSKCHNG		/* sensed change of disk */
     99 #define MCDDSKIN	MCD_ST_DSKIN		/* sensed disk in drive */
    100 #define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
    101 
    102 /* toc */
    103 #define MCD_MAXTOCS	104	/* from the Linux driver */
    104 #define MCD_LASTPLUS1	170	/* special toc entry */
    105 
    106 struct mcd_mbx {
    107 	short		unit;
    108 	short		port;
    109 	short		retry;
    110 	short		nblk;
    111 	int		sz;
    112 	u_long		skip;
    113 	struct buf	*bp;
    114 	int		p_offset;
    115 	short		count;
    116 };
    117 
    118 struct mcd_data {
    119 	short	config;
    120 	short	flags;
    121 	short	status;
    122 	int	blksize;
    123 	u_long	disksize;
    124 	int	iobase;
    125 	struct disklabel dlabel;
    126 	int	partflags[MAXPARTITIONS];
    127 	int	openflags;
    128 	struct mcd_volinfo volinfo;
    129 #ifndef MCDMINI
    130 	struct mcd_qchninfo toc[MCD_MAXTOCS];
    131 	short	audio_status;
    132 	struct mcd_read2 lastpb;
    133 #endif
    134 	short	debug;
    135 	struct buf head;		/* head of buf queue */
    136 	struct mcd_mbx mbx;
    137 } mcd_data[NMCD];
    138 
    139 /* reader state machine */
    140 #define MCD_S_BEGIN	0
    141 #define MCD_S_BEGIN1	1
    142 #define MCD_S_WAITSTAT	2
    143 #define MCD_S_WAITMODE	3
    144 #define MCD_S_WAITREAD	4
    145 
    146 /* prototypes */
    147 int	mcdopen(dev_t dev);
    148 int	mcdclose(dev_t dev);
    149 int	mcdstrategy(struct buf *bp);
    150 int	mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
    151 int	mcdsize(dev_t dev);
    152 static	void	mcd_done(struct mcd_mbx *mbx);
    153 static	void	mcd_start(int unit);
    154 static	int	mcd_getdisklabel(int unit);
    155 static	void	mcd_configure(struct mcd_data *cd);
    156 static	int	mcd_get(int unit, char *buf, int nmax);
    157 static	void	mcd_setflags(int unit,struct mcd_data *cd);
    158 static	int	mcd_getstat(int unit,int sflg);
    159 static	int	mcd_send(int unit, int cmd,int nretrys);
    160 static	int	bcd2bin(bcd_t b);
    161 static	bcd_t	bin2bcd(int b);
    162 static	void	hsg2msf(int hsg, bcd_t *msf);
    163 static	int	msf2hsg(bcd_t *msf);
    164 static	int	mcd_volinfo(int unit);
    165 static	int	mcd_waitrdy(int port,int dly);
    166 static 	void	mcd_doread(int state, struct mcd_mbx *mbxin);
    167 #ifndef MCDMINI
    168 static	int 	mcd_setmode(int unit, int mode);
    169 static	int	mcd_getqchan(int unit, struct mcd_qchninfo *q);
    170 static	int	mcd_subchan(int unit, struct ioc_read_subchannel *sc);
    171 static	int	mcd_toc_header(int unit, struct ioc_toc_header *th);
    172 static	int	mcd_read_toc(int unit);
    173 static	int	mcd_toc_entry(int unit, struct ioc_read_toc_entry *te);
    174 static	int	mcd_stop(int unit);
    175 static	int	mcd_playtracks(int unit, struct ioc_play_track *pt);
    176 static	int	mcd_play(int unit, struct mcd_read2 *pb);
    177 static	int	mcd_pause(int unit);
    178 static	int	mcd_resume(int unit);
    179 #endif
    180 
    181 extern	int	hz;
    182 extern	int	mcd_probe(struct isa_device *dev);
    183 extern	int	mcd_attach(struct isa_device *dev);
    184 struct	isa_driver	mcddriver = { mcd_probe, mcd_attach, "mcd" };
    185 
    186 #define mcd_put(port,byte)	outb(port,byte)
    187 
    188 #define MCD_RETRYS	5
    189 #define MCD_RDRETRYS	8
    190 
    191 #define MCDBLK	2048	/* for cooked mode */
    192 #define MCDRBLK	2352	/* for raw mode */
    193 
    194 /* several delays */
    195 #define RDELAY_WAITSTAT	300
    196 #define RDELAY_WAITMODE	300
    197 #define RDELAY_WAITREAD	800
    198 
    199 #define DELAY_STATUS	10000l		/* 10000 * 1us */
    200 #define DELAY_GETREPLY	200000l		/* 200000 * 2us */
    201 #define DELAY_SEEKREAD	20000l		/* 20000 * 1us */
    202 #define mcd_delay	DELAY
    203 
    204 int mcd_attach(struct isa_device *dev)
    205 {
    206 	struct mcd_data *cd = mcd_data + dev->id_unit;
    207 	int i;
    208 
    209 	cd->iobase = dev->id_iobase;
    210 	cd->flags |= MCDINIT;
    211 	cd->openflags = 0;
    212 	for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
    213 
    214 #ifdef NOTYET
    215 	/* wire controller for interrupts and dma */
    216 	mcd_configure(cd);
    217 #endif
    218 
    219 	return 1;
    220 }
    221 
    222 int mcdopen(dev_t dev)
    223 {
    224 	int unit,part,phys;
    225 	struct mcd_data *cd;
    226 
    227 	unit = mcd_unit(dev);
    228 	if (unit >= NMCD)
    229 		return ENXIO;
    230 
    231 	cd = mcd_data + unit;
    232 	part = mcd_part(dev);
    233 	phys = mcd_phys(dev);
    234 
    235 	/* not initialized*/
    236 	if (!(cd->flags & MCDINIT))
    237 		return ENXIO;
    238 
    239 	/* invalidated in the meantime? mark all open part's invalid */
    240 	if (!(cd->flags & MCDVALID) && cd->openflags)
    241 		return ENXIO;
    242 
    243 	if (mcd_getstat(unit,1) < 0)
    244 		return ENXIO;
    245 
    246 	/* XXX get a default disklabel */
    247 	mcd_getdisklabel(unit);
    248 
    249 	if (mcdsize(dev) < 0) {
    250 		printf("mcd%d: failed to get disk size\n",unit);
    251 		return ENXIO;
    252 	} else
    253 		cd->flags |= MCDVALID;
    254 
    255 MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
    256 	part,cd->disksize,cd->blksize,0);
    257 
    258 	if (part == RAW_PART ||
    259 		(part < cd->dlabel.d_npartitions &&
    260 		cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
    261 		cd->partflags[part] |= MCDOPEN;
    262 		cd->openflags |= (1<<part);
    263 		if (part == RAW_PART && phys != 0)
    264 			cd->partflags[part] |= MCDREADRAW;
    265 		return 0;
    266 	}
    267 
    268 	return ENXIO;
    269 }
    270 
    271 int mcdclose(dev_t dev)
    272 {
    273 	int unit,part,phys;
    274 	struct mcd_data *cd;
    275 
    276 	unit = mcd_unit(dev);
    277 	if (unit >= NMCD)
    278 		return ENXIO;
    279 
    280 	cd = mcd_data + unit;
    281 	part = mcd_part(dev);
    282 	phys = mcd_phys(dev);
    283 
    284 	if (!(cd->flags & MCDINIT))
    285 		return ENXIO;
    286 
    287 	mcd_getstat(unit,1);	/* get status */
    288 
    289 	/* close channel */
    290 	cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
    291 	cd->openflags &= ~(1<<part);
    292 	MCD_TRACE("close: partition=%d\n",part,0,0,0);
    293 
    294 	return 0;
    295 }
    296 
    297 int mcdstrategy(struct buf *bp)
    298 {
    299 	struct mcd_data *cd;
    300 	struct buf *qp;
    301 	int s;
    302 
    303 	int unit = mcd_unit(bp->b_dev);
    304 
    305 	cd = mcd_data + unit;
    306 
    307 	/* test validity */
    308 /*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
    309 	bp,unit,bp->b_blkno,bp->b_bcount);*/
    310 	if (unit >= NMCD || bp->b_blkno < 0) {
    311 		printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
    312 			unit, bp->b_blkno, bp->b_bcount);
    313 		pg("mcd: mcdstratregy failure");
    314 		bp->b_error = EINVAL;
    315 		bp->b_flags |= B_ERROR;
    316 		goto bad;
    317 	}
    318 
    319 	/* if device invalidated (e.g. media change, door open), error */
    320 	if (!(cd->flags & MCDVALID)) {
    321 MCD_TRACE("strategy: drive not valid\n",0,0,0,0);
    322 		bp->b_error = EIO;
    323 		goto bad;
    324 	}
    325 
    326 	/* read only */
    327 	if (!(bp->b_flags & B_READ)) {
    328 		bp->b_error = EROFS;
    329 		goto bad;
    330 	}
    331 
    332 	/* no data to read */
    333 	if (bp->b_bcount == 0)
    334 		goto done;
    335 
    336 	/* for non raw access, check partition limits */
    337 	if (mcd_part(bp->b_dev) != RAW_PART) {
    338 		if (!(cd->flags & MCDLABEL)) {
    339 			bp->b_error = EIO;
    340 			goto bad;
    341 		}
    342 		/* adjust transfer if necessary */
    343 		if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
    344 			goto done;
    345 		}
    346 	}
    347 
    348 	/* queue it */
    349 	qp = &cd->head;
    350 	s = splbio();
    351 	disksort(qp,bp);
    352 	splx(s);
    353 
    354 	/* now check whether we can perform processing */
    355 	mcd_start(unit);
    356 	return;
    357 
    358 bad:
    359 	bp->b_flags |= B_ERROR;
    360 done:
    361 	bp->b_resid = bp->b_bcount;
    362 	biodone(bp);
    363 	return;
    364 }
    365 
    366 static void mcd_start(int unit)
    367 {
    368 	struct mcd_data *cd = mcd_data + unit;
    369 	struct buf *bp, *qp = &cd->head;
    370 	struct partition *p;
    371 	int part;
    372 	register s = splbio();
    373 
    374 	if (cd->flags & MCDMBXBSY)
    375 		return;
    376 
    377 	if ((bp = qp->b_actf) != 0) {
    378 		/* block found to process, dequeue */
    379 		/*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
    380 		qp->b_actf = bp->b_actf;
    381 		splx(s);
    382 	} else {
    383 		/* nothing to do */
    384 		splx(s);
    385 		return;
    386 	}
    387 
    388 	/* changed media? */
    389 	if (!(cd->flags	& MCDVALID)) {
    390 		MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
    391 		return;
    392 	}
    393 
    394 	p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
    395 
    396 	cd->flags |= MCDMBXBSY;
    397 	cd->mbx.unit = unit;
    398 	cd->mbx.port = cd->iobase;
    399 	cd->mbx.retry = MCD_RETRYS;
    400 	cd->mbx.bp = bp;
    401 	cd->mbx.p_offset = p->p_offset;
    402 
    403 	/* calling the read routine */
    404 	mcd_doread(MCD_S_BEGIN,&(cd->mbx));
    405 	/* triggers mcd_start, when successful finished */
    406 	return;
    407 }
    408 
    409 int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
    410 {
    411 	struct mcd_data *cd;
    412 	int unit,part;
    413 
    414 	unit = mcd_unit(dev);
    415 	part = mcd_part(dev);
    416 	cd = mcd_data + unit;
    417 
    418 #ifdef MCDMINI
    419 	return ENOTTY;
    420 #else
    421 	if (!(cd->flags & MCDVALID))
    422 		return EIO;
    423 MCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
    424 
    425 	switch (cmd) {
    426 	case DIOCSBAD:
    427 		return EINVAL;
    428 	case DIOCGDINFO:
    429 	case DIOCGPART:
    430 	case DIOCWDINFO:
    431 	case DIOCSDINFO:
    432 	case DIOCWLABEL:
    433 		return ENOTTY;
    434 	case CDIOCPLAYTRACKS:
    435 		return mcd_playtracks(unit, (struct ioc_play_track *) addr);
    436 	case CDIOCPLAYBLOCKS:
    437 		return mcd_play(unit, (struct mcd_read2 *) addr);
    438 	case CDIOCREADSUBCHANNEL:
    439 		return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
    440 	case CDIOREADTOCHEADER:
    441 		return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
    442 	case CDIOREADTOCENTRYS:
    443 		return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr);
    444 	case CDIOCSETPATCH:
    445 	case CDIOCGETVOL:
    446 	case CDIOCSETVOL:
    447 	case CDIOCSETMONO:
    448 	case CDIOCSETSTERIO:
    449 	case CDIOCSETMUTE:
    450 	case CDIOCSETLEFT:
    451 	case CDIOCSETRIGHT:
    452 		return EINVAL;
    453 	case CDIOCRESUME:
    454 		return mcd_resume(unit);
    455 	case CDIOCPAUSE:
    456 		return mcd_pause(unit);
    457 	case CDIOCSTART:
    458 		return EINVAL;
    459 	case CDIOCSTOP:
    460 		return mcd_stop(unit);
    461 	case CDIOCEJECT:
    462 		return EINVAL;
    463 	case CDIOCSETDEBUG:
    464 		cd->debug = 1;
    465 		return 0;
    466 	case CDIOCCLRDEBUG:
    467 		cd->debug = 0;
    468 		return 0;
    469 	case CDIOCRESET:
    470 		return EINVAL;
    471 	default:
    472 		return ENOTTY;
    473 	}
    474 	/*NOTREACHED*/
    475 #endif /*!MCDMINI*/
    476 }
    477 
    478 /* this could have been taken from scsi/cd.c, but it is not clear
    479  * whether the scsi cd driver is linked in
    480  */
    481 static int mcd_getdisklabel(int unit)
    482 {
    483 	struct mcd_data *cd = mcd_data + unit;
    484 
    485 	if (cd->flags & MCDLABEL)
    486 		return -1;
    487 
    488 	bzero(&cd->dlabel,sizeof(struct disklabel));
    489 	strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16);
    490 	strncpy(cd->dlabel.d_packname,"unknown        ",16);
    491 	cd->dlabel.d_secsize 	= cd->blksize;
    492 	cd->dlabel.d_nsectors	= 100;
    493 	cd->dlabel.d_ntracks	= 1;
    494 	cd->dlabel.d_ncylinders	= (cd->disksize/100)+1;
    495 	cd->dlabel.d_secpercyl	= 100;
    496 	cd->dlabel.d_secperunit	= cd->disksize;
    497 	cd->dlabel.d_rpm	= 300;
    498 	cd->dlabel.d_interleave	= 1;
    499 	cd->dlabel.d_flags	= D_REMOVABLE;
    500 	cd->dlabel.d_npartitions= 1;
    501 	cd->dlabel.d_partitions[0].p_offset = 0;
    502 	cd->dlabel.d_partitions[0].p_size = cd->disksize;
    503 	cd->dlabel.d_partitions[0].p_fstype = 9;
    504 	cd->dlabel.d_magic	= DISKMAGIC;
    505 	cd->dlabel.d_magic2	= DISKMAGIC;
    506 	cd->dlabel.d_checksum	= dkcksum(&cd->dlabel);
    507 
    508 	cd->flags |= MCDLABEL;
    509 	return 0;
    510 }
    511 
    512 int mcdsize(dev_t dev)
    513 {
    514 	int size;
    515 	int unit = mcd_unit(dev);
    516 	struct mcd_data *cd = mcd_data + unit;
    517 
    518 	if (mcd_volinfo(unit) >= 0) {
    519 		cd->blksize = MCDBLK;
    520 		size = msf2hsg(cd->volinfo.vol_msf);
    521 		cd->disksize = size * (MCDBLK/DEV_BSIZE);
    522 		return 0;
    523 	}
    524 	return -1;
    525 }
    526 
    527 /***************************************************************
    528  * lower level of driver starts here
    529  **************************************************************/
    530 
    531 #ifdef NOTDEF
    532 static char irqs[] = {
    533 	0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
    534 	0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
    535 };
    536 
    537 static char drqs[] = {
    538 	0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
    539 };
    540 #endif
    541 
    542 static void mcd_configure(struct mcd_data *cd)
    543 {
    544 	outb(cd->iobase+mcd_config,cd->config);
    545 }
    546 
    547 /* check if there is a cdrom */
    548 /* Heavly hacked by gclarkii (at) sugar.neosoft.com */
    549 
    550 int mcd_probe(struct isa_device *dev)
    551 {
    552 	int port = dev->id_iobase;
    553 	int unit = dev->id_unit;
    554 	int i;
    555 	int st;
    556 	int check;
    557 	int junk;
    558 
    559 	mcd_data[unit].flags = MCDPROBING;
    560 
    561 #ifdef NOTDEF
    562 	/* get irq/drq configuration word */
    563 	mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
    564 #else
    565 	mcd_data[unit].config = 0;
    566 #endif
    567 
    568 	/* send a reset */
    569 	outb(port+MCD_FLAGS,0);
    570 	DELAY(100000);
    571 	/* get any pending status and throw away...*/
    572 	for (i=10; i != 0; i--) {
    573 		inb(port+MCD_DATA);
    574 	}
    575 	DELAY(1000);
    576 
    577 	outb(port+MCD_DATA,MCD_CMDGETSTAT);	/* Send get status command */
    578 
    579 	/* Loop looking for avail of status */
    580 	/* XXX May have to increase for fast machinces */
    581 	for (i = 1000; i != 0; i--) {
    582 		if ((inb(port+MCD_FLAGS) & 0xF ) == STATUS_AVAIL) {
    583 			break;
    584 		}
    585 		DELAY(10);
    586 	}
    587 	/* get status */
    588 
    589 	if (i == 0) {
    590 #ifdef DEBUG
    591 		printf ("Mitsumi drive NOT detected\n");
    592 #endif
    593 	return 0;
    594 	}
    595 
    596 /*
    597  * The following code uses the 0xDC command, it returns a M from the
    598  * second byte and a number in the third.  Does anyone know what the
    599  * number is for? Better yet, how about someone thats REAL good in
    600  * i80x86 asm looking at the Dos driver... Most of this info came
    601  * from a friend of mine spending a whole weekend.....
    602  */
    603 
    604 	DELAY (2000);
    605 	outb(port+MCD_DATA,MCD_CMDCONTINFO);
    606 	for (i = 0; i < 100000; i++) {
    607 		if ((inb(port+MCD_FLAGS) & 0xF) == STATUS_AVAIL)
    608 			break;
    609 	}
    610 	if (i > 100000) {
    611 #ifdef DEBUG
    612 		printf ("Mitsumi drive error\n");
    613 #endif
    614 		return 0;
    615 	}
    616 	DELAY (40000);
    617 	st = inb(port+MCD_DATA);
    618 	DELAY (500);
    619 	check = inb(port+MCD_DATA);
    620 	DELAY (500);
    621 	junk = inb(port+MCD_DATA);	/* What is byte used for?!?!? */
    622 
    623 	if (check = 'M') {
    624 #ifdef DEBUG
    625 		printf("Mitsumi drive detected\n");
    626 #endif
    627 		return 4;
    628 	} else {
    629 		printf("Mitsumi drive NOT detected\n");
    630 		printf("Mitsumi drive error\n");
    631 		return 0;
    632 	}
    633 }
    634 
    635 static int mcd_waitrdy(int port,int dly)
    636 {
    637 	int i;
    638 
    639 	/* wait until xfer port senses data ready */
    640 	for (i=0; i<dly; i++) {
    641 		if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0)
    642 			return 0;
    643 		mcd_delay(1);
    644 	}
    645 	return -1;
    646 }
    647 
    648 static int mcd_getreply(int unit,int dly)
    649 {
    650 	int	i;
    651 	struct	mcd_data *cd = mcd_data + unit;
    652 	int	port = cd->iobase;
    653 
    654 	/* wait data to become ready */
    655 	if (mcd_waitrdy(port,dly)<0) {
    656 #ifdef MCD_TO_WARNING_ON
    657 		printf("mcd%d: timeout getreply\n",unit);
    658 #endif
    659 		return -1;
    660 	}
    661 
    662 	/* get the data */
    663 	return inb(port+mcd_status) & 0xFF;
    664 }
    665 
    666 static int mcd_getstat(int unit,int sflg)
    667 {
    668 	int	i;
    669 	struct	mcd_data *cd = mcd_data + unit;
    670 	int	port = cd->iobase;
    671 
    672 	/* get the status */
    673 	if (sflg)
    674 		outb(port+mcd_command, MCD_CMDGETSTAT);
    675 	i = mcd_getreply(unit,DELAY_GETREPLY);
    676 	if (i<0) return -1;
    677 
    678 	cd->status = i;
    679 
    680 	mcd_setflags(unit,cd);
    681 	return cd->status;
    682 }
    683 
    684 static void mcd_setflags(int unit, struct mcd_data *cd)
    685 {
    686 	/* check flags */
    687 	if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
    688 		MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
    689 		cd->flags &= ~MCDVALID;
    690 	}
    691 
    692 #ifndef MCDMINI
    693 	if (cd->status & MCDAUDIOBSY)
    694 		cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
    695 	else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
    696 		cd->audio_status = CD_AS_PLAY_COMPLETED;
    697 #endif
    698 }
    699 
    700 static int mcd_get(int unit, char *buf, int nmax)
    701 {
    702 	int port = mcd_data[unit].iobase;
    703 	int i,k;
    704 
    705 	for (i=0; i<nmax; i++) {
    706 
    707 		/* wait for data */
    708 		if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
    709 #ifdef MCD_TO_WARNING_ON
    710 			printf("mcd%d: timeout mcd_get\n",unit);
    711 #endif
    712 			return -1;
    713 		}
    714 		buf[i] = k;
    715 	}
    716 	return i;
    717 }
    718 
    719 static int mcd_send(int unit, int cmd,int nretrys)
    720 {
    721 	int i,k;
    722 	int port = mcd_data[unit].iobase;
    723 
    724 /*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
    725 	for (i=0; i<nretrys; i++) {
    726 		outb(port+mcd_command, cmd);
    727 		if ((k=mcd_getstat(unit,0)) != -1)
    728 			break;
    729 	}
    730 	if (i == nretrys) {
    731 		printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
    732 		return -1;
    733 	}
    734 /*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
    735 	return 0;
    736 }
    737 
    738 static int bcd2bin(bcd_t b)
    739 {
    740 	return (b >> 4) * 10 + (b & 15);
    741 }
    742 
    743 static bcd_t bin2bcd(int b)
    744 {
    745 	return ((b / 10) << 4) | (b % 10);
    746 }
    747 
    748 static void hsg2msf(int hsg, bcd_t *msf)
    749 {
    750 	hsg += 150;
    751 	M_msf(msf) = bin2bcd(hsg / 4500);
    752 	hsg %= 4500;
    753 	S_msf(msf) = bin2bcd(hsg / 75);
    754 	F_msf(msf) = bin2bcd(hsg % 75);
    755 }
    756 
    757 static int msf2hsg(bcd_t *msf)
    758 {
    759 	return (bcd2bin(M_msf(msf)) * 60 +
    760 		bcd2bin(S_msf(msf))) * 75 +
    761 		bcd2bin(F_msf(msf)) - 150;
    762 }
    763 
    764 static int mcd_volinfo(int unit)
    765 {
    766 	struct mcd_data *cd = mcd_data + unit;
    767 	int i;
    768 
    769 /*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
    770 
    771 	/* Get the status, in case the disc has been changed */
    772 	if (mcd_getstat(unit, 1) < 0) return EIO;
    773 
    774 	/* Just return if we already have it */
    775 	if (cd->flags & MCDVOLINFO) return 0;
    776 
    777 	/* send volume info command */
    778 	if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
    779 		return -1;
    780 
    781 	/* get data */
    782 	if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
    783 		printf("mcd%d: mcd_volinfo: error read data\n",unit);
    784 		return -1;
    785 	}
    786 
    787 	if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
    788 		cd->flags |= MCDVOLINFO;	/* volinfo is OK */
    789 		return 0;
    790 	}
    791 
    792 	return -1;
    793 }
    794 
    795 int mcdintr(unit)
    796 {
    797 	int	port = mcd_data[unit].iobase;
    798 	u_int	i;
    799 
    800 	MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0);
    801 
    802 	/* just read out status and ignore the rest */
    803 	if ((inb(port+mcd_xfer)&0xFF) != 0xFF) {
    804 		i = inb(port+mcd_status);
    805 	}
    806 }
    807 
    808 /* state machine to process read requests
    809  * initialize with MCD_S_BEGIN: calculate sizes, and read status
    810  * MCD_S_WAITSTAT: wait for status reply, set mode
    811  * MCD_S_WAITMODE: waits for status reply from set mode, set read command
    812  * MCD_S_WAITREAD: wait for read ready, read data
    813  */
    814 static struct mcd_mbx *mbxsave;
    815 
    816 static void mcd_doread(int state, struct mcd_mbx *mbxin)
    817 {
    818 	struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
    819 	int	unit = mbx->unit;
    820 	int	port = mbx->port;
    821 	struct	buf *bp = mbx->bp;
    822 	struct	mcd_data *cd = mcd_data + unit;
    823 
    824 	int	rm,i,k;
    825 	struct mcd_read2 rbuf;
    826 	int	blknum;
    827 	caddr_t	addr;
    828 
    829 loop:
    830 	switch (state) {
    831 	case MCD_S_BEGIN:
    832 		mbx = mbxsave = mbxin;
    833 
    834 	case MCD_S_BEGIN1:
    835 		/* get status */
    836 		outb(port+mcd_command, MCD_CMDGETSTAT);
    837 		mbx->count = RDELAY_WAITSTAT;
    838 		timeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITSTAT,hz/100);
    839 		return;
    840 	case MCD_S_WAITSTAT:
    841 		untimeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITSTAT);
    842 		if (mbx->count-- >= 0) {
    843 			if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
    844 				timeout((timeout_t) mcd_doread,
    845 				    (caddr_t) MCD_S_WAITSTAT,hz/100);
    846 				return;
    847 			}
    848 			mcd_setflags(unit,cd);
    849 			MCD_TRACE("got WAITSTAT delay=%d\n",RDELAY_WAITSTAT-mbx->count,0,0,0);
    850 			/* reject, if audio active */
    851 			if (cd->status & MCDAUDIOBSY) {
    852 				printf("mcd%d: audio is active\n",unit);
    853 				goto readerr;
    854 			}
    855 
    856 			/* to check for raw/cooked mode */
    857 			if (cd->flags & MCDREADRAW) {
    858 				rm = MCD_MD_RAW;
    859 				mbx->sz = MCDRBLK;
    860 			} else {
    861 				rm = MCD_MD_COOKED;
    862 				mbx->sz = cd->blksize;
    863 			}
    864 
    865 			mbx->count = RDELAY_WAITMODE;
    866 
    867 			mcd_put(port+mcd_command, MCD_CMDSETMODE);
    868 			mcd_put(port+mcd_command, rm);
    869 			timeout((timeout_t) mcd_doread,
    870 			    (caddr_t) MCD_S_WAITMODE,hz/100);
    871 			return;
    872 		} else {
    873 #ifdef MCD_TO_WARNING_ON
    874 			printf("mcd%d: timeout getstatus\n",unit);
    875 #endif
    876 			goto readerr;
    877 		}
    878 
    879 	case MCD_S_WAITMODE:
    880 		untimeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITMODE);
    881 		if (mbx->count-- < 0) {
    882 #ifdef MCD_TO_WARNING_ON
    883 			printf("mcd%d: timeout set mode\n",unit);
    884 #endif
    885 			goto readerr;
    886 		}
    887 		if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
    888 			timeout((timeout_t) mcd_doread,
    889 			    (caddr_t) MCD_S_WAITMODE,hz/100);
    890 			return;
    891 		}
    892 		mcd_setflags(unit,cd);
    893 		MCD_TRACE("got WAITMODE delay=%d\n",RDELAY_WAITMODE-mbx->count,0,0,0);
    894 		/* for first block */
    895 		mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
    896 		mbx->skip = 0;
    897 
    898 nextblock:
    899 		blknum 	= (bp->b_blkno / (mbx->sz/DEV_BSIZE))
    900 			+ mbx->p_offset + mbx->skip/mbx->sz;
    901 
    902 		MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",blknum,bp,0,0);
    903 
    904 		/* build parameter block */
    905 		hsg2msf(blknum,rbuf.start_msf);
    906 
    907 		/* send the read command */
    908 		mcd_put(port+mcd_command,MCD_CMDREAD2);
    909 		mcd_put(port+mcd_command,rbuf.start_msf[0]);
    910 		mcd_put(port+mcd_command,rbuf.start_msf[1]);
    911 		mcd_put(port+mcd_command,rbuf.start_msf[2]);
    912 		mcd_put(port+mcd_command,0);
    913 		mcd_put(port+mcd_command,0);
    914 		mcd_put(port+mcd_command,1);
    915 		mbx->count = RDELAY_WAITREAD;
    916 		timeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITREAD,hz/100);
    917 		return;
    918 	case MCD_S_WAITREAD:
    919 		untimeout((timeout_t) mcd_doread,(caddr_t) MCD_S_WAITREAD);
    920 		if (mbx->count-- > 0) {
    921 			k = inb(port+mcd_xfer);
    922 			if ((k & 2)==0) {
    923 			MCD_TRACE("got data delay=%d\n",RDELAY_WAITREAD-mbx->count,0,0,0);
    924 				/* data is ready */
    925 				addr	= bp->b_un.b_addr + mbx->skip;
    926 				outb(port+mcd_ctl2,0x04);	/* XXX */
    927 				for (i=0; i<mbx->sz; i++)
    928 					*addr++	= inb(port+mcd_rdata);
    929 				outb(port+mcd_ctl2,0x0c);	/* XXX */
    930 
    931 				if (--mbx->nblk > 0) {
    932 					mbx->skip += mbx->sz;
    933 					goto nextblock;
    934 				}
    935 
    936 				/* return buffer */
    937 				bp->b_resid = 0;
    938 				biodone(bp);
    939 
    940 				cd->flags &= ~MCDMBXBSY;
    941 				mcd_start(mbx->unit);
    942 				return;
    943 			}
    944 			if ((k & 4)==0)
    945 				mcd_getstat(unit,0);
    946 			timeout((timeout_t) mcd_doread,
    947 			    (caddr_t) MCD_S_WAITREAD,hz/100);
    948 			return;
    949 		} else {
    950 #ifdef MCD_TO_WARNING_ON
    951 			printf("mcd%d: timeout read data\n",unit);
    952 #endif
    953 			goto readerr;
    954 		}
    955 	}
    956 
    957 readerr:
    958 	if (mbx->retry-- > 0) {
    959 #ifdef MCD_TO_WARNING_ON
    960 		printf("mcd%d: retrying\n",unit);
    961 #endif
    962 		state = MCD_S_BEGIN1;
    963 		goto loop;
    964 	}
    965 
    966 	/* invalidate the buffer */
    967 	bp->b_flags |= B_ERROR;
    968 	bp->b_resid = bp->b_bcount;
    969 	biodone(bp);
    970 	mcd_start(mbx->unit);
    971 	return;
    972 
    973 #ifdef NOTDEF
    974 	printf("mcd%d: unit timeout, resetting\n",mbx->unit);
    975 	outb(mbx->port+mcd_reset,MCD_CMDRESET);
    976 	DELAY(300000);
    977 	(void)mcd_getstat(mbx->unit,1);
    978 	(void)mcd_getstat(mbx->unit,1);
    979 	/*cd->status &= ~MCDDSKCHNG; */
    980 	cd->debug = 1; /* preventive set debug mode */
    981 
    982 #endif
    983 
    984 }
    985 
    986 #ifndef MCDMINI
    987 static int mcd_setmode(int unit, int mode)
    988 {
    989 	struct mcd_data *cd = mcd_data + unit;
    990 	int port = cd->iobase;
    991 	int retry;
    992 
    993 	printf("mcd%d: setting mode to %d\n", unit, mode);
    994 	for(retry=0; retry<MCD_RETRYS; retry++)
    995 	{
    996 		outb(port+mcd_command, MCD_CMDSETMODE);
    997 		outb(port+mcd_command, mode);
    998 		if (mcd_getstat(unit, 0) != -1) return 0;
    999 	}
   1000 
   1001 	return -1;
   1002 }
   1003 
   1004 static int mcd_toc_header(int unit, struct ioc_toc_header *th)
   1005 {
   1006 	struct mcd_data *cd = mcd_data + unit;
   1007 
   1008 	if (mcd_volinfo(unit) < 0)
   1009 		return ENXIO;
   1010 
   1011 	th->len = msf2hsg(cd->volinfo.vol_msf);
   1012 	th->starting_track = bcd2bin(cd->volinfo.trk_low);
   1013 	th->ending_track = bcd2bin(cd->volinfo.trk_high);
   1014 
   1015 	return 0;
   1016 }
   1017 
   1018 static int mcd_read_toc(int unit)
   1019 {
   1020 	struct mcd_data *cd = mcd_data + unit;
   1021 	struct ioc_toc_header th;
   1022 	struct mcd_qchninfo q;
   1023 	int rc, trk, idx, retry;
   1024 
   1025 	/* Only read TOC if needed */
   1026 	if (cd->flags & MCDTOC) return 0;
   1027 
   1028 	printf("mcd%d: reading toc header\n", unit);
   1029 	if (mcd_toc_header(unit, &th) != 0)
   1030 		return ENXIO;
   1031 
   1032 	printf("mcd%d: stopping play\n", unit);
   1033 	if ((rc=mcd_stop(unit)) != 0)
   1034 		return rc;
   1035 
   1036 	/* try setting the mode twice */
   1037 	if (mcd_setmode(unit, MCD_MD_TOC) != 0)
   1038 		return EIO;
   1039 	if (mcd_setmode(unit, MCD_MD_TOC) != 0)
   1040 		return EIO;
   1041 
   1042 	printf("mcd%d: get_toc reading qchannel info\n",unit);
   1043 	for(trk=th.starting_track; trk<=th.ending_track; trk++)
   1044 		cd->toc[trk].idx_no = 0;
   1045 	trk = th.ending_track - th.starting_track + 1;
   1046 	for(retry=0; retry<300 && trk>0; retry++)
   1047 	{
   1048 		if (mcd_getqchan(unit, &q) < 0) break;
   1049 		idx = bcd2bin(q.idx_no);
   1050 		if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0)
   1051 			if (cd->toc[idx].idx_no == 0)
   1052 			{
   1053 				cd->toc[idx] = q;
   1054 				trk--;
   1055 			}
   1056 	}
   1057 
   1058 	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
   1059 		return EIO;
   1060 
   1061 	if (trk != 0) return ENXIO;
   1062 
   1063 	/* add a fake last+1 */
   1064 	idx = th.ending_track + 1;
   1065 	cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
   1066 	cd->toc[idx].trk_no = 0;
   1067 	cd->toc[idx].idx_no = 0xAA;
   1068 	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
   1069 	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
   1070 	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
   1071 
   1072 	cd->flags |= MCDTOC;
   1073 
   1074 	return 0;
   1075 }
   1076 
   1077 static int mcd_toc_entry(int unit, struct ioc_read_toc_entry *te)
   1078 {
   1079 	struct mcd_data *cd = mcd_data + unit;
   1080 	struct ret_toc
   1081 	{
   1082 		struct ioc_toc_header th;
   1083 		struct cd_toc_entry rt;
   1084 	} ret_toc;
   1085 	struct ioc_toc_header th;
   1086 	int rc, i;
   1087 
   1088 	/* Make sure we have a valid toc */
   1089 	if ((rc=mcd_read_toc(unit)) != 0)
   1090 		return rc;
   1091 
   1092 	/* find the toc to copy*/
   1093 	i = te->starting_track;
   1094 	if (i == MCD_LASTPLUS1)
   1095 		i = bcd2bin(cd->volinfo.trk_high) + 1;
   1096 
   1097 	/* verify starting track */
   1098 	if (i < bcd2bin(cd->volinfo.trk_low) ||
   1099 		i > bcd2bin(cd->volinfo.trk_high)+1)
   1100 		return EINVAL;
   1101 
   1102 	/* do we have room */
   1103 	if (te->data_len < sizeof(struct ioc_toc_header) +
   1104 		sizeof(struct cd_toc_entry)) return EINVAL;
   1105 
   1106 	/* Copy the toc header */
   1107 	if (mcd_toc_header(unit, &th) < 0) return EIO;
   1108 	ret_toc.th = th;
   1109 
   1110 	/* copy the toc data */
   1111 	ret_toc.rt.control = cd->toc[i].ctrl_adr;
   1112 	ret_toc.rt.addr_type = te->address_format;
   1113 	ret_toc.rt.track = i;
   1114 	if (te->address_format == CD_MSF_FORMAT)
   1115 	{
   1116 		ret_toc.rt.addr[1] = cd->toc[i].hd_pos_msf[0];
   1117 		ret_toc.rt.addr[2] = cd->toc[i].hd_pos_msf[1];
   1118 		ret_toc.rt.addr[3] = cd->toc[i].hd_pos_msf[2];
   1119 	}
   1120 
   1121 	/* copy the data back */
   1122 	copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
   1123 		+ sizeof(struct ioc_toc_header));
   1124 
   1125 	return 0;
   1126 }
   1127 
   1128 static int mcd_stop(int unit)
   1129 {
   1130 	struct mcd_data *cd = mcd_data + unit;
   1131 
   1132 	if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0)
   1133 		return ENXIO;
   1134 	cd->audio_status = CD_AS_PLAY_COMPLETED;
   1135 	return 0;
   1136 }
   1137 
   1138 static int mcd_getqchan(int unit, struct mcd_qchninfo *q)
   1139 {
   1140 	struct mcd_data *cd = mcd_data + unit;
   1141 
   1142 	if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0)
   1143 		return -1;
   1144 	if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0)
   1145 		return -1;
   1146 	if (cd->debug)
   1147 	printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
   1148 		unit,
   1149 		q->ctrl_adr, q->trk_no, q->idx_no,
   1150 		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
   1151 		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
   1152 	return 0;
   1153 }
   1154 
   1155 static int mcd_subchan(int unit, struct ioc_read_subchannel *sc)
   1156 {
   1157 	struct mcd_data *cd = mcd_data + unit;
   1158 	struct mcd_qchninfo q;
   1159 	struct cd_sub_channel_info data;
   1160 
   1161 	printf("mcd%d: subchan af=%d, df=%d\n", unit,
   1162 		sc->address_format,
   1163 		sc->data_format);
   1164 	if (sc->address_format != CD_MSF_FORMAT) return EIO;
   1165 	if (sc->data_format != CD_CURRENT_POSITION) return EIO;
   1166 
   1167 	if (mcd_getqchan(unit, &q) < 0) return EIO;
   1168 
   1169 	data.header.audio_status = cd->audio_status;
   1170 	data.what.position.data_format = CD_MSF_FORMAT;
   1171 	data.what.position.track_number = bcd2bin(q.trk_no);
   1172 
   1173 	if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0)
   1174 		return EFAULT;
   1175 	return 0;
   1176 }
   1177 
   1178 static int mcd_playtracks(int unit, struct ioc_play_track *pt)
   1179 {
   1180 	struct mcd_data *cd = mcd_data + unit;
   1181 	struct mcd_read2 pb;
   1182 	int a = pt->start_track;
   1183 	int z = pt->end_track;
   1184 	int rc;
   1185 
   1186 	if ((rc = mcd_read_toc(unit)) != 0) return rc;
   1187 
   1188 	printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
   1189 		a, pt->start_index, z, pt->end_index);
   1190 
   1191 	if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
   1192 		z < cd->volinfo.trk_low || z > cd->volinfo.trk_high)
   1193 		return EINVAL;
   1194 
   1195 	pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
   1196 	pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
   1197 	pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
   1198 	pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
   1199 	pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
   1200 	pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
   1201 
   1202 	return mcd_play(unit, &pb);
   1203 }
   1204 
   1205 static int mcd_play(int unit, struct mcd_read2 *pb)
   1206 {
   1207 	struct mcd_data *cd = mcd_data + unit;
   1208 	int port = cd->iobase;
   1209 	int retry, st;
   1210 
   1211 	cd->lastpb = *pb;
   1212 	for(retry=0; retry<MCD_RETRYS; retry++)
   1213 	{
   1214 		outb(port+mcd_command, MCD_CMDREAD2);
   1215 		outb(port+mcd_command, pb->start_msf[0]);
   1216 		outb(port+mcd_command, pb->start_msf[1]);
   1217 		outb(port+mcd_command, pb->start_msf[2]);
   1218 		outb(port+mcd_command, pb->end_msf[0]);
   1219 		outb(port+mcd_command, pb->end_msf[1]);
   1220 		outb(port+mcd_command, pb->end_msf[2]);
   1221 		if ((st=mcd_getstat(unit, 0)) != -1) break;
   1222 	}
   1223 
   1224 	if (cd->debug)
   1225 	printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st);
   1226 	if (st == -1) return ENXIO;
   1227 	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
   1228 	return 0;
   1229 }
   1230 
   1231 static int mcd_pause(int unit)
   1232 {
   1233 	struct mcd_data *cd = mcd_data + unit;
   1234 	struct mcd_qchninfo q;
   1235 	int rc;
   1236 
   1237 	/* Verify current status */
   1238 	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS)
   1239 	{
   1240 		printf("mcd%d: pause attempted when not playing\n", unit);
   1241 		return EINVAL;
   1242 	}
   1243 
   1244 	/* Get the current position */
   1245 	if (mcd_getqchan(unit, &q) < 0) return EIO;
   1246 
   1247 	/* Copy it into lastpb */
   1248 	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
   1249 	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
   1250 	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
   1251 
   1252 	/* Stop playing */
   1253 	if ((rc=mcd_stop(unit)) != 0) return rc;
   1254 
   1255 	/* Set the proper status and exit */
   1256 	cd->audio_status = CD_AS_PLAY_PAUSED;
   1257 	return 0;
   1258 }
   1259 
   1260 static int mcd_resume(int unit)
   1261 {
   1262 	struct mcd_data *cd = mcd_data + unit;
   1263 
   1264 	if (cd->audio_status != CD_AS_PLAY_PAUSED) return EINVAL;
   1265 	return mcd_play(unit, &cd->lastpb);
   1266 }
   1267 #endif /*!MCDMINI*/
   1268 
   1269 #endif /* NMCD > 0 */
   1270