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