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