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