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