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