mcd.c revision 13822
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.63 1996/01/30 23:27:20 ache Exp $
44 */
45static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
46
47#include "mcd.h"
48#if NMCD > 0
49#include <sys/types.h>
50#include <sys/param.h>
51#include <sys/systm.h>
52#include <sys/conf.h>
53#include <sys/file.h>
54#include <sys/buf.h>
55#include <sys/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, int relative);
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, 0);
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	F_msf(msf) = bin2bcd(hsg % 75);
946	hsg /= 75;
947	S_msf(msf) = bin2bcd(hsg % 60);
948	hsg /= 60;
949	M_msf(msf) = bin2bcd(hsg);
950}
951
952static int
953msf2hsg(bcd_t *msf, int relative)
954{
955	return (bcd2bin(M_msf(msf)) * 60 + bcd2bin(S_msf(msf))) * 75 +
956		bcd2bin(F_msf(msf)) - (!relative) * 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->starting_track = bcd2bin(cd->volinfo.trk_low);
1347	th->ending_track = bcd2bin(cd->volinfo.trk_high);
1348	th->len = sizeof(struct ioc_toc_header) +
1349		  (th->ending_track - th->starting_track + 1) *
1350		  sizeof(struct cd_toc_entry);
1351
1352	return 0;
1353}
1354
1355static int
1356mcd_read_toc(int unit)
1357{
1358	struct mcd_data *cd = mcd_data + unit;
1359	struct ioc_toc_header th;
1360	struct mcd_qchninfo q;
1361	int rc, trk, idx, retry;
1362
1363	/* Only read TOC if needed */
1364	if (cd->flags & MCDTOC)
1365		return 0;
1366
1367	if (cd->debug)
1368		printf("mcd%d: reading toc header\n", unit);
1369
1370	if ((rc = mcd_toc_header(unit, &th)) != 0)
1371		return rc;
1372
1373	if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0)
1374		return EIO;
1375
1376	if (mcd_setmode(unit, MCD_MD_TOC) != 0)
1377		return EIO;
1378
1379	if (cd->debug)
1380		printf("mcd%d: get_toc reading qchannel info\n",unit);
1381
1382	for(trk=th.starting_track; trk<=th.ending_track; trk++)
1383		cd->toc[trk].idx_no = 0;
1384	trk = th.ending_track - th.starting_track + 1;
1385	for(retry=0; retry<600 && trk>0; retry++)
1386	{
1387		if (mcd_getqchan(unit, &q) < 0) break;
1388		idx = bcd2bin(q.idx_no);
1389		if (idx>=th.starting_track && idx<=th.ending_track && q.trk_no==0) {
1390			if (cd->toc[idx].idx_no == 0) {
1391				cd->toc[idx] = q;
1392				trk--;
1393			}
1394		}
1395	}
1396
1397	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1398		return EIO;
1399
1400	if (trk != 0)
1401		return ENXIO;
1402
1403	/* add a fake last+1 */
1404	idx = th.ending_track + 1;
1405	cd->toc[idx].control = cd->toc[idx-1].control;
1406	cd->toc[idx].addr_type = cd->toc[idx-1].addr_type;
1407	cd->toc[idx].trk_no = 0;
1408	cd->toc[idx].idx_no = MCD_LASTPLUS1;
1409	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
1410	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
1411	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
1412
1413	if (cd->debug)
1414	{ int i;
1415	for (i = th.starting_track; i <= idx; i++)
1416		printf("mcd%d: trk %d idx %d pos %d %d %d\n",
1417			unit, i,
1418			cd->toc[i].idx_no > 0x99 ? cd->toc[i].idx_no :
1419			bcd2bin(cd->toc[i].idx_no),
1420			bcd2bin(cd->toc[i].hd_pos_msf[0]),
1421			bcd2bin(cd->toc[i].hd_pos_msf[1]),
1422			bcd2bin(cd->toc[i].hd_pos_msf[2]));
1423	}
1424
1425	cd->flags |= MCDTOC;
1426
1427	return 0;
1428}
1429
1430static int
1431mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te)
1432{
1433	struct mcd_data *cd = mcd_data + unit;
1434	struct cd_toc_entry entries[MCD_MAXTOCS];
1435	struct ioc_toc_header th;
1436	int rc, n, trk, len;
1437
1438	if (   te->data_len < sizeof(entries[0])
1439	    || (te->data_len % sizeof(entries[0])) != 0
1440	    || te->address_format != CD_MSF_FORMAT
1441	    && te->address_format != CD_LBA_FORMAT
1442	   )
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	len = ((th.ending_track + 1 - trk) + 1) *
1459		sizeof(entries[0]);
1460	if (te->data_len < len)
1461		len = te->data_len;
1462	if (len > sizeof(entries))
1463		return EINVAL;
1464
1465	/* Make sure we have a valid toc */
1466	if ((rc=mcd_read_toc(unit)) != 0)
1467		return rc;
1468
1469	/* Copy the TOC data. */
1470	for (n = 0; len > 0 && trk <= th.ending_track + 1; trk++) {
1471		if (cd->toc[trk].idx_no == 0)
1472			continue;
1473		entries[n].control = cd->toc[trk].control;
1474		entries[n].addr_type = cd->toc[trk].addr_type;
1475		entries[n].track =
1476			cd->toc[trk].idx_no > 0x99 ? cd->toc[trk].idx_no :
1477			bcd2bin(cd->toc[trk].idx_no);
1478		switch (te->address_format) {
1479		case CD_MSF_FORMAT:
1480			entries[n].addr.msf.unused = 0;
1481			entries[n].addr.msf.minute = bcd2bin(cd->toc[trk].hd_pos_msf[0]);
1482			entries[n].addr.msf.second = bcd2bin(cd->toc[trk].hd_pos_msf[1]);
1483			entries[n].addr.msf.frame = bcd2bin(cd->toc[trk].hd_pos_msf[2]);
1484			break;
1485		case CD_LBA_FORMAT:
1486			entries[n].addr.lba = htonl(msf2hsg(cd->toc[trk].hd_pos_msf, 0));
1487			break;
1488		}
1489		len -= sizeof(struct cd_toc_entry);
1490		n++;
1491	}
1492
1493	/* copy the data back */
1494	return copyout(entries, te->data, n * sizeof(struct cd_toc_entry));
1495}
1496
1497static int
1498mcd_stop(int unit)
1499{
1500	struct mcd_data *cd = mcd_data + unit;
1501
1502	/* Verify current status */
1503	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS &&
1504	    cd->audio_status != CD_AS_PLAY_PAUSED &&
1505	    cd->audio_status != CD_AS_PLAY_COMPLETED) {
1506		if (cd->debug)
1507			printf("mcd%d: stop attempted when not playing, audio status %d\n",
1508				unit, cd->audio_status);
1509		return EINVAL;
1510	}
1511	if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
1512		if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0)
1513			return EIO;
1514	cd->audio_status = CD_AS_PLAY_COMPLETED;
1515	return 0;
1516}
1517
1518static int
1519mcd_getqchan(int unit, struct mcd_qchninfo *q)
1520{
1521	struct mcd_data *cd = mcd_data + unit;
1522
1523	if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0)
1524		return -1;
1525	if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0)
1526		return -1;
1527	if (cd->debug) {
1528		printf("mcd%d: getqchan control=0x%x addr_type=0x%x trk=%d ind=%d ttm=%d:%d.%d dtm=%d:%d.%d\n",
1529		unit,
1530		q->control, q->addr_type, bcd2bin(q->trk_no),
1531		bcd2bin(q->idx_no),
1532		bcd2bin(q->trk_size_msf[0]), bcd2bin(q->trk_size_msf[1]),
1533		bcd2bin(q->trk_size_msf[2]),
1534		bcd2bin(q->hd_pos_msf[0]), bcd2bin(q->hd_pos_msf[1]),
1535		bcd2bin(q->hd_pos_msf[2]));
1536	}
1537	return 0;
1538}
1539
1540static int
1541mcd_subchan(int unit, struct ioc_read_subchannel *sc)
1542{
1543	struct mcd_data *cd = mcd_data + unit;
1544	struct mcd_qchninfo q;
1545	struct cd_sub_channel_info data;
1546	int lba;
1547
1548	if (cd->debug)
1549		printf("mcd%d: subchan af=%d, df=%d\n", unit,
1550			sc->address_format,
1551			sc->data_format);
1552
1553	if (sc->address_format != CD_MSF_FORMAT &&
1554	    sc->address_format != CD_LBA_FORMAT)
1555		return EINVAL;
1556
1557	if (sc->data_format != CD_CURRENT_POSITION)
1558		return EINVAL;
1559
1560	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1561		return EIO;
1562
1563	if (mcd_getqchan(unit, &q) < 0)
1564		return EIO;
1565
1566	data.header.audio_status = cd->audio_status;
1567	data.what.position.data_format = sc->data_format;
1568	data.what.position.control = q.control;
1569	data.what.position.addr_type = q.addr_type;
1570	data.what.position.track_number = bcd2bin(q.trk_no);
1571	data.what.position.index_number = bcd2bin(q.idx_no);
1572	switch (sc->address_format) {
1573	case CD_MSF_FORMAT:
1574		data.what.position.reladdr.msf.unused = 0;
1575		data.what.position.reladdr.msf.minute = bcd2bin(q.trk_size_msf[0]);
1576		data.what.position.reladdr.msf.second = bcd2bin(q.trk_size_msf[1]);
1577		data.what.position.reladdr.msf.frame = bcd2bin(q.trk_size_msf[2]);
1578		data.what.position.absaddr.msf.unused = 0;
1579		data.what.position.absaddr.msf.minute = bcd2bin(q.hd_pos_msf[0]);
1580		data.what.position.absaddr.msf.second = bcd2bin(q.hd_pos_msf[1]);
1581		data.what.position.absaddr.msf.frame = bcd2bin(q.hd_pos_msf[2]);
1582		break;
1583	case CD_LBA_FORMAT:
1584		lba = msf2hsg(q.trk_size_msf, 1);
1585		/*
1586		 * Pre-gap has index number of 0, and decreasing MSF
1587		 * address.  Must be converted to negative LBA, per
1588		 * SCSI spec.
1589		 */
1590		if (data.what.position.index_number == 0)
1591			lba = -lba;
1592		data.what.position.reladdr.lba = htonl(lba);
1593		data.what.position.absaddr.lba = htonl(msf2hsg(q.hd_pos_msf, 0));
1594		break;
1595	}
1596
1597	return copyout(&data, sc->data, min(sizeof(struct cd_sub_channel_info), sc->data_len));
1598}
1599
1600static int
1601mcd_playmsf(int unit, struct ioc_play_msf *pt)
1602{
1603	struct mcd_read2 pb;
1604
1605	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1606		return EIO;
1607
1608	pb.start_msf[0] = bin2bcd(pt->start_m);
1609	pb.start_msf[1] = bin2bcd(pt->start_s);
1610	pb.start_msf[2] = bin2bcd(pt->start_f);
1611	pb.end_msf[0] = bin2bcd(pt->end_m);
1612	pb.end_msf[1] = bin2bcd(pt->end_s);
1613	pb.end_msf[2] = bin2bcd(pt->end_f);
1614
1615	return mcd_play(unit, &pb);
1616}
1617
1618static int
1619mcd_playtracks(int unit, struct ioc_play_track *pt)
1620{
1621	struct mcd_data *cd = mcd_data + unit;
1622	struct mcd_read2 pb;
1623	int a = pt->start_track;
1624	int z = pt->end_track;
1625	int rc, i;
1626
1627	if ((rc = mcd_read_toc(unit)) != 0)
1628		return rc;
1629
1630	if (cd->debug)
1631		printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
1632			a, pt->start_index, z, pt->end_index);
1633
1634	if (   a < bcd2bin(cd->volinfo.trk_low)
1635	    || a > bcd2bin(cd->volinfo.trk_high)
1636	    || a > z
1637	    || z < bcd2bin(cd->volinfo.trk_low)
1638	    || z > bcd2bin(cd->volinfo.trk_high))
1639		return EINVAL;
1640
1641	for (i = 0; i < 3; i++) {
1642		pb.start_msf[i] = cd->toc[a].hd_pos_msf[i];
1643		pb.end_msf[i] = cd->toc[z+1].hd_pos_msf[i];
1644	}
1645
1646	return mcd_play(unit, &pb);
1647}
1648
1649static int
1650mcd_play(int unit, struct mcd_read2 *pb)
1651{
1652	struct mcd_data *cd = mcd_data + unit;
1653	int com_port = cd->iobase + mcd_command;
1654	int retry, st = -1, status;
1655
1656	cd->lastpb = *pb;
1657	for(retry=0; retry<MCD_RETRYS; retry++) {
1658
1659		disable_intr();
1660		outb(com_port, MCD_CMDSINGLESPEEDREAD);
1661		outb(com_port, pb->start_msf[0]);
1662		outb(com_port, pb->start_msf[1]);
1663		outb(com_port, pb->start_msf[2]);
1664		outb(com_port, pb->end_msf[0]);
1665		outb(com_port, pb->end_msf[1]);
1666		outb(com_port, pb->end_msf[2]);
1667		enable_intr();
1668
1669		status=mcd_getstat(unit, 0);
1670		if (status == -1)
1671			continue;
1672		else if (status != -2)
1673			st = 0;
1674		break;
1675	}
1676
1677	if (status == -2) {
1678		printf("mcd%d: media changed\n", unit);
1679		return ENXIO;
1680	}
1681	if (cd->debug)
1682		printf("mcd%d: mcd_play retry=%d, status=0x%02x\n", unit, retry, status);
1683	if (st < 0)
1684		return ENXIO;
1685	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
1686	return 0;
1687}
1688
1689static int
1690mcd_pause(int unit)
1691{
1692	struct mcd_data *cd = mcd_data + unit;
1693	struct mcd_qchninfo q;
1694	int rc;
1695
1696	/* Verify current status */
1697	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS &&
1698	    cd->audio_status != CD_AS_PLAY_PAUSED) {
1699		if (cd->debug)
1700			printf("mcd%d: pause attempted when not playing, audio status %d\n",
1701			       unit, cd->audio_status);
1702		return EINVAL;
1703	}
1704
1705	/* Get the current position */
1706	if (mcd_getqchan(unit, &q) < 0)
1707		return EIO;
1708
1709	/* Copy it into lastpb */
1710	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
1711	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
1712	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
1713
1714	/* Stop playing */
1715	if ((rc=mcd_stop(unit)) != 0)
1716		return rc;
1717
1718	/* Set the proper status and exit */
1719	cd->audio_status = CD_AS_PLAY_PAUSED;
1720	return 0;
1721}
1722
1723static int
1724mcd_resume(int unit)
1725{
1726	struct mcd_data *cd = mcd_data + unit;
1727
1728	if (cd->audio_status != CD_AS_PLAY_PAUSED)
1729		return EINVAL;
1730	return mcd_play(unit, &cd->lastpb);
1731}
1732
1733
1734static mcd_devsw_installed = 0;
1735
1736static void 	mcd_drvinit(void *unused)
1737{
1738	dev_t dev;
1739
1740	if( ! mcd_devsw_installed ) {
1741		dev = makedev(CDEV_MAJOR,0);
1742		cdevsw_add(&dev,&mcd_cdevsw,NULL);
1743		dev = makedev(BDEV_MAJOR,0);
1744		bdevsw_add(&dev,&mcd_bdevsw,NULL);
1745		mcd_devsw_installed = 1;
1746    	}
1747}
1748
1749SYSINIT(mcddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,mcd_drvinit,NULL)
1750
1751
1752#endif /* NMCD > 0 */
1753