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