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