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