Home | History | Annotate | Line # | Download | only in isa
mcd.c revision 1.1.2.1
      1 /*
      2  * Copyright (c) 1993 Charles Hannum.
      3  * Copyright 1993 by Holger Veit (data part)
      4  * Copyright 1993 by Brian Moore (audio part)
      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.1.2.1 1993/11/29 18:19:12 mycroft Exp $
     39  */
     40 /*static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";*/
     41 
     42 #include "mcd.h"
     43 
     44 #include <sys/types.h>
     45 #include <sys/param.h>
     46 #include <sys/systm.h>
     47 #include <sys/kernel.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 #include <sys/disk.h>
     59 #include <sys/device.h>
     60 
     61 #include <i386/isa/isavar.h>
     62 #include <i386/isa/icu.h>
     63 
     64 #include <i386/isa/mcdreg.h>
     65 
     66 #ifndef MCDDEBUG
     67 #define MCD_TRACE(fmt,a,b,c,d)
     68 #else
     69 #define MCD_TRACE(fmt,a,b,c,d)	{if (cd->debug) {printf("%s: st=%02x: ", cd->sc_dev.dv_xname, mcd->status); printf(fmt,a,b,c,d);}}
     70 #endif
     71 
     72 #define MCDPART(dev)	(((minor(dev)) & 0x07)     )
     73 #define MCDUNIT(dev)	(((minor(dev)) & 0x78) >> 3)
     74 #define MCDPHYS(dev)	(((minor(dev)) & 0x80) >> 7)
     75 #define RAW_PART	3
     76 
     77 /* flags */
     78 #define MCDOPEN		0x0001	/* device opened */
     79 #define MCDVALID	0x0002	/* parameters loaded */
     80 #define MCDINIT		0x0004	/* device is init'd */
     81 #define MCDWAIT		0x0008	/* waiting for something */
     82 #define MCDLABEL	0x0010	/* label is read */
     83 #define	MCDREADRAW	0x0020	/* read raw mode (2352 bytes) */
     84 #define	MCDVOLINFO	0x0040	/* already read volinfo */
     85 #define	MCDTOC		0x0080	/* already read toc */
     86 #define	MCDMBXBSY	0x0100	/* local mbx is busy */
     87 
     88 /* status */
     89 #define	MCDAUDIOBSY	MCD_ST_AUDIOBSY		/* playing audio */
     90 #define MCDDSKCHNG	MCD_ST_DSKCHNG		/* sensed change of disk */
     91 #define MCDDSKIN	MCD_ST_DSKIN		/* sensed disk in drive */
     92 #define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
     93 
     94 /* toc */
     95 #define MCD_MAXTOCS	104	/* from the Linux driver */
     96 #define MCD_LASTPLUS1	170	/* special toc entry */
     97 
     98 struct mcd_mbx {
     99 	short		unit;
    100 	u_short		iobase;
    101 	short		retry;
    102 	short		nblk;
    103 	int		sz;
    104 	u_long		skip;
    105 	struct buf	*bp;
    106 	int		p_offset;
    107 	short		count;
    108 };
    109 
    110 struct mcd_data {
    111 	struct	device sc_dev;
    112 	struct	isadev sc_id;
    113 	struct	intrhand sc_ih;
    114 	struct	dkdevice sc_dk;
    115 
    116 	short	config;
    117 	short	flags;
    118 	short	status;
    119 	int	blksize;
    120 	u_long	disksize;
    121 	int	iobase;
    122 	struct disklabel dlabel;
    123 	int	partflags[MAXPARTITIONS];
    124 	int	openflags;
    125 	struct mcd_volinfo volinfo;
    126 	struct mcd_qchninfo toc[MCD_MAXTOCS];
    127 	short	audio_status;
    128 	struct mcd_read2 lastpb;
    129 	short	debug;
    130 	struct buf head;		/* head of buf queue */
    131 	struct mcd_mbx mbx;
    132 };
    133 
    134 int mcdprobe __P((struct device *, struct cfdata *, void *));
    135 void mcdattach __P((struct device *, struct device *, void *));
    136 
    137 struct cfdriver mcdcd =
    138 { NULL, "mcd", mcdprobe, mcdattach, DV_DISK, sizeof(struct mcd_data) };
    139 
    140 void mcdstrategy __P((struct buf *));
    141 
    142 struct dkdriver mcddkdriver = { mcdstrategy };
    143 
    144 /* reader state machine */
    145 #define MCD_S_BEGIN	0
    146 #define MCD_S_BEGIN1	1
    147 #define MCD_S_WAITSTAT	2
    148 #define MCD_S_WAITMODE	3
    149 #define MCD_S_WAITREAD	4
    150 
    151 int mcdopen __P((dev_t));
    152 int mcdclose __P((dev_t));
    153 static void mcd_start __P((struct mcd_data *));
    154 int mcdioctl __P((dev_t, int, caddr_t, int));
    155 static int mcd_getdisklabel __P((struct mcd_data *));
    156 int mcdsize __P((dev_t));
    157 static void mcd_configure __P((struct mcd_data *));
    158 static int mcd_waitrdy __P((u_short, int));
    159 static int mcd_getreply __P((struct mcd_data *, int));
    160 static int mcd_getstat __P((struct mcd_data *, int));
    161 static void mcd_setflags __P((struct mcd_data *));
    162 static int mcd_get __P((struct mcd_data *, char *, int));
    163 static int mcd_send __P((struct mcd_data *, int, int));
    164 static int bcd2bin __P((bcd_t));
    165 static bcd_t bin2bcd __P((int));
    166 static void hsg2msf __P((int, bcd_t *));
    167 static int msf2hsg __P((bcd_t *));
    168 static int mcd_volinfo __P((struct mcd_data *));
    169 int mcdintr __P((caddr_t));
    170 static void mcd_doread __P((int, struct mcd_mbx *));
    171 static int mcd_setmode __P((struct mcd_data *, int));
    172 static int mcd_toc_header __P((struct mcd_data *, struct ioc_toc_header *));
    173 static int mcd_read_toc __P((struct mcd_data *));
    174 static int mcd_toc_entry __P((struct mcd_data *, struct ioc_read_toc_entry *));
    175 static int mcd_stop __P((struct mcd_data *));
    176 static int mcd_getqchan __P((struct mcd_data *, struct mcd_qchninfo *));
    177 static int mcd_subchan __P((struct mcd_data *, struct ioc_read_subchannel *));
    178 static int mcd_playtracks __P((struct mcd_data *, struct ioc_play_track *));
    179 static int mcd_play __P((struct mcd_data *, struct mcd_read2 *));
    180 static int mcd_pause __P((struct mcd_data *));
    181 static int mcd_resume __P((struct mcd_data *));
    182 
    183 #define mcd_put(port,byte)	outb(port,byte)
    184 
    185 #define MCD_RETRIES	5
    186 #define MCD_RDRETRIES	8
    187 
    188 #define MCDBLK	2048	/* for cooked mode */
    189 #define MCDRBLK	2352	/* for raw mode */
    190 
    191 /* several delays */
    192 #define RDELAY_WAITSTAT	300
    193 #define RDELAY_WAITMODE	300
    194 #define RDELAY_WAITREAD	800
    195 
    196 #define DELAY_STATUS	10000l		/* 10000 * 1us */
    197 #define DELAY_GETREPLY	200000l		/* 200000 * 2us */
    198 #define DELAY_SEEKREAD	20000l		/* 20000 * 1us */
    199 
    200 void
    201 mcdattach(parent, self, aux)
    202 	struct device *parent, *self;
    203 	void *aux;
    204 {
    205 	struct mcd_data *cd = (void *)self;
    206 	struct isa_attach_args *ia = aux;
    207 
    208 	cd->iobase = ia->ia_iobase;
    209 	cd->flags = MCDINIT;
    210 
    211 #ifdef notyet
    212 	/* get irq/drq configuration word */
    213 	cd->config = irqs[ia->ia_irq]; /* | drqs[ia->ia_drq];*/
    214 	/* wire controller for interrupts and dma */
    215 	mcd_configure(cd);
    216 #endif
    217 
    218 	printf(": Mitsumi CD-ROM\n");
    219 
    220 	isa_establish(&cd->sc_id, &cd->sc_dev);
    221 	cd->sc_ih.ih_fun = mcdintr;
    222 	cd->sc_ih.ih_arg = cd;
    223 	intr_establish(ia->ia_irq, &cd->sc_ih, DV_DISK);
    224 	cd->sc_dk.dk_driver = &mcddkdriver;
    225 	dk_establish(&cd->sc_dk, &cd->sc_dev);
    226 }
    227 
    228 int
    229 mcdopen(dev)
    230 	dev_t dev;
    231 {
    232 	int unit, part, phys;
    233 	struct mcd_data *cd;
    234 
    235 	unit = MCDUNIT(dev);
    236 	part = MCDPART(dev);
    237 	phys = MCDPHYS(dev);
    238 
    239 	if (unit >= mcdcd.cd_ndevs)
    240 		return ENXIO;
    241 	cd = mcdcd.cd_devs[unit];
    242 	if (!cd || !(cd->flags & MCDINIT))
    243 		return ENXIO;
    244 
    245 	/* invalidated in the meantime? mark all open part's invalid */
    246 	if (!(cd->flags & MCDVALID) && cd->openflags)
    247 		return EBUSY;
    248 
    249 	if (mcd_getstat(cd, 1) < 0)
    250 		return ENXIO;
    251 
    252 	/* XXX get a default disklabel */
    253 	mcd_getdisklabel(cd);
    254 
    255 	if (mcdsize(dev) < 0) {
    256 		printf("%s: failed to get disk size\n", cd->sc_dev.dv_xname);
    257 		return ENXIO;
    258 	} else
    259 		cd->flags |= MCDVALID;
    260 
    261 MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
    262 	part,cd->disksize,cd->blksize,0);
    263 
    264 	if (part == RAW_PART ||
    265 		(part < cd->dlabel.d_npartitions &&
    266 		cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
    267 		cd->partflags[part] |= MCDOPEN;
    268 		cd->openflags |= (1<<part);
    269 		if (part == RAW_PART && phys != 0)
    270 			cd->partflags[part] |= MCDREADRAW;
    271 		return 0;
    272 	}
    273 
    274 	return ENXIO;
    275 }
    276 
    277 int
    278 mcdclose(dev)
    279 	dev_t dev;
    280 {
    281 	int unit, part;
    282 	struct mcd_data *cd;
    283 
    284 	unit = MCDUNIT(dev);
    285 	part = MCDPART(dev);
    286 	cd = mcdcd.cd_devs[unit];
    287 
    288 	mcd_getstat(cd, 1);	/* get status */
    289 
    290 	/* close channel */
    291 	cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
    292 	cd->openflags &= ~(1<<part);
    293 	MCD_TRACE("close: partition=%d\n", part, 0, 0, 0);
    294 
    295 	return 0;
    296 }
    297 
    298 void
    299 mcdstrategy(bp)
    300 	struct buf *bp;
    301 {
    302 	struct mcd_data *cd;
    303 	struct buf *qp;
    304 	int s;
    305 
    306 	cd = mcdcd.cd_devs[MCDUNIT(bp->b_dev)];
    307 
    308 	/* test validity */
    309 /*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
    310 	bp, cd->sc_dev.dv_unit, bp->b_blkno, bp->b_bcount);*/
    311 	if (bp->b_blkno < 0) {
    312 		printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
    313 			cd->sc_dev.dv_unit, bp->b_blkno, bp->b_bcount);
    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 (MCDPART(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(cd);
    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
    367 mcd_start(cd)
    368 	struct mcd_data *cd;
    369 {
    370 	struct buf *bp, *qp = &cd->head;
    371 	struct partition *p;
    372 	int 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->av_forw;
    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->sc_dk.dk_label.d_partitions[MCDPART(bp->b_dev)];
    395 
    396 	cd->flags |= MCDMBXBSY;
    397 	cd->mbx.unit = cd->sc_dev.dv_unit;
    398 	cd->mbx.iobase = cd->iobase;
    399 	cd->mbx.retry = MCD_RETRIES;
    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 }
    406 
    407 int
    408 mcdioctl(dev, cmd, addr, flags)
    409 	dev_t dev;
    410 	int cmd;
    411 	caddr_t addr;
    412 	int flags;
    413 {
    414 	struct mcd_data *cd;
    415 	int unit;
    416 
    417 	unit = MCDUNIT(dev);
    418 	cd = mcdcd.cd_devs[unit];
    419 
    420 	if (!(cd->flags & MCDVALID))
    421 		return EIO;
    422 	MCD_TRACE("ioctl called 0x%x\n", cmd, 0, 0, 0);
    423 
    424 	switch (cmd) {
    425 	case DIOCSBAD:
    426 		return EINVAL;
    427 	case DIOCGDINFO:
    428 	case DIOCGPART:
    429 	case DIOCWDINFO:
    430 	case DIOCSDINFO:
    431 	case DIOCWLABEL:
    432 		return ENOTTY;
    433 	case CDIOCPLAYTRACKS:
    434 		return mcd_playtracks(cd, (struct ioc_play_track *) addr);
    435 	case CDIOCPLAYBLOCKS:
    436 		return mcd_play(cd, (struct mcd_read2 *) addr);
    437 	case CDIOCREADSUBCHANNEL:
    438 		return mcd_subchan(cd, (struct ioc_read_subchannel *) addr);
    439 	case CDIOREADTOCHEADER:
    440 		return mcd_toc_header(cd, (struct ioc_toc_header *) addr);
    441 	case CDIOREADTOCENTRYS:
    442 		return mcd_toc_entry(cd, (struct ioc_read_toc_entry *) addr);
    443 	case CDIOCSETPATCH:
    444 	case CDIOCGETVOL:
    445 	case CDIOCSETVOL:
    446 	case CDIOCSETMONO:
    447 	case CDIOCSETSTERIO:
    448 	case CDIOCSETMUTE:
    449 	case CDIOCSETLEFT:
    450 	case CDIOCSETRIGHT:
    451 		return EINVAL;
    452 	case CDIOCRESUME:
    453 		return mcd_resume(cd);
    454 	case CDIOCPAUSE:
    455 		return mcd_pause(cd);
    456 	case CDIOCSTART:
    457 		return EINVAL;
    458 	case CDIOCSTOP:
    459 		return mcd_stop(cd);
    460 	case CDIOCEJECT:
    461 		return EINVAL;
    462 	case CDIOCSETDEBUG:
    463 		cd->debug = 1;
    464 		return 0;
    465 	case CDIOCCLRDEBUG:
    466 		cd->debug = 0;
    467 		return 0;
    468 	case CDIOCRESET:
    469 		return EINVAL;
    470 	default:
    471 		return ENOTTY;
    472 	}
    473 #ifdef DIAGNOSTIC
    474 	panic("mcdioctl: impossible");
    475 #endif
    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
    482 mcd_getdisklabel(cd)
    483 	struct mcd_data *cd;
    484 {
    485 
    486 	if (cd->flags & MCDLABEL)
    487 		return -1;
    488 
    489 	bzero(&cd->sc_dk.dk_label, sizeof(struct disklabel));
    490 	bzero(&cd->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
    491 	strncpy(cd->sc_dk.dk_label.d_typename, "Mitsumi CD ROM ", 16);
    492 	strncpy(cd->sc_dk.dk_label.d_packname, "unknown        ", 16);
    493 	cd->sc_dk.dk_label.d_secsize 	= cd->blksize;
    494 	cd->sc_dk.dk_label.d_nsectors	= 100;
    495 	cd->sc_dk.dk_label.d_ntracks	= 1;
    496 	cd->sc_dk.dk_label.d_ncylinders	= (cd->disksize/100)+1;
    497 	cd->sc_dk.dk_label.d_secpercyl	= 100;
    498 	cd->sc_dk.dk_label.d_secperunit	= cd->disksize;
    499 	cd->sc_dk.dk_label.d_rpm	= 300;
    500 	cd->sc_dk.dk_label.d_interleave	= 1;
    501 	cd->sc_dk.dk_label.d_flags	= D_REMOVABLE;
    502 	cd->sc_dk.dk_label.d_npartitions= 1;
    503 	cd->sc_dk.dk_label.d_partitions[0].p_offset = 0;
    504 	cd->sc_dk.dk_label.d_partitions[0].p_size = cd->disksize;
    505 	cd->sc_dk.dk_label.d_partitions[0].p_fstype = 9;
    506 	cd->sc_dk.dk_label.d_magic	= DISKMAGIC;
    507 	cd->sc_dk.dk_label.d_magic2	= DISKMAGIC;
    508 	cd->sc_dk.dk_label.d_checksum	= dkcksum(&cd->sc_dk.dk_label);
    509 
    510 	cd->flags |= MCDLABEL;
    511 	return 0;
    512 }
    513 
    514 int
    515 mcdsize(dev)
    516 	dev_t dev;
    517 {
    518 	int size;
    519 	int unit = MCDUNIT(dev);
    520 	struct mcd_data *cd = mcdcd.cd_devs[unit];
    521 
    522 	if (mcd_volinfo(cd) >= 0) {
    523 		cd->blksize = MCDBLK;
    524 		size = msf2hsg(cd->volinfo.vol_msf);
    525 		cd->disksize = size * (MCDBLK/DEV_BSIZE);
    526 		return 0;
    527 	}
    528 	return -1;
    529 }
    530 
    531 /***************************************************************
    532  * lower level of driver starts here
    533  **************************************************************/
    534 
    535 #ifdef notyet
    536 static char irqs[] = {
    537 	0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
    538 	0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
    539 };
    540 
    541 static char drqs[] = {
    542 	0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
    543 };
    544 #endif
    545 
    546 static void
    547 mcd_configure(cd)
    548 	struct mcd_data *cd;
    549 {
    550 
    551 	outb(cd->iobase + mcd_config, cd->config);
    552 }
    553 
    554 int
    555 mcdprobe(parent, cf, aux)
    556 	struct device *parent;
    557 	struct cfdata *cf;
    558 	void *aux;
    559 {
    560 	struct isa_attach_args *ia = aux;
    561 	u_short iobase = ia->ia_iobase;
    562 	int i;
    563 	u_char st, check, junk;
    564 
    565 	if (iobase == IOBASEUNK)
    566 		return 0;
    567 
    568 	/* send a reset */
    569 	outb(iobase + MCD_FLAGS, 0);
    570 	delay(100000);
    571 
    572 	/* get any pending status and throw away...*/
    573 	for (i=10; i; i--)
    574 		inb(iobase + MCD_DATA);
    575 
    576 	delay(1000);
    577 	outb(iobase + MCD_DATA, MCD_CMDGETSTAT);/* Send get status command */
    578 	/* Loop looking for avail of status */
    579 	/* XXX May have to increase for fast machinces */
    580 	for (i = 1000; i; i--) {
    581 		if ((inb(iobase + MCD_FLAGS) & 0xf) == STATUS_AVAIL)
    582 			break;
    583 		delay(10);
    584 	}
    585 	if (!i)
    586 		return 0;
    587 
    588 /*
    589  * The following code uses the 0xDC command, it returns a M from the
    590  * second byte and a number in the third.  Does anyone know what the
    591  * number is for? Better yet, how about someone thats REAL good in
    592  * i80x86 asm looking at the Dos driver... Most of this info came
    593  * from a friend of mine spending a whole weekend.....
    594  */
    595 
    596 	delay(2000);
    597 	outb(iobase + MCD_DATA, MCD_CMDCONTINFO);
    598 	for (i = 1000; i; i--) {
    599 		if ((inb(iobase + MCD_FLAGS) & 0xf) == STATUS_AVAIL)
    600 			break;
    601 		delay(10);
    602 	}
    603 	if (!i)
    604 		return 0;
    605 
    606 	delay(40000);
    607 	st = inb(iobase + MCD_DATA);
    608 	delay(500);
    609 	check = inb(iobase + MCD_DATA);
    610 	delay(500);
    611 	junk = inb(iobase + MCD_DATA);	/* What is this byte used for? */
    612 
    613 	if (check != 'M')
    614 		return 0;
    615 
    616 	/* XXXX check irq and drq */
    617 	printf("mcd%d: config register says %x\n", cf->cf_unit,
    618 		inb(iobase + mcd_config));
    619 
    620 	return 4;
    621 }
    622 
    623 static int
    624 mcd_waitrdy(iobase, dly)
    625 	u_short iobase;
    626 	int dly;
    627 {
    628 	int i;
    629 
    630 	/* wait until xfer port senses data ready */
    631 	for (i = dly; i; i--) {
    632 		if ((inb(iobase + mcd_xfer) & MCD_ST_BUSY) == 0)
    633 			return 0;
    634 		delay(1);
    635 	}
    636 	return -1;
    637 }
    638 
    639 static int
    640 mcd_getreply(cd, dly)
    641 	struct mcd_data *cd;
    642 	int dly;
    643 {
    644 	u_short iobase = cd->iobase;
    645 
    646 	/* wait data to become ready */
    647 	if (mcd_waitrdy(iobase, dly)<0) {
    648 		printf("%s: timeout getreply\n", cd->sc_dev.dv_xname);
    649 		return -1;
    650 	}
    651 
    652 	/* get the data */
    653 	return inb(iobase + mcd_status) & 0xff;
    654 }
    655 
    656 static int
    657 mcd_getstat(cd, sflg)
    658 	struct mcd_data *cd;
    659 	int sflg;
    660 {
    661 	int i;
    662 	u_short iobase = cd->iobase;
    663 
    664 	/* get the status */
    665 	if (sflg)
    666 		outb(iobase + mcd_command, MCD_CMDGETSTAT);
    667 	i = mcd_getreply(cd, DELAY_GETREPLY);
    668 	if (i < 0)
    669 		return -1;
    670 
    671 	cd->status = i;
    672 
    673 	mcd_setflags(cd);
    674 	return cd->status;
    675 }
    676 
    677 static void
    678 mcd_setflags(cd)
    679 	struct mcd_data *cd;
    680 {
    681 
    682 	/* check flags */
    683 	if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
    684 		MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n", 0, 0, 0, 0);
    685 		cd->flags &= ~MCDVALID;
    686 	}
    687 
    688 	if (cd->status & MCDAUDIOBSY)
    689 		cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
    690 	else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
    691 		cd->audio_status = CD_AS_PLAY_COMPLETED;
    692 }
    693 
    694 static int
    695 mcd_get(cd, buf, nmax)
    696 	struct mcd_data *cd;
    697 	char *buf;
    698 	int nmax;
    699 {
    700 	int i, k;
    701 
    702 	for (i = 0; i < nmax; i++) {
    703 		/* wait for data */
    704 		if ((k = mcd_getreply(cd, DELAY_GETREPLY)) < 0) {
    705 			printf("%s: timeout mcd_get\n", cd->sc_dev.dv_xname);
    706 			return -1;
    707 		}
    708 		buf[i] = k;
    709 	}
    710 	return i;
    711 }
    712 
    713 static int
    714 mcd_send(cd, cmd, nretries)
    715 	struct mcd_data *cd;
    716 	int cmd, nretries;
    717 {
    718 	int i, k;
    719 	u_short iobase = cd->iobase;
    720 
    721 /*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
    722 	for (i = nretries; i; i--) {
    723 		outb(iobase + mcd_command, cmd);
    724 		if ((k = mcd_getstat(cd, 0)) != -1)
    725 			break;
    726 	}
    727 	if (!i) {
    728 		printf("%s: mcd_send retry cnt exceeded\n",
    729 			cd->sc_dev.dv_xname);
    730 		return -1;
    731 	}
    732 
    733 /*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
    734 	return 0;
    735 }
    736 
    737 static int
    738 bcd2bin(b)
    739 	bcd_t b;
    740 {
    741 
    742 	return (b >> 4) * 10 + (b & 15);
    743 }
    744 
    745 static bcd_t
    746 bin2bcd(b)
    747 	int b;
    748 {
    749 
    750 	return ((b / 10) << 4) | (b % 10);
    751 }
    752 
    753 static void
    754 hsg2msf(hsg, msf)
    755 	int hsg;
    756 	bcd_t *msf;
    757 {
    758 
    759 	hsg += 150;
    760 	M_msf(msf) = bin2bcd(hsg / 4500);
    761 	hsg %= 4500;
    762 	S_msf(msf) = bin2bcd(hsg / 75);
    763 	F_msf(msf) = bin2bcd(hsg % 75);
    764 }
    765 
    766 static int
    767 msf2hsg(msf)
    768 	bcd_t *msf;
    769 {
    770 
    771 	return (bcd2bin(M_msf(msf)) * 60 +
    772 		bcd2bin(S_msf(msf))) * 75 +
    773 		bcd2bin(F_msf(msf)) - 150;
    774 }
    775 
    776 static int
    777 mcd_volinfo(cd)
    778 	struct mcd_data *cd;
    779 {
    780 
    781 /*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
    782 
    783 	/* Get the status, in case the disc has been changed */
    784 	if (mcd_getstat(cd, 1) < 0)
    785 		return EIO;
    786 
    787 	/* Just return if we already have it */
    788 	if (cd->flags & MCDVOLINFO)
    789 		return 0;
    790 
    791 	/* send volume info command */
    792 	if (mcd_send(cd, MCD_CMDGETVOLINFO, MCD_RETRIES) < 0)
    793 		return -1;
    794 
    795 	/* get data */
    796 	if (mcd_get(cd, (char*) &cd->volinfo, sizeof(struct mcd_volinfo)) < 0) {
    797 		printf("%s: mcd_volinfo: error read data\n",
    798 			cd->sc_dev.dv_xname);
    799 		return -1;
    800 	}
    801 
    802 	if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
    803 		cd->flags |= MCDVOLINFO;	/* volinfo is OK */
    804 		return 0;
    805 	}
    806 
    807 	return -1;
    808 }
    809 
    810 int
    811 mcdintr(arg)
    812 	caddr_t arg;
    813 {
    814 	struct mcd_data *cd = (void *)arg;
    815 	u_short iobase = cd->iobase;
    816 
    817 	MCD_TRACE("stray interrupt xfer=0x%x\n", inb(iobase + mcd_xfer),
    818 		0, 0, 0);
    819 
    820 	/* just read out status and ignore the rest */
    821 	if ((inb(iobase + mcd_xfer) & 0xff) != 0xff)
    822 		(void) inb(iobase + mcd_status);
    823 }
    824 
    825 /* state machine to process read requests
    826  * initialize with MCD_S_BEGIN: calculate sizes, and read status
    827  * MCD_S_WAITSTAT: wait for status reply, set mode
    828  * MCD_S_WAITMODE: waits for status reply from set mode, set read command
    829  * MCD_S_WAITREAD: wait for read ready, read data
    830  */
    831 static struct mcd_mbx *mbxsave;
    832 
    833 static void
    834 mcd_doread(state, mbxin)
    835 	int state;
    836 	struct mcd_mbx *mbxin;
    837 {
    838 	struct mcd_mbx *mbx = (state != MCD_S_BEGIN) ? mbxsave : mbxin;
    839 	struct mcd_data *cd = mcdcd.cd_devs[mbx->unit];
    840 	u_short iobase = mbx->iobase;
    841 	struct buf *bp = mbx->bp;
    842 
    843 	int rm, i, k;
    844 	struct mcd_read2 rbuf;
    845 	int blkno;
    846 	caddr_t	addr;
    847 
    848 loop:
    849 	switch (state) {
    850 	case MCD_S_BEGIN:
    851 		mbx = mbxsave = mbxin;
    852 
    853 	case MCD_S_BEGIN1:
    854 		/* get status */
    855 		outb(iobase + mcd_command, MCD_CMDGETSTAT);
    856 		mbx->count = RDELAY_WAITSTAT;
    857 		timeout((timeout_t) mcd_doread, (caddr_t) MCD_S_WAITSTAT,
    858 			hz/100);
    859 		return;
    860 
    861 	case MCD_S_WAITSTAT:
    862 		untimeout((timeout_t) mcd_doread, (caddr_t) MCD_S_WAITSTAT);
    863 		if (mbx->count-- >= 0) {
    864 			if (inb(iobase + mcd_xfer) & MCD_ST_BUSY) {
    865 				timeout((timeout_t) mcd_doread,
    866 					(caddr_t) MCD_S_WAITSTAT, hz/100);
    867 				return;
    868 			}
    869 			mcd_setflags(cd);
    870 			MCD_TRACE("got WAITSTAT delay=%d\n",
    871 				RDELAY_WAITSTAT-mbx->count, 0, 0, 0);
    872 			/* reject, if audio active */
    873 			if (cd->status & MCDAUDIOBSY) {
    874 				printf("%s: audio is active\n",
    875 					cd->sc_dev.dv_xname);
    876 				goto readerr;
    877 			}
    878 
    879 			/* to check for raw/cooked mode */
    880 			if (cd->flags & MCDREADRAW) {
    881 				rm = MCD_MD_RAW;
    882 				mbx->sz = MCDRBLK;
    883 			} else {
    884 				rm = MCD_MD_COOKED;
    885 				mbx->sz = cd->blksize;
    886 			}
    887 
    888 			mbx->count = RDELAY_WAITMODE;
    889 
    890 			mcd_put(iobase + mcd_command, MCD_CMDSETMODE);
    891 			mcd_put(iobase + mcd_command, rm);
    892 			timeout((timeout_t) mcd_doread,
    893 				(caddr_t) MCD_S_WAITMODE, hz/100);
    894 			return;
    895 		} else {
    896 			printf("%s: timeout getstatus\n", cd->sc_dev.dv_xname);
    897 			goto readerr;
    898 		}
    899 
    900 	case MCD_S_WAITMODE:
    901 		untimeout((timeout_t) mcd_doread, (caddr_t) MCD_S_WAITMODE);
    902 		if (mbx->count-- < 0) {
    903 			printf("%s: timeout set mode\n", cd->sc_dev.dv_xname);
    904 			goto readerr;
    905 		}
    906 		if (inb(iobase + mcd_xfer) & MCD_ST_BUSY) {
    907 			timeout((timeout_t) mcd_doread,
    908 				(caddr_t) MCD_S_WAITMODE, hz/100);
    909 			return;
    910 		}
    911 		mcd_setflags(cd);
    912 		MCD_TRACE("got WAITMODE delay=%d\n",
    913 			RDELAY_WAITMODE-mbx->count, 0, 0, 0);
    914 		/* for first block */
    915 		mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
    916 		mbx->skip = 0;
    917 
    918 nextblock:
    919 		blkno = (bp->b_blkno / (mbx->sz/DEV_BSIZE))
    920 			+ mbx->p_offset + mbx->skip/mbx->sz;
    921 
    922 		MCD_TRACE("mcd_doread: read blkno=%d for bp=0x%x\n",
    923 			blkno, bp, 0, 0);
    924 
    925 		/* build parameter block */
    926 		hsg2msf(blkno, rbuf.start_msf);
    927 
    928 		/* send the read command */
    929 		mcd_put(iobase + mcd_command,MCD_CMDREAD2);
    930 		mcd_put(iobase + mcd_command,rbuf.start_msf[0]);
    931 		mcd_put(iobase + mcd_command,rbuf.start_msf[1]);
    932 		mcd_put(iobase + mcd_command,rbuf.start_msf[2]);
    933 		mcd_put(iobase + mcd_command,0);
    934 		mcd_put(iobase + mcd_command,0);
    935 		mcd_put(iobase + mcd_command,1);
    936 		mbx->count = RDELAY_WAITREAD;
    937 		timeout((timeout_t) mcd_doread, (caddr_t) MCD_S_WAITREAD,
    938 			hz/100);
    939 		return;
    940 
    941 	case MCD_S_WAITREAD:
    942 		untimeout((timeout_t) mcd_doread, (caddr_t) MCD_S_WAITREAD);
    943 		if (mbx->count-- > 0) {
    944 			k = inb(iobase + mcd_xfer);
    945 			if ((k & 2)==0) {
    946 				MCD_TRACE("got data delay=%d\n",
    947 					RDELAY_WAITREAD-mbx->count, 0, 0, 0);
    948 				/* data is ready */
    949 				addr = bp->b_un.b_addr + mbx->skip;
    950 				outb(iobase + mcd_ctl2,0x04);	/* XXX */
    951 				for (i=0; i<mbx->sz; i++)
    952 					*addr++	= inb(iobase + mcd_rdata);
    953 				outb(iobase + mcd_ctl2,0x0c);	/* XXX */
    954 
    955 				if (--mbx->nblk > 0) {
    956 					mbx->skip += mbx->sz;
    957 					goto nextblock;
    958 				}
    959 
    960 				/* return buffer */
    961 				bp->b_resid = 0;
    962 				biodone(bp);
    963 
    964 				cd->flags &= ~MCDMBXBSY;
    965 				mcd_start(cd);
    966 				return;
    967 			}
    968 			if ((k & 4) == 0)
    969 				mcd_getstat(cd, 0);
    970 			timeout((timeout_t) mcd_doread,
    971 				(caddr_t) MCD_S_WAITREAD, hz/100);
    972 			return;
    973 		} else {
    974 			printf("%s: timeout read data\n", cd->sc_dev.dv_xname);
    975 			goto readerr;
    976 		}
    977 	}
    978 
    979 readerr:
    980 	if (mbx->retry-- > 0) {
    981 		printf("%s: retrying\n", cd->sc_dev.dv_xname);
    982 		state = MCD_S_BEGIN1;
    983 		goto loop;
    984 	}
    985 
    986 	/* invalidate the buffer */
    987 	bp->b_flags |= B_ERROR;
    988 	bp->b_resid = bp->b_bcount;
    989 	biodone(bp);
    990 	mcd_start(cd);
    991 
    992 #ifdef notdef
    993 	printf("%s: unit timeout, resetting\n", cd->sc_dev.dv_xname);
    994 	outb(mbx->iobase + mcd_reset,MCD_CMDRESET);
    995 	delay(300000);
    996 	(void)mcd_getstat(cd, 1);
    997 	(void)mcd_getstat(cd, 1);
    998 	/*cd->status &= ~MCDDSKCHNG; */
    999 	cd->debug = 1; /* preventive set debug mode */
   1000 #endif
   1001 }
   1002 
   1003 static int
   1004 mcd_setmode(cd, mode)
   1005 	struct mcd_data *cd;
   1006 	int mode;
   1007 {
   1008 	u_short iobase = cd->iobase;
   1009 	int retry;
   1010 
   1011 	printf("%s: setting mode to %d\n", cd->sc_dev.dv_xname, mode);
   1012 	for(retry = MCD_RETRIES; retry; retry--) {
   1013 		outb(iobase + mcd_command, MCD_CMDSETMODE);
   1014 		outb(iobase + mcd_command, mode);
   1015 		if (mcd_getstat(cd, 0) != -1)
   1016 			return 0;
   1017 	}
   1018 
   1019 	return -1;
   1020 }
   1021 
   1022 static int
   1023 mcd_toc_header(cd, th)
   1024 	struct mcd_data *cd;
   1025 	struct ioc_toc_header *th;
   1026 {
   1027 
   1028 	if (mcd_volinfo(cd) < 0)
   1029 		return ENXIO;
   1030 
   1031 	th->len = msf2hsg(cd->volinfo.vol_msf);
   1032 	th->starting_track = bcd2bin(cd->volinfo.trk_low);
   1033 	th->ending_track = bcd2bin(cd->volinfo.trk_high);
   1034 
   1035 	return 0;
   1036 }
   1037 
   1038 static int
   1039 mcd_read_toc(cd)
   1040 	struct mcd_data *cd;
   1041 {
   1042 	struct ioc_toc_header th;
   1043 	struct mcd_qchninfo q;
   1044 	int rc, trk, idx, retry;
   1045 
   1046 	/* Only read TOC if needed */
   1047 	if (cd->flags & MCDTOC)
   1048 		return 0;
   1049 
   1050 	printf("%s: reading toc header\n", cd->sc_dev.dv_xname);
   1051 	if (mcd_toc_header(cd, &th) != 0)
   1052 		return ENXIO;
   1053 
   1054 	printf("%s: stopping play\n", cd->sc_dev.dv_xname);
   1055 	if ((rc = mcd_stop(cd)) != 0)
   1056 		return rc;
   1057 
   1058 	/* try setting the mode twice */
   1059 	if (mcd_setmode(cd, MCD_MD_TOC) != 0)
   1060 		return EIO;
   1061 	if (mcd_setmode(cd, MCD_MD_TOC) != 0)
   1062 		return EIO;
   1063 
   1064 	printf("%s: get_toc reading qchannel info\n", cd->sc_dev.dv_xname);
   1065 	for(trk = th.starting_track; trk <= th.ending_track; trk++)
   1066 		cd->toc[trk].idx_no = 0;
   1067 	trk = th.ending_track - th.starting_track + 1;
   1068 	for(retry = 300; retry && trk > 0; retry--) {
   1069 		if (mcd_getqchan(cd, &q) < 0)
   1070 			break;
   1071 		idx = bcd2bin(q.idx_no);
   1072 		if (idx > 0 && idx < MCD_MAXTOCS && q.trk_no==0)
   1073 			if (cd->toc[idx].idx_no == 0) {
   1074 				cd->toc[idx] = q;
   1075 				trk--;
   1076 			}
   1077 	}
   1078 
   1079 	if (mcd_setmode(cd, MCD_MD_COOKED) != 0)
   1080 		return EIO;
   1081 
   1082 	if (trk != 0)
   1083 		return ENXIO;
   1084 
   1085 	/* add a fake last+1 */
   1086 	idx = th.ending_track + 1;
   1087 	cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
   1088 	cd->toc[idx].trk_no = 0;
   1089 	cd->toc[idx].idx_no = 0xAA;
   1090 	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
   1091 	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
   1092 	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
   1093 
   1094 	cd->flags |= MCDTOC;
   1095 
   1096 	return 0;
   1097 }
   1098 
   1099 static int
   1100 mcd_toc_entry(cd, te)
   1101 	struct mcd_data *cd;
   1102 	struct ioc_read_toc_entry *te;
   1103 {
   1104 	struct ret_toc {
   1105 		struct ioc_toc_header th;
   1106 		struct cd_toc_entry rt;
   1107 	} ret_toc;
   1108 	struct ioc_toc_header th;
   1109 	int rc, i;
   1110 
   1111 	/* Make sure we have a valid toc */
   1112 	if ((rc = mcd_read_toc(cd)) != 0)
   1113 		return rc;
   1114 
   1115 	/* find the toc to copy*/
   1116 	i = te->starting_track;
   1117 	if (i == MCD_LASTPLUS1)
   1118 		i = bcd2bin(cd->volinfo.trk_high) + 1;
   1119 
   1120 	/* verify starting track */
   1121 	if (i < bcd2bin(cd->volinfo.trk_low) ||
   1122 	    i > bcd2bin(cd->volinfo.trk_high) + 1)
   1123 		return EINVAL;
   1124 
   1125 	/* do we have room */
   1126 	if (te->data_len < sizeof(struct ioc_toc_header) +
   1127 	    sizeof(struct cd_toc_entry))
   1128 		return EINVAL;
   1129 
   1130 	/* Copy the toc header */
   1131 	if (mcd_toc_header(cd, &th) < 0)
   1132 		return EIO;
   1133 	ret_toc.th = th;
   1134 
   1135 	/* copy the toc data */
   1136 	ret_toc.rt.control = cd->toc[i].ctrl_adr;
   1137 	ret_toc.rt.addr_type = te->address_format;
   1138 	ret_toc.rt.track = i;
   1139 	if (te->address_format == CD_MSF_FORMAT) {
   1140 		ret_toc.rt.addr[1] = cd->toc[i].hd_pos_msf[0];
   1141 		ret_toc.rt.addr[2] = cd->toc[i].hd_pos_msf[1];
   1142 		ret_toc.rt.addr[3] = cd->toc[i].hd_pos_msf[2];
   1143 	}
   1144 
   1145 	/* copy the data back */
   1146 	copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
   1147 		+ sizeof(struct ioc_toc_header));
   1148 
   1149 	return 0;
   1150 }
   1151 
   1152 static int
   1153 mcd_stop(cd)
   1154 	struct mcd_data *cd;
   1155 {
   1156 
   1157 	if (mcd_send(cd, MCD_CMDSTOPAUDIO, MCD_RETRIES) < 0)
   1158 		return ENXIO;
   1159 	cd->audio_status = CD_AS_PLAY_COMPLETED;
   1160 	return 0;
   1161 }
   1162 
   1163 static int
   1164 mcd_getqchan(cd, q)
   1165 	struct mcd_data *cd;
   1166 	struct mcd_qchninfo *q;
   1167 {
   1168 
   1169 	if (mcd_send(cd, MCD_CMDGETQCHN, MCD_RETRIES) < 0)
   1170 		return -1;
   1171 	if (mcd_get(cd, (char *) q, sizeof(struct mcd_qchninfo)) < 0)
   1172 		return -1;
   1173 	if (cd->debug)
   1174 	printf("%s: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
   1175 		cd->sc_dev.dv_xname, q->ctrl_adr, q->trk_no, q->idx_no,
   1176 		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
   1177 		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
   1178 	return 0;
   1179 }
   1180 
   1181 static int
   1182 mcd_subchan(cd, sc)
   1183 	struct mcd_data *cd;
   1184 	struct ioc_read_subchannel *sc;
   1185 {
   1186 	struct mcd_qchninfo q;
   1187 	struct cd_sub_channel_info data;
   1188 
   1189 	printf("%s: subchan af=%d, df=%d\n", cd->sc_dev.dv_xname,
   1190 		sc->address_format, sc->data_format);
   1191 	if (sc->address_format != CD_MSF_FORMAT)
   1192 		return EIO;
   1193 	if (sc->data_format != CD_CURRENT_POSITION)
   1194 		return EIO;
   1195 
   1196 	if (mcd_getqchan(cd, &q) < 0)
   1197 		return EIO;
   1198 
   1199 	data.header.audio_status = cd->audio_status;
   1200 	data.what.position.data_format = CD_MSF_FORMAT;
   1201 	data.what.position.track_number = bcd2bin(q.trk_no);
   1202 
   1203 	if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info)) != 0)
   1204 		return EFAULT;
   1205 	return 0;
   1206 }
   1207 
   1208 static int
   1209 mcd_playtracks(cd, pt)
   1210 	struct mcd_data *cd;
   1211 	struct ioc_play_track *pt;
   1212 {
   1213 	struct mcd_read2 pb;
   1214 	int a = pt->start_track;
   1215 	int z = pt->end_track;
   1216 	int rc;
   1217 
   1218 	if ((rc = mcd_read_toc(cd)) != 0)
   1219 		return rc;
   1220 
   1221 	printf("%s: playtracks from %d:%d to %d:%d\n", cd->sc_dev.dv_xname,
   1222 		a, pt->start_index, z, pt->end_index);
   1223 
   1224 	if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
   1225 		z < cd->volinfo.trk_low || z > cd->volinfo.trk_high)
   1226 		return EINVAL;
   1227 
   1228 	pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
   1229 	pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
   1230 	pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
   1231 	pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
   1232 	pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
   1233 	pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
   1234 
   1235 	return mcd_play(cd, &pb);
   1236 }
   1237 
   1238 static int
   1239 mcd_play(cd, pb)
   1240 	struct mcd_data *cd;
   1241 	struct mcd_read2 *pb;
   1242 {
   1243 	u_short iobase = cd->iobase;
   1244 	int retry, st;
   1245 
   1246 	cd->lastpb = *pb;
   1247 	for (retry = MCD_RETRIES; retry; retry--) {
   1248 		outb(iobase + mcd_command, MCD_CMDREAD2);
   1249 		outb(iobase + mcd_command, pb->start_msf[0]);
   1250 		outb(iobase + mcd_command, pb->start_msf[1]);
   1251 		outb(iobase + mcd_command, pb->start_msf[2]);
   1252 		outb(iobase + mcd_command, pb->end_msf[0]);
   1253 		outb(iobase + mcd_command, pb->end_msf[1]);
   1254 		outb(iobase + mcd_command, pb->end_msf[2]);
   1255 		if ((st = mcd_getstat(cd, 0)) != -1)
   1256 			break;
   1257 	}
   1258 	if (!retry)
   1259 		return ENXIO;
   1260 
   1261 	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
   1262 	return 0;
   1263 }
   1264 
   1265 static int
   1266 mcd_pause(cd)
   1267 	struct mcd_data *cd;
   1268 {
   1269 	struct mcd_qchninfo q;
   1270 	int rc;
   1271 
   1272 	/* Verify current status */
   1273 	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) {
   1274 		printf("%s: pause attempted when not playing\n",
   1275 			cd->sc_dev.dv_xname);
   1276 		return EINVAL;
   1277 	}
   1278 
   1279 	/* Get the current position */
   1280 	if (mcd_getqchan(cd, &q) < 0)
   1281 		return EIO;
   1282 
   1283 	/* Copy it into lastpb */
   1284 	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
   1285 	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
   1286 	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
   1287 
   1288 	/* Stop playing */
   1289 	if ((rc = mcd_stop(cd)) != 0)
   1290 		return rc;
   1291 
   1292 	/* Set the proper status and exit */
   1293 	cd->audio_status = CD_AS_PLAY_PAUSED;
   1294 	return 0;
   1295 }
   1296 
   1297 static int
   1298 mcd_resume(cd)
   1299 	struct mcd_data *cd;
   1300 {
   1301 
   1302 	if (cd->audio_status != CD_AS_PLAY_PAUSED)
   1303 		return EINVAL;
   1304 	return mcd_play(cd, &cd->lastpb);
   1305 }
   1306