mcd.c revision 1379
1174891Sedwin/*
2174891Sedwin * Copyright 1993 by Holger Veit (data part)
3174891Sedwin * Copyright 1993 by Brian Moore (audio part)
4174891Sedwin * Changes Copyright 1993 by Gary Clark II
5174891Sedwin *
6174891Sedwin * Rewrote probe routine to work on newer Mitsumi drives.
7174891Sedwin * Additional changes (C) 1994 by Jordan K. Hubbard
8174891Sedwin *
9174891Sedwin * All rights reserved.
10174891Sedwin *
11174891Sedwin * Redistribution and use in source and binary forms, with or without
12174891Sedwin * modification, are permitted provided that the following conditions
13174891Sedwin * are met:
14174891Sedwin * 1. Redistributions of source code must retain the above copyright
15174891Sedwin *    notice, this list of conditions and the following disclaimer.
16174891Sedwin * 2. Redistributions in binary form must reproduce the above copyright
17174891Sedwin *    notice, this list of conditions and the following disclaimer in the
18174891Sedwin *    documentation and/or other materials provided with the distribution.
19174891Sedwin * 3. All advertising materials mentioning features or use of this software
20174891Sedwin *    must display the following acknowledgement:
21174891Sedwin *	This software was developed by Holger Veit and Brian Moore
22174891Sedwin *	for use with "386BSD" and similar operating systems.
23174891Sedwin *    "Similar operating systems" includes mainly non-profit oriented
24174891Sedwin *    systems for research and education, including but not restricted to
25174891Sedwin *    "NetBSD", "FreeBSD", "Mach" (by CMU).
26174891Sedwin * 4. Neither the name of the developer(s) nor the name "386BSD"
27228992Suqs *    may be used to endorse or promote products derived from this
28174891Sedwin *    software without specific prior written permission.
29174891Sedwin *
30174891Sedwin * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
31174891Sedwin * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32174891Sedwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33174891Sedwin * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER(S) BE
34174891Sedwin * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
35174891Sedwin * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
36174891Sedwin * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
37174891Sedwin * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
38174891Sedwin * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
39174891Sedwin * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40174891Sedwin * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41174891Sedwin *
42174891Sedwin *	$Id: mcd.c,v 1.14 1994/03/21 20:59:55 ats Exp $
43174891Sedwin */
44174891Sedwinstatic char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
45174891Sedwin
46174891Sedwin#include "mcd.h"
47174891Sedwin#if NMCD > 0
48174891Sedwin#include "types.h"
49174891Sedwin#include "param.h"
50174891Sedwin#include "systm.h"
51174891Sedwin#include "conf.h"
52174891Sedwin#include "file.h"
53174891Sedwin#include "buf.h"
54174891Sedwin#include "stat.h"
55174891Sedwin#include "uio.h"
56174891Sedwin#include "ioctl.h"
57174891Sedwin#include "cdio.h"
58174891Sedwin#include "errno.h"
59174891Sedwin#include "dkbad.h"
60281501Seadler#include "disklabel.h"
61281501Seadler#include "i386/isa/isa.h"
62281501Seadler#include "i386/isa/isa_device.h"
63281501Seadler#include "mcdreg.h"
64281501Seadler
65281501Seadler/* user definable options */
66281501Seadler/*#define MCD_TO_WARNING_ON*/	/* define to get timeout messages */
67174891Sedwin/*#define MCDMINI*/ 		/* define for a mini configuration for boot kernel */
68174891Sedwin
69174891Sedwin
70174891Sedwin#ifdef MCDMINI
71174891Sedwin#define MCD_TRACE(fmt,a,b,c,d)
72174891Sedwin#ifdef MCD_TO_WARNING_ON
73174891Sedwin#undef MCD_TO_WARNING_ON
74174891Sedwin#endif
75224016Sbz#else
76174891Sedwin#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);}}
77174891Sedwin#endif
78174891Sedwin
79174891Sedwin#define mcd_part(dev)	((minor(dev)) & 7)
80174891Sedwin#define mcd_unit(dev)	(((minor(dev)) & 0x38) >> 3)
81174891Sedwin#define mcd_phys(dev)	(((minor(dev)) & 0x40) >> 6)
82174891Sedwin#define RAW_PART	0
83174891Sedwin
84174891Sedwin/* flags */
85174891Sedwin#define MCDOPEN		0x0001	/* device opened */
86174891Sedwin#define MCDVALID	0x0002	/* parameters loaded */
87174891Sedwin#define MCDINIT		0x0004	/* device is init'd */
88174891Sedwin#define MCDWAIT		0x0008	/* waiting for something */
89174891Sedwin#define MCDLABEL	0x0010	/* label is read */
90174891Sedwin#define	MCDPROBING	0x0020	/* probing */
91174891Sedwin#define	MCDREADRAW	0x0040	/* read raw mode (2352 bytes) */
92174891Sedwin#define	MCDVOLINFO	0x0080	/* already read volinfo */
93174891Sedwin#define	MCDTOC		0x0100	/* already read toc */
94281501Seadler#define	MCDMBXBSY	0x0200	/* local mbx is busy */
95174891Sedwin
96174891Sedwin/* status */
97281501Seadler#define	MCDAUDIOBSY	MCD_ST_AUDIOBSY		/* playing audio */
98174891Sedwin#define MCDDSKCHNG	MCD_ST_DSKCHNG		/* sensed change of disk */
99174891Sedwin#define MCDDSKIN	MCD_ST_DSKIN		/* sensed disk in drive */
100281501Seadler#define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
101174891Sedwin
102174891Sedwin/* These are apparently the different states a mitsumi can get up to */
103174891Sedwin#define MCDCDABSENT	0x0030
104174891Sedwin#define MCDCDPRESENT	0x0020
105174891Sedwin#define MCDSCLOSED	0x0080
106174891Sedwin#define MCDSOPEN	0x00a0
107174891Sedwin
108174891Sedwin/* toc */
109174891Sedwin#define MCD_MAXTOCS	104	/* from the Linux driver */
110174891Sedwin#define MCD_LASTPLUS1	170	/* special toc entry */
111174891Sedwin
112174891Sedwinstruct mcd_mbx {
113174891Sedwin	short		unit;
114174891Sedwin	short		port;
115174891Sedwin	short		retry;
116174891Sedwin	short		nblk;
117174891Sedwin	int		sz;
118174891Sedwin	u_long		skip;
119174891Sedwin	struct buf	*bp;
120174891Sedwin	int		p_offset;
121223925Sdelphij	short		count;
122174891Sedwin};
123174891Sedwin
124174891Sedwinstruct mcd_data {
125174891Sedwin	short	config;
126174891Sedwin	short	flags;
127174891Sedwin	short	status;
128174891Sedwin	int	blksize;
129208986Sbz	u_long	disksize;
130174891Sedwin	int	iobase;
131174891Sedwin	struct disklabel dlabel;
132174891Sedwin	int	partflags[MAXPARTITIONS];
133174891Sedwin	int	openflags;
134174891Sedwin	struct mcd_volinfo volinfo;
135174891Sedwin#ifndef MCDMINI
136174891Sedwin	struct mcd_qchninfo toc[MCD_MAXTOCS];
137174891Sedwin	short	audio_status;
138174891Sedwin	struct mcd_read2 lastpb;
139174891Sedwin#endif
140174891Sedwin	short	debug;
141174891Sedwin	struct buf head;		/* head of buf queue */
142174891Sedwin	struct mcd_mbx mbx;
143174891Sedwin} mcd_data[NMCD];
144174891Sedwin
145174891Sedwin/* reader state machine */
146174891Sedwin#define MCD_S_BEGIN	0
147174891Sedwin#define MCD_S_BEGIN1	1
148174891Sedwin#define MCD_S_WAITSTAT	2
149174891Sedwin#define MCD_S_WAITMODE	3
150174891Sedwin#define MCD_S_WAITREAD	4
151174891Sedwin
152174891Sedwin/* prototypes */
153174891Sedwinint	mcdopen(dev_t dev);
154174891Sedwinint	mcdclose(dev_t dev);
155174891Sedwinvoid	mcdstrategy(struct buf *bp);
156174891Sedwinint	mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
157174891Sedwinint	mcdsize(dev_t dev);
158174891Sedwinstatic	void	mcd_done(struct mcd_mbx *mbx);
159174891Sedwinstatic	void	mcd_start(int unit);
160174891Sedwinstatic	int	mcd_getdisklabel(int unit);
161174891Sedwinstatic	void	mcd_configure(struct mcd_data *cd);
162174891Sedwinstatic	int	mcd_get(int unit, char *buf, int nmax);
163174891Sedwinstatic	void	mcd_setflags(int unit,struct mcd_data *cd);
164174891Sedwinstatic	int	mcd_getstat(int unit,int sflg);
165174891Sedwinstatic	int	mcd_send(int unit, int cmd,int nretrys);
166174891Sedwinstatic	int	bcd2bin(bcd_t b);
167174891Sedwinstatic	bcd_t	bin2bcd(int b);
168174891Sedwinstatic	void	hsg2msf(int hsg, bcd_t *msf);
169174891Sedwinstatic	int	msf2hsg(bcd_t *msf);
170174891Sedwinstatic	int	mcd_volinfo(int unit);
171174891Sedwinstatic	int	mcd_waitrdy(int port,int dly);
172174891Sedwinstatic 	void	mcd_doread(int state, struct mcd_mbx *mbxin);
173174891Sedwin#ifndef MCDMINI
174174891Sedwinstatic	int 	mcd_setmode(int unit, int mode);
175174891Sedwinstatic	int	mcd_getqchan(int unit, struct mcd_qchninfo *q);
176174891Sedwinstatic	int	mcd_subchan(int unit, struct ioc_read_subchannel *sc);
177174891Sedwinstatic	int	mcd_toc_header(int unit, struct ioc_toc_header *th);
178174891Sedwinstatic	int	mcd_read_toc(int unit);
179174891Sedwinstatic	int	mcd_toc_entry(int unit, struct ioc_read_toc_entry *te);
180174891Sedwinstatic	int	mcd_stop(int unit);
181174891Sedwinstatic	int	mcd_playtracks(int unit, struct ioc_play_track *pt);
182174891Sedwinstatic	int	mcd_play(int unit, struct mcd_read2 *pb);
183174891Sedwinstatic	int	mcd_pause(int unit);
184174891Sedwinstatic	int	mcd_resume(int unit);
185174891Sedwin#endif
186174891Sedwin
187174891Sedwinextern	int	hz;
188174891Sedwinextern	int	mcd_probe(struct isa_device *dev);
189174891Sedwinextern	int	mcd_attach(struct isa_device *dev);
190174891Sedwinstruct	isa_driver	mcddriver = { mcd_probe, mcd_attach, "mcd" };
191174891Sedwin
192174891Sedwin#define mcd_put(port,byte)	outb(port,byte)
193174891Sedwin
194174891Sedwin#define MCD_RETRYS	5
195174891Sedwin#define MCD_RDRETRYS	8
196174891Sedwin
197174891Sedwin#define MCDBLK	2048	/* for cooked mode */
198174891Sedwin#define MCDRBLK	2352	/* for raw mode */
199174891Sedwin
200223925Sdelphij/* several delays */
201174891Sedwin#define RDELAY_WAITSTAT	300
202174891Sedwin#define RDELAY_WAITMODE	300
203174891Sedwin#define RDELAY_WAITREAD	800
204174891Sedwin
205174891Sedwin#define DELAY_STATUS	10000l		/* 10000 * 1us */
206#define DELAY_GETREPLY	200000l		/* 200000 * 2us */
207#define DELAY_SEEKREAD	20000l		/* 20000 * 1us */
208#define mcd_delay	DELAY
209
210int mcd_attach(struct isa_device *dev)
211{
212	struct mcd_data *cd = mcd_data + dev->id_unit;
213	int i;
214
215	cd->iobase = dev->id_iobase;
216	cd->flags |= MCDINIT;
217	cd->openflags = 0;
218	for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
219
220#ifdef NOTYET
221	/* wire controller for interrupts and dma */
222	mcd_configure(cd);
223#endif
224
225	return 1;
226}
227
228int mcdopen(dev_t dev)
229{
230	int unit,part,phys;
231	struct mcd_data *cd;
232
233	unit = mcd_unit(dev);
234	if (unit >= NMCD)
235		return ENXIO;
236
237	cd = mcd_data + unit;
238	part = mcd_part(dev);
239	phys = mcd_phys(dev);
240
241	/* not initialized*/
242	if (!(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 ENXIO;
248
249	if (mcd_getstat(unit,1) < 0)
250		return ENXIO;
251
252	/* XXX get a default disklabel */
253	mcd_getdisklabel(unit);
254
255	if (mcdsize(dev) < 0) {
256		printf("mcd%d: failed to get disk size\n",unit);
257		return ENXIO;
258	} else
259		cd->flags |= MCDVALID;
260
261MCD_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
277int mcdclose(dev_t dev)
278{
279	int unit,part,phys;
280	struct mcd_data *cd;
281
282	unit = mcd_unit(dev);
283	if (unit >= NMCD)
284		return ENXIO;
285
286	cd = mcd_data + unit;
287	part = mcd_part(dev);
288	phys = mcd_phys(dev);
289
290	if (!(cd->flags & MCDINIT))
291		return ENXIO;
292
293	mcd_getstat(unit,1);	/* get status */
294
295	/* close channel */
296	cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
297	cd->openflags &= ~(1<<part);
298	MCD_TRACE("close: partition=%d\n",part,0,0,0);
299
300	return 0;
301}
302
303void
304mcdstrategy(struct buf *bp)
305{
306	struct mcd_data *cd;
307	struct buf *qp;
308	int s;
309
310	int unit = mcd_unit(bp->b_dev);
311
312	cd = mcd_data + unit;
313
314	/* test validity */
315/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
316	bp,unit,bp->b_blkno,bp->b_bcount);*/
317	if (unit >= NMCD || bp->b_blkno < 0) {
318		printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
319			unit, bp->b_blkno, bp->b_bcount);
320		pg("mcd: mcdstratregy failure");
321		bp->b_error = EINVAL;
322		bp->b_flags |= B_ERROR;
323		goto bad;
324	}
325
326	/* if device invalidated (e.g. media change, door open), error */
327	if (!(cd->flags & MCDVALID)) {
328MCD_TRACE("strategy: drive not valid\n",0,0,0,0);
329		bp->b_error = EIO;
330		goto bad;
331	}
332
333	/* read only */
334	if (!(bp->b_flags & B_READ)) {
335		bp->b_error = EROFS;
336		goto bad;
337	}
338
339	/* no data to read */
340	if (bp->b_bcount == 0)
341		goto done;
342
343	/* for non raw access, check partition limits */
344	if (mcd_part(bp->b_dev) != RAW_PART) {
345		if (!(cd->flags & MCDLABEL)) {
346			bp->b_error = EIO;
347			goto bad;
348		}
349		/* adjust transfer if necessary */
350		if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
351			goto done;
352		}
353	} else {
354		bp->b_pblkno = bp->b_blkno;
355		bp->b_cylin = 0;
356	}
357
358	/* queue it */
359	qp = &cd->head;
360	s = splbio();
361	disksort(qp,bp);
362	splx(s);
363
364	/* now check whether we can perform processing */
365	mcd_start(unit);
366	return;
367
368bad:
369	bp->b_flags |= B_ERROR;
370done:
371	bp->b_resid = bp->b_bcount;
372	biodone(bp);
373	return;
374}
375
376static void mcd_start(int unit)
377{
378	struct mcd_data *cd = mcd_data + unit;
379	struct buf *bp, *qp = &cd->head;
380	struct partition *p;
381	int part;
382	register s = splbio();
383
384	if (cd->flags & MCDMBXBSY)
385		return;
386
387	if ((bp = qp->b_actf) != 0) {
388		/* block found to process, dequeue */
389		/*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
390		qp->b_actf = bp->av_forw;
391		splx(s);
392	} else {
393		/* nothing to do */
394		splx(s);
395		return;
396	}
397
398	/* changed media? */
399	if (!(cd->flags	& MCDVALID)) {
400		MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
401		return;
402	}
403
404	p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
405
406	cd->flags |= MCDMBXBSY;
407	cd->mbx.unit = unit;
408	cd->mbx.port = cd->iobase;
409	cd->mbx.retry = MCD_RETRYS;
410	cd->mbx.bp = bp;
411	cd->mbx.p_offset = p->p_offset;
412
413	/* calling the read routine */
414	mcd_doread(MCD_S_BEGIN,&(cd->mbx));
415	/* triggers mcd_start, when successful finished */
416	return;
417}
418
419int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
420{
421	struct mcd_data *cd;
422	int unit,part;
423
424	unit = mcd_unit(dev);
425	part = mcd_part(dev);
426	cd = mcd_data + unit;
427
428#ifdef MCDMINI
429	return ENOTTY;
430#else
431	if (!(cd->flags & MCDVALID))
432		return EIO;
433MCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
434
435	switch (cmd) {
436	case DIOCSBAD:
437		return EINVAL;
438	case DIOCGDINFO:
439	case DIOCGPART:
440	case DIOCWDINFO:
441	case DIOCSDINFO:
442	case DIOCWLABEL:
443		return ENOTTY;
444	case CDIOCPLAYTRACKS:
445		return mcd_playtracks(unit, (struct ioc_play_track *) addr);
446	case CDIOCPLAYBLOCKS:
447		return mcd_play(unit, (struct mcd_read2 *) addr);
448	case CDIOCREADSUBCHANNEL:
449		return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
450	case CDIOREADTOCHEADER:
451		return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
452	case CDIOREADTOCENTRYS:
453		return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr);
454	case CDIOCSETPATCH:
455	case CDIOCGETVOL:
456	case CDIOCSETVOL:
457	case CDIOCSETMONO:
458	case CDIOCSETSTERIO:
459	case CDIOCSETMUTE:
460	case CDIOCSETLEFT:
461	case CDIOCSETRIGHT:
462		return EINVAL;
463	case CDIOCRESUME:
464		return mcd_resume(unit);
465	case CDIOCPAUSE:
466		return mcd_pause(unit);
467	case CDIOCSTART:
468		return EINVAL;
469	case CDIOCSTOP:
470		return mcd_stop(unit);
471	case CDIOCEJECT:
472		return EINVAL;
473	case CDIOCSETDEBUG:
474		cd->debug = 1;
475		return 0;
476	case CDIOCCLRDEBUG:
477		cd->debug = 0;
478		return 0;
479	case CDIOCRESET:
480		return EINVAL;
481	default:
482		return ENOTTY;
483	}
484	/*NOTREACHED*/
485#endif /*!MCDMINI*/
486}
487
488/* this could have been taken from scsi/cd.c, but it is not clear
489 * whether the scsi cd driver is linked in
490 */
491static int mcd_getdisklabel(int unit)
492{
493	struct mcd_data *cd = mcd_data + unit;
494
495	if (cd->flags & MCDLABEL)
496		return -1;
497
498	bzero(&cd->dlabel,sizeof(struct disklabel));
499	strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16);
500	strncpy(cd->dlabel.d_packname,"unknown        ",16);
501	cd->dlabel.d_secsize 	= cd->blksize;
502	cd->dlabel.d_nsectors	= 100;
503	cd->dlabel.d_ntracks	= 1;
504	cd->dlabel.d_ncylinders	= (cd->disksize/100)+1;
505	cd->dlabel.d_secpercyl	= 100;
506	cd->dlabel.d_secperunit	= cd->disksize;
507	cd->dlabel.d_rpm	= 300;
508	cd->dlabel.d_interleave	= 1;
509	cd->dlabel.d_flags	= D_REMOVABLE;
510	cd->dlabel.d_npartitions= 1;
511	cd->dlabel.d_partitions[0].p_offset = 0;
512	cd->dlabel.d_partitions[0].p_size = cd->disksize;
513	cd->dlabel.d_partitions[0].p_fstype = 9;
514	cd->dlabel.d_magic	= DISKMAGIC;
515	cd->dlabel.d_magic2	= DISKMAGIC;
516	cd->dlabel.d_checksum	= dkcksum(&cd->dlabel);
517
518	cd->flags |= MCDLABEL;
519	return 0;
520}
521
522int mcdsize(dev_t dev)
523{
524	int size;
525	int unit = mcd_unit(dev);
526	struct mcd_data *cd = mcd_data + unit;
527
528	if (mcd_volinfo(unit) >= 0) {
529		cd->blksize = MCDBLK;
530		size = msf2hsg(cd->volinfo.vol_msf);
531		cd->disksize = size * (MCDBLK/DEV_BSIZE);
532		return 0;
533	}
534	return -1;
535}
536
537/***************************************************************
538 * lower level of driver starts here
539 **************************************************************/
540
541#ifdef NOTDEF
542static char
543irqs[] = {
544	0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
545	0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
546};
547
548static char
549drqs[] = {
550	0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
551};
552#endif
553
554static void
555mcd_configure(struct mcd_data *cd)
556{
557	outb(cd->iobase+mcd_config,cd->config);
558}
559
560/* Wait for non-busy - return 0 on timeout */
561static int
562twiddle_thumbs(int port, int unit, int count, char *whine)
563{
564	int i;
565
566	for (i = 0; i < count; i++) {
567		if (!(inb(port+MCD_FLAGS) & MCD_ST_BUSY)) {
568			return 1;
569		}
570	}
571#ifdef MCD_TO_WARNING_ON
572	printf("mcd%d: timeout %s\n", unit, whine);
573#endif
574	return 0;
575}
576
577/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
578
579int
580mcd_probe(struct isa_device *dev)
581{
582	int port = dev->id_iobase;
583	int unit = dev->id_unit;
584	int i, j;
585	int status;
586	unsigned char stbytes[3];
587
588	mcd_data[unit].flags = MCDPROBING;
589
590#ifdef NOTDEF
591	/* get irq/drq configuration word */
592	mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
593#else
594	mcd_data[unit].config = 0;
595#endif
596
597	/* send a reset */
598	outb(port+MCD_FLAGS, M_RESET);
599
600	/*
601	 * delay awhile by getting any pending garbage (old data) and
602	 * throwing it away.
603	 */
604	for (i = 1000000; i != 0; i--) {
605		inb(port+MCD_FLAGS);
606	}
607
608	/* Get status */
609	outb(port+MCD_DATA, MCD_CMDGETSTAT);
610	if (!twiddle_thumbs(port, unit, 1000000, "getting status")) {
611		return 0;	/* Timeout */
612	}
613	status = inb(port+MCD_DATA);
614	if (status != MCDCDABSENT && status != MCDCDPRESENT &&
615		status != MCDSOPEN && status != MCDSCLOSED)
616		return 0;	/* Not actually a Mitsumi drive here */
617	/* Get version information */
618	outb(port+MCD_DATA, MCD_CMDCONTINFO);
619	for (j = 0; j < 3; j++) {
620		if (!twiddle_thumbs(port, unit, 3000, "getting version info")) {
621			return 0;
622		}
623		stbytes[j] = (inb(port+MCD_DATA) & 0xFF);
624	}
625	printf("mcd%d: version information is %x %c %x\n", unit,
626		stbytes[0], stbytes[1], stbytes[2]);
627	if (stbytes[1] >= 4) {
628		outb(port+MCD_CTRL, M_PICKLE);
629		printf("mcd%d: Adjusted for newer drive model\n", unit);
630	}
631	return 4;
632}
633
634
635static int
636mcd_waitrdy(int port,int dly)
637{
638	int i;
639
640	/* wait until xfer port senses data ready */
641	for (i=0; i<dly; i++) {
642		if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0)
643			return 0;
644		mcd_delay(1);
645	}
646	return -1;
647}
648
649static int
650mcd_getreply(int unit,int dly)
651{
652	int	i;
653	struct	mcd_data *cd = mcd_data + unit;
654	int	port = cd->iobase;
655
656	/* wait data to become ready */
657	if (mcd_waitrdy(port,dly)<0) {
658#ifdef MCD_TO_WARNING_ON
659		printf("mcd%d: timeout getreply\n",unit);
660#endif
661		return -1;
662	}
663
664	/* get the data */
665	return inb(port+mcd_status) & 0xFF;
666}
667
668static int
669mcd_getstat(int unit,int sflg)
670{
671	int	i;
672	struct	mcd_data *cd = mcd_data + unit;
673	int	port = cd->iobase;
674
675	/* get the status */
676	if (sflg)
677		outb(port+mcd_command, MCD_CMDGETSTAT);
678	i = mcd_getreply(unit,DELAY_GETREPLY);
679	if (i<0) return -1;
680
681	cd->status = i;
682
683	mcd_setflags(unit,cd);
684	return cd->status;
685}
686
687static void
688mcd_setflags(int unit, struct mcd_data *cd)
689{
690	/* check flags */
691	if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
692		MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
693		cd->flags &= ~MCDVALID;
694	}
695
696#ifndef MCDMINI
697	if (cd->status & MCDAUDIOBSY)
698		cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
699	else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
700		cd->audio_status = CD_AS_PLAY_COMPLETED;
701#endif
702}
703
704static int
705mcd_get(int unit, char *buf, int nmax)
706{
707	int port = mcd_data[unit].iobase;
708	int i,k;
709
710	for (i=0; i<nmax; i++) {
711		/* wait for data */
712		if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
713#ifdef MCD_TO_WARNING_ON
714			printf("mcd%d: timeout mcd_get\n",unit);
715#endif
716			return -1;
717		}
718		buf[i] = k;
719	}
720	return i;
721}
722
723static int
724mcd_send(int unit, int cmd,int nretrys)
725{
726	int i,k;
727	int port = mcd_data[unit].iobase;
728
729/*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
730	for (i=0; i<nretrys; i++) {
731		outb(port+mcd_command, cmd);
732		if ((k=mcd_getstat(unit,0)) != -1) {
733			break;
734		}
735	}
736	if (i == nretrys) {
737		printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
738		return -1;
739	}
740/*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
741	return 0;
742}
743
744static int
745bcd2bin(bcd_t b)
746{
747	return (b >> 4) * 10 + (b & 15);
748}
749
750static bcd_t
751bin2bcd(int b)
752{
753	return ((b / 10) << 4) | (b % 10);
754}
755
756static void
757hsg2msf(int hsg, bcd_t *msf)
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
766static int
767msf2hsg(bcd_t *msf)
768{
769	return (bcd2bin(M_msf(msf)) * 60 +
770		bcd2bin(S_msf(msf))) * 75 +
771		bcd2bin(F_msf(msf)) - 150;
772}
773
774static int
775mcd_volinfo(int unit)
776{
777	struct mcd_data *cd = mcd_data + unit;
778	int i;
779
780/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
781
782	/* Get the status, in case the disc has been changed */
783	if (mcd_getstat(unit, 1) < 0) return EIO;
784
785	/* Just return if we already have it */
786	if (cd->flags & MCDVOLINFO) return 0;
787
788	/* send volume info command */
789	if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
790		return -1;
791
792	/* get data */
793	if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
794		printf("mcd%d: mcd_volinfo: error read data\n",unit);
795		return -1;
796	}
797
798	if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
799		cd->flags |= MCDVOLINFO;	/* volinfo is OK */
800		return 0;
801	}
802
803	return -1;
804}
805
806void
807mcdintr(unit)
808	int unit;
809{
810	int	port = mcd_data[unit].iobase;
811	u_int	i;
812
813	MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0);
814
815	/* just read out status and ignore the rest */
816	if ((inb(port+mcd_xfer)&0xFF) != 0xFF) {
817		i = inb(port+mcd_status);
818	}
819}
820
821/* state machine to process read requests
822 * initialize with MCD_S_BEGIN: calculate sizes, and read status
823 * MCD_S_WAITSTAT: wait for status reply, set mode
824 * MCD_S_WAITMODE: waits for status reply from set mode, set read command
825 * MCD_S_WAITREAD: wait for read ready, read data
826 */
827static struct mcd_mbx *mbxsave;
828
829static void
830mcd_doread(int state, struct mcd_mbx *mbxin)
831{
832	struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
833	int	unit = mbx->unit;
834	int	port = mbx->port;
835	struct	buf *bp = mbx->bp;
836	struct	mcd_data *cd = mcd_data + unit;
837
838	int	rm,i,k;
839	struct mcd_read2 rbuf;
840	int	blknum;
841	caddr_t	addr;
842
843loop:
844	switch (state) {
845	case MCD_S_BEGIN:
846		mbx = mbxsave = mbxin;
847
848	case MCD_S_BEGIN1:
849		/* get status */
850		outb(port+mcd_command, MCD_CMDGETSTAT);
851		mbx->count = RDELAY_WAITSTAT;
852		timeout((timeout_func_t)mcd_doread,
853			(caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
854		return;
855	case MCD_S_WAITSTAT:
856		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT);
857		if (mbx->count-- >= 0) {
858			if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
859				timeout((timeout_func_t)mcd_doread,
860				    (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
861				return;
862			}
863			mcd_setflags(unit,cd);
864			MCD_TRACE("got WAITSTAT delay=%d\n",
865				RDELAY_WAITSTAT-mbx->count,0,0,0);
866			/* reject, if audio active */
867			if (cd->status & MCDAUDIOBSY) {
868				printf("mcd%d: audio is active\n",unit);
869				goto readerr;
870			}
871
872			/* to check for raw/cooked mode */
873			if (cd->flags & MCDREADRAW) {
874				rm = MCD_MD_RAW;
875				mbx->sz = MCDRBLK;
876			} else {
877				rm = MCD_MD_COOKED;
878				mbx->sz = cd->blksize;
879			}
880
881			mbx->count = RDELAY_WAITMODE;
882
883			mcd_put(port+mcd_command, MCD_CMDSETMODE);
884			mcd_put(port+mcd_command, rm);
885			timeout((timeout_func_t)mcd_doread,
886				(caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */
887			return;
888		} else {
889#ifdef MCD_TO_WARNING_ON
890			printf("mcd%d: timeout getstatus\n",unit);
891#endif
892			goto readerr;
893		}
894
895	case MCD_S_WAITMODE:
896		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE);
897		if (mbx->count-- < 0) {
898#ifdef MCD_TO_WARNING_ON
899			printf("mcd%d: timeout set mode\n",unit);
900#endif
901			goto readerr;
902		}
903		if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
904			timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100);
905			return;
906		}
907		mcd_setflags(unit,cd);
908		MCD_TRACE("got WAITMODE delay=%d\n",
909			RDELAY_WAITMODE-mbx->count,0,0,0);
910		/* for first block */
911		mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
912		mbx->skip = 0;
913
914nextblock:
915		blknum 	= (bp->b_blkno / (mbx->sz/DEV_BSIZE))
916			+ mbx->p_offset + mbx->skip/mbx->sz;
917
918		MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",
919			blknum,bp,0,0);
920
921		/* build parameter block */
922		hsg2msf(blknum,rbuf.start_msf);
923
924		/* send the read command */
925		mcd_put(port+mcd_command,MCD_CMDREAD2);
926		mcd_put(port+mcd_command,rbuf.start_msf[0]);
927		mcd_put(port+mcd_command,rbuf.start_msf[1]);
928		mcd_put(port+mcd_command,rbuf.start_msf[2]);
929		mcd_put(port+mcd_command,0);
930		mcd_put(port+mcd_command,0);
931		mcd_put(port+mcd_command,1);
932		mbx->count = RDELAY_WAITREAD;
933		timeout((timeout_func_t)mcd_doread,
934			(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
935		return;
936	case MCD_S_WAITREAD:
937		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD);
938		if (mbx->count-- > 0) {
939			k = inb(port+mcd_xfer);
940			if ((k & 2)==0) {
941				MCD_TRACE("got data delay=%d\n",
942					RDELAY_WAITREAD-mbx->count,0,0,0);
943				/* data is ready */
944				addr	= bp->b_un.b_addr + mbx->skip;
945				outb(port+mcd_ctl2,0x04);	/* XXX */
946				for (i=0; i<mbx->sz; i++)
947					*addr++	= inb(port+mcd_rdata);
948				outb(port+mcd_ctl2,0x0c);	/* XXX */
949
950				if (--mbx->nblk > 0) {
951					mbx->skip += mbx->sz;
952					goto nextblock;
953				}
954
955				/* return buffer */
956				bp->b_resid = 0;
957				biodone(bp);
958
959				cd->flags &= ~MCDMBXBSY;
960				mcd_start(mbx->unit);
961				return;
962			}
963			if ((k & 4)==0)
964				mcd_getstat(unit,0);
965			timeout((timeout_func_t)mcd_doread,
966				(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
967			return;
968		} else {
969#ifdef MCD_TO_WARNING_ON
970			printf("mcd%d: timeout read data\n",unit);
971#endif
972			goto readerr;
973		}
974	}
975
976readerr:
977	if (mbx->retry-- > 0) {
978#ifdef MCD_TO_WARNING_ON
979		printf("mcd%d: retrying\n",unit);
980#endif
981		state = MCD_S_BEGIN1;
982		goto loop;
983	}
984
985	/* invalidate the buffer */
986	bp->b_flags |= B_ERROR;
987	bp->b_resid = bp->b_bcount;
988	biodone(bp);
989	mcd_start(mbx->unit);
990	return;
991
992#ifdef NOTDEF
993	printf("mcd%d: unit timeout, resetting\n",mbx->unit);
994	outb(mbx->port+mcd_reset,MCD_CMDRESET);
995	DELAY(300000);
996	(void)mcd_getstat(mbx->unit,1);
997	(void)mcd_getstat(mbx->unit,1);
998	/*cd->status &= ~MCDDSKCHNG; */
999	cd->debug = 1; /* preventive set debug mode */
1000
1001#endif
1002
1003}
1004
1005#ifndef MCDMINI
1006static int
1007mcd_setmode(int unit, int mode)
1008{
1009	struct mcd_data *cd = mcd_data + unit;
1010	int port = cd->iobase;
1011	int retry;
1012
1013	printf("mcd%d: setting mode to %d\n", unit, mode);
1014	for(retry=0; retry<MCD_RETRYS; retry++)
1015	{
1016		outb(port+mcd_command, MCD_CMDSETMODE);
1017		outb(port+mcd_command, mode);
1018		if (mcd_getstat(unit, 0) != -1) return 0;
1019	}
1020
1021	return -1;
1022}
1023
1024static int
1025mcd_toc_header(int unit, struct ioc_toc_header *th)
1026{
1027	struct mcd_data *cd = mcd_data + unit;
1028
1029	if (mcd_volinfo(unit) < 0) {
1030		return ENXIO;
1031	}
1032
1033	th->len = msf2hsg(cd->volinfo.vol_msf);
1034	th->starting_track = bcd2bin(cd->volinfo.trk_low);
1035	th->ending_track = bcd2bin(cd->volinfo.trk_high);
1036
1037	return 0;
1038}
1039
1040static int
1041mcd_read_toc(int unit)
1042{
1043	struct mcd_data *cd = mcd_data + unit;
1044	struct ioc_toc_header th;
1045	struct mcd_qchninfo q;
1046	int rc, trk, idx, retry;
1047
1048	/* Only read TOC if needed */
1049	if (cd->flags & MCDTOC) {
1050		return 0;
1051	}
1052
1053	printf("mcd%d: reading toc header\n", unit);
1054	if (mcd_toc_header(unit, &th) != 0) {
1055		return ENXIO;
1056	}
1057
1058	printf("mcd%d: stopping play\n", unit);
1059	if ((rc=mcd_stop(unit)) != 0) {
1060		return rc;
1061	}
1062
1063	/* try setting the mode twice */
1064	if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
1065		return EIO;
1066	}
1067	if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
1068		return EIO;
1069	}
1070
1071	printf("mcd%d: get_toc reading qchannel info\n",unit);
1072	for(trk=th.starting_track; trk<=th.ending_track; trk++)
1073		cd->toc[trk].idx_no = 0;
1074	trk = th.ending_track - th.starting_track + 1;
1075	for(retry=0; retry<300 && trk>0; retry++)
1076	{
1077		if (mcd_getqchan(unit, &q) < 0) break;
1078		idx = bcd2bin(q.idx_no);
1079		if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0) {
1080			if (cd->toc[idx].idx_no == 0) {
1081				cd->toc[idx] = q;
1082				trk--;
1083			}
1084		}
1085	}
1086
1087	if (mcd_setmode(unit, MCD_MD_COOKED) != 0) {
1088		return EIO;
1089	}
1090
1091	if (trk != 0) {
1092		return ENXIO;
1093	}
1094
1095	/* add a fake last+1 */
1096	idx = th.ending_track + 1;
1097	cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
1098	cd->toc[idx].trk_no = 0;
1099	cd->toc[idx].idx_no = 0xAA;
1100	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
1101	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
1102	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
1103
1104	cd->flags |= MCDTOC;
1105
1106	return 0;
1107}
1108
1109static int
1110mcd_toc_entry(int unit, struct ioc_read_toc_entry *te)
1111{
1112	struct mcd_data *cd = mcd_data + unit;
1113	struct ret_toc {
1114		struct ioc_toc_header th;
1115		struct cd_toc_entry rt;
1116	} ret_toc;
1117	struct ioc_toc_header th;
1118	int rc, i;
1119
1120	/* Make sure we have a valid toc */
1121	if ((rc=mcd_read_toc(unit)) != 0) {
1122		return rc;
1123	}
1124
1125	/* find the toc to copy*/
1126	i = te->starting_track;
1127	if (i == MCD_LASTPLUS1) {
1128		i = bcd2bin(cd->volinfo.trk_high) + 1;
1129	}
1130
1131	/* verify starting track */
1132	if (i < bcd2bin(cd->volinfo.trk_low) ||
1133		i > bcd2bin(cd->volinfo.trk_high)+1) {
1134		return EINVAL;
1135	}
1136
1137	/* do we have room */
1138	if (te->data_len < sizeof(struct ioc_toc_header) +
1139		sizeof(struct cd_toc_entry)) {
1140		return EINVAL;
1141	}
1142
1143	/* Copy the toc header */
1144	if (mcd_toc_header(unit, &th) < 0) {
1145		return EIO;
1146	}
1147	ret_toc.th = th;
1148
1149	/* copy the toc data */
1150	ret_toc.rt.control = cd->toc[i].ctrl_adr;
1151	ret_toc.rt.addr_type = te->address_format;
1152	ret_toc.rt.track = i;
1153	if (te->address_format == CD_MSF_FORMAT) {
1154		ret_toc.rt.addr.addr[1] = cd->toc[i].hd_pos_msf[0];
1155		ret_toc.rt.addr.addr[2] = cd->toc[i].hd_pos_msf[1];
1156		ret_toc.rt.addr.addr[3] = cd->toc[i].hd_pos_msf[2];
1157	}
1158
1159	/* copy the data back */
1160	copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
1161		+ sizeof(struct ioc_toc_header));
1162
1163	return 0;
1164}
1165
1166static int
1167mcd_stop(int unit)
1168{
1169	struct mcd_data *cd = mcd_data + unit;
1170
1171	if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) {
1172		return ENXIO;
1173	}
1174	cd->audio_status = CD_AS_PLAY_COMPLETED;
1175	return 0;
1176}
1177
1178static int
1179mcd_getqchan(int unit, struct mcd_qchninfo *q)
1180{
1181	struct mcd_data *cd = mcd_data + unit;
1182
1183	if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) {
1184		return -1;
1185	}
1186	if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) {
1187		return -1;
1188	}
1189	if (cd->debug) {
1190		printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
1191		unit,
1192		q->ctrl_adr, q->trk_no, q->idx_no,
1193		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
1194		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
1195	}
1196	return 0;
1197}
1198
1199static int
1200mcd_subchan(int unit, struct ioc_read_subchannel *sc)
1201{
1202	struct mcd_data *cd = mcd_data + unit;
1203	struct mcd_qchninfo q;
1204	struct cd_sub_channel_info data;
1205
1206	printf("mcd%d: subchan af=%d, df=%d\n", unit,
1207		sc->address_format,
1208		sc->data_format);
1209	if (sc->address_format != CD_MSF_FORMAT) {
1210		return EIO;
1211	}
1212	if (sc->data_format != CD_CURRENT_POSITION) {
1213		return EIO;
1214	}
1215	if (mcd_getqchan(unit, &q) < 0) {
1216		return EIO;
1217	}
1218
1219	data.header.audio_status = cd->audio_status;
1220	data.what.position.data_format = CD_MSF_FORMAT;
1221	data.what.position.track_number = bcd2bin(q.trk_no);
1222
1223	if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0) {
1224		return EFAULT;
1225	}
1226	return 0;
1227}
1228
1229static int
1230mcd_playtracks(int unit, struct ioc_play_track *pt)
1231{
1232	struct mcd_data *cd = mcd_data + unit;
1233	struct mcd_read2 pb;
1234	int a = pt->start_track;
1235	int z = pt->end_track;
1236	int rc;
1237
1238	if ((rc = mcd_read_toc(unit)) != 0) {
1239		return rc;
1240	}
1241	printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
1242		a, pt->start_index, z, pt->end_index);
1243
1244	if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
1245	    z < cd->volinfo.trk_low || z > cd->volinfo.trk_high) {
1246		return EINVAL;
1247	}
1248
1249	pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
1250	pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
1251	pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
1252	pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
1253	pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
1254	pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
1255
1256	return mcd_play(unit, &pb);
1257}
1258
1259static int
1260mcd_play(int unit, struct mcd_read2 *pb)
1261{
1262	struct mcd_data *cd = mcd_data + unit;
1263	int port = cd->iobase;
1264	int retry, st;
1265
1266	cd->lastpb = *pb;
1267	for(retry=0; retry<MCD_RETRYS; retry++) {
1268		outb(port+mcd_command, MCD_CMDREAD2);
1269		outb(port+mcd_command, pb->start_msf[0]);
1270		outb(port+mcd_command, pb->start_msf[1]);
1271		outb(port+mcd_command, pb->start_msf[2]);
1272		outb(port+mcd_command, pb->end_msf[0]);
1273		outb(port+mcd_command, pb->end_msf[1]);
1274		outb(port+mcd_command, pb->end_msf[2]);
1275		if ((st=mcd_getstat(unit, 0)) != -1) {
1276			break;
1277		}
1278	}
1279
1280	if (cd->debug) {
1281		printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st);
1282	}
1283	if (st == -1) {
1284		return ENXIO;
1285	}
1286	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
1287	return 0;
1288}
1289
1290static int
1291mcd_pause(int unit)
1292{
1293	struct mcd_data *cd = mcd_data + unit;
1294	struct mcd_qchninfo q;
1295	int rc;
1296
1297	/* Verify current status */
1298	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) {
1299		printf("mcd%d: pause attempted when not playing\n", unit);
1300		return EINVAL;
1301	}
1302
1303	/* Get the current position */
1304	if (mcd_getqchan(unit, &q) < 0) {
1305		return EIO;
1306	}
1307
1308	/* Copy it into lastpb */
1309	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
1310	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
1311	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
1312
1313	/* Stop playing */
1314	if ((rc=mcd_stop(unit)) != 0) {
1315		return rc;
1316	}
1317
1318	/* Set the proper status and exit */
1319	cd->audio_status = CD_AS_PLAY_PAUSED;
1320	return 0;
1321}
1322
1323static int
1324mcd_resume(int unit)
1325{
1326	struct mcd_data *cd = mcd_data + unit;
1327
1328	if (cd->audio_status != CD_AS_PLAY_PAUSED) {
1329		return EINVAL;
1330	}
1331	return mcd_play(unit, &cd->lastpb);
1332}
1333#endif /*!MCDMINI*/
1334
1335#endif /* NMCD > 0 */
1336