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