mcd.c revision 2395
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 *
6 * Rewrote probe routine to work on newer Mitsumi drives.
7 * Additional changes (C) 1994 by Jordan K. Hubbard
8 *
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *	This software was developed by Holger Veit and Brian Moore
22 *	for use with "386BSD" and similar operating systems.
23 *    "Similar operating systems" includes mainly non-profit oriented
24 *    systems for research and education, including but not restricted to
25 *    "NetBSD", "FreeBSD", "Mach" (by CMU).
26 * 4. Neither the name of the developer(s) nor the name "386BSD"
27 *    may be used to endorse or promote products derived from this
28 *    software without specific prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
31 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER(S) BE
34 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
35 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
36 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
37 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
38 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
39 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 *
42 *	$Id: mcd.c,v 1.21 1994/08/28 20:37:59 ache Exp $
43 */
44static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
45
46#include "mcd.h"
47#if NMCD > 0
48#include <sys/types.h>
49#include <sys/param.h>
50#include <sys/systm.h>
51#include <sys/conf.h>
52#include <sys/file.h>
53#include <sys/buf.h>
54#include <sys/stat.h>
55#include <sys/uio.h>
56#include <sys/ioctl.h>
57#include <sys/cdio.h>
58#include <sys/errno.h>
59#include <sys/dkbad.h>
60#include <sys/disklabel.h>
61#include <i386/isa/isa.h>
62#include <i386/isa/isa_device.h>
63#include <i386/isa/mcdreg.h>
64
65/* user definable options */
66/*#define MCD_TO_WARNING_ON*/	/* define to get timeout messages */
67/*#define MCDMINI*/ 		/* define for a mini configuration for boot kernel */
68/*#define DEBUG*/
69
70#ifdef MCDMINI
71#define MCD_TRACE(fmt,a,b,c,d)
72#ifdef MCD_TO_WARNING_ON
73#undef MCD_TO_WARNING_ON
74#endif
75#else
76#define MCD_TRACE(fmt,a,b,c,d)	{if (mcd_data[unit].debug) {printf("mcd%d st=%02x: ",unit,mcd_data[unit].status); printf(fmt,a,b,c,d);}}
77#endif
78
79#define mcd_part(dev)	((minor(dev)) & 7)
80#define mcd_unit(dev)	(((minor(dev)) & 0x38) >> 3)
81#define mcd_phys(dev)	(((minor(dev)) & 0x40) >> 6)
82#define RAW_PART        2
83
84/* flags */
85#define MCDOPEN		0x0001	/* device opened */
86#define MCDVALID	0x0002	/* parameters loaded */
87#define MCDINIT		0x0004	/* device is init'd */
88#define MCDWAIT		0x0008	/* waiting for something */
89#define MCDLABEL	0x0010	/* label is read */
90#define	MCDPROBING	0x0020	/* probing */
91#define	MCDREADRAW	0x0040	/* read raw mode (2352 bytes) */
92#define	MCDVOLINFO	0x0080	/* already read volinfo */
93#define	MCDTOC		0x0100	/* already read toc */
94#define	MCDMBXBSY	0x0200	/* local mbx is busy */
95
96/* status */
97#define	MCDAUDIOBSY	MCD_ST_AUDIOBSY		/* playing audio */
98#define MCDDSKCHNG	MCD_ST_DSKCHNG		/* sensed change of disk */
99#define MCDDSKIN	MCD_ST_DSKIN		/* sensed disk in drive */
100#define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
101
102/* These are apparently the different states a mitsumi can get up to */
103#define MCDCDABSENT	0x0030
104#define MCDCDPRESENT	0x0020
105#define MCDSCLOSED	0x0080
106#define MCDSOPEN	0x00a0
107
108/* toc */
109#define MCD_MAXTOCS	104	/* from the Linux driver */
110#define MCD_LASTPLUS1	170	/* special toc entry */
111
112struct mcd_mbx {
113	short		unit;
114	short		port;
115	short		retry;
116	short		nblk;
117	int		sz;
118	u_long		skip;
119	struct buf	*bp;
120	int		p_offset;
121	short		count;
122};
123
124struct mcd_data {
125	short	config;
126	short	flags;
127	short	status;
128	int	blksize;
129	u_long	disksize;
130	int	iobase;
131	struct disklabel dlabel;
132	int	partflags[MAXPARTITIONS];
133	int	openflags;
134	struct mcd_volinfo volinfo;
135#ifndef MCDMINI
136	struct mcd_qchninfo toc[MCD_MAXTOCS];
137	short	audio_status;
138	struct mcd_read2 lastpb;
139#endif
140	short	debug;
141	struct buf head;		/* head of buf queue */
142	struct mcd_mbx mbx;
143} mcd_data[NMCD];
144
145/* reader state machine */
146#define MCD_S_BEGIN	0
147#define MCD_S_BEGIN1	1
148#define MCD_S_WAITSTAT	2
149#define MCD_S_WAITMODE	3
150#define MCD_S_WAITREAD	4
151
152/* prototypes */
153int	mcdopen(dev_t dev);
154int	mcdclose(dev_t dev);
155void	mcdstrategy(struct buf *bp);
156int	mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
157int	mcdsize(dev_t dev);
158static	void	mcd_done(struct mcd_mbx *mbx);
159static	void	mcd_start(int unit);
160static	int	mcd_getdisklabel(int unit);
161static	void	mcd_configure(struct mcd_data *cd);
162static	int	mcd_get(int unit, char *buf, int nmax);
163static	void	mcd_setflags(int unit,struct mcd_data *cd);
164static	int	mcd_getstat(int unit,int sflg);
165static	int	mcd_send(int unit, int cmd,int nretrys);
166static	int	bcd2bin(bcd_t b);
167static	bcd_t	bin2bcd(int b);
168static	void	hsg2msf(int hsg, bcd_t *msf);
169static	int	msf2hsg(bcd_t *msf);
170static	int	mcd_volinfo(int unit);
171static	int	mcd_waitrdy(int port,int dly);
172static 	void	mcd_doread(int state, struct mcd_mbx *mbxin);
173#ifndef MCDMINI
174static	int 	mcd_setmode(int unit, int mode);
175static	int	mcd_getqchan(int unit, struct mcd_qchninfo *q);
176static	int	mcd_subchan(int unit, struct ioc_read_subchannel *sc);
177static	int	mcd_toc_header(int unit, struct ioc_toc_header *th);
178static	int	mcd_read_toc(int unit);
179static	int	mcd_toc_entry(int unit, struct ioc_read_toc_entry *te);
180static	int	mcd_stop(int unit);
181static	int	mcd_playtracks(int unit, struct ioc_play_track *pt);
182static	int	mcd_play(int unit, struct mcd_read2 *pb);
183static	int	mcd_pause(int unit);
184static	int	mcd_resume(int unit);
185#endif
186
187extern	int	hz;
188extern	int	mcd_probe(struct isa_device *dev);
189extern	int	mcd_attach(struct isa_device *dev);
190struct	isa_driver	mcddriver = { mcd_probe, mcd_attach, "mcd" };
191
192#define mcd_put(port,byte)	outb(port,byte)
193
194#define MCD_RETRYS	5
195#define MCD_RDRETRYS	8
196
197#define MCDBLK	2048	/* for cooked mode */
198#define MCDRBLK	2352	/* for raw mode */
199
200/* several delays */
201#define RDELAY_WAITSTAT	300
202#define RDELAY_WAITMODE	300
203#define RDELAY_WAITREAD	800
204
205#define DELAY_GETREPLY	200000l		/* 200000 * 2us */
206
207int mcd_attach(struct isa_device *dev)
208{
209	struct mcd_data *cd = mcd_data + dev->id_unit;
210	int i;
211
212	cd->iobase = dev->id_iobase;
213	cd->flags |= MCDINIT;
214	cd->openflags = 0;
215	for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
216
217#ifdef NOTYET
218	/* wire controller for interrupts and dma */
219	mcd_configure(cd);
220#endif
221
222	return 1;
223}
224
225int mcdopen(dev_t dev)
226{
227	int unit,part,phys;
228	struct mcd_data *cd;
229
230	unit = mcd_unit(dev);
231	if (unit >= NMCD)
232		return ENXIO;
233
234	cd = mcd_data + unit;
235	part = mcd_part(dev);
236	phys = mcd_phys(dev);
237
238	/* not initialized*/
239	if (!(cd->flags & MCDINIT))
240		return ENXIO;
241
242	/* invalidated in the meantime? mark all open part's invalid */
243	if (!(cd->flags & MCDVALID) && cd->openflags)
244		return ENXIO;
245
246	if (mcd_getstat(unit,1) < 0)
247		return ENXIO;
248
249	/* XXX get a default disklabel */
250	mcd_getdisklabel(unit);
251
252	if (mcdsize(dev) < 0) {
253		printf("mcd%d: failed to get disk size\n",unit);
254		return ENXIO;
255	} else
256		cd->flags |= MCDVALID;
257
258MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
259	part,cd->disksize,cd->blksize,0);
260
261	if (part == RAW_PART ||
262		(part < cd->dlabel.d_npartitions &&
263		cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
264		cd->partflags[part] |= MCDOPEN;
265		cd->openflags |= (1<<part);
266		if (part == RAW_PART && phys != 0)
267			cd->partflags[part] |= MCDREADRAW;
268		return 0;
269	}
270
271	return ENXIO;
272}
273
274int mcdclose(dev_t dev)
275{
276	int unit,part,phys;
277	struct mcd_data *cd;
278
279	unit = mcd_unit(dev);
280	if (unit >= NMCD)
281		return ENXIO;
282
283	cd = mcd_data + unit;
284	part = mcd_part(dev);
285	phys = mcd_phys(dev);
286
287	if (!(cd->flags & MCDINIT))
288		return ENXIO;
289
290	mcd_getstat(unit,1);	/* get status */
291
292	/* close channel */
293	cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
294	cd->openflags &= ~(1<<part);
295	MCD_TRACE("close: partition=%d\n",part,0,0,0);
296
297	return 0;
298}
299
300void
301mcdstrategy(struct buf *bp)
302{
303	struct mcd_data *cd;
304	struct buf *qp;
305	int s;
306
307	int unit = mcd_unit(bp->b_dev);
308
309	cd = mcd_data + unit;
310
311	/* test validity */
312/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
313	bp,unit,bp->b_blkno,bp->b_bcount);*/
314	if (unit >= NMCD || bp->b_blkno < 0) {
315		printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
316			unit, bp->b_blkno, bp->b_bcount);
317		pg("mcd: mcdstratregy failure");
318		bp->b_error = EINVAL;
319		bp->b_flags |= B_ERROR;
320		goto bad;
321	}
322
323	/* if device invalidated (e.g. media change, door open), error */
324	if (!(cd->flags & MCDVALID)) {
325MCD_TRACE("strategy: drive not valid\n",0,0,0,0);
326		bp->b_error = EIO;
327		goto bad;
328	}
329
330	/* read only */
331	if (!(bp->b_flags & B_READ)) {
332		bp->b_error = EROFS;
333		goto bad;
334	}
335
336	/* no data to read */
337	if (bp->b_bcount == 0)
338		goto done;
339
340	/* for non raw access, check partition limits */
341	if (mcd_part(bp->b_dev) != RAW_PART) {
342		if (!(cd->flags & MCDLABEL)) {
343			bp->b_error = EIO;
344			goto bad;
345		}
346		/* adjust transfer if necessary */
347		if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
348			goto done;
349		}
350	} else {
351		bp->b_pblkno = bp->b_blkno;
352		bp->b_resid = 0;
353	}
354
355	/* queue it */
356	qp = &cd->head;
357	s = splbio();
358	disksort(qp,bp);
359	splx(s);
360
361	/* now check whether we can perform processing */
362	mcd_start(unit);
363	return;
364
365bad:
366	bp->b_flags |= B_ERROR;
367done:
368	bp->b_resid = bp->b_bcount;
369	biodone(bp);
370	return;
371}
372
373static void mcd_start(int unit)
374{
375	struct mcd_data *cd = mcd_data + unit;
376	struct buf *bp, *qp = &cd->head;
377	struct partition *p;
378	int part;
379	register s = splbio();
380
381	if (cd->flags & MCDMBXBSY)
382		return;
383
384	if ((bp = qp->b_actf) != 0) {
385		/* block found to process, dequeue */
386		/*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
387		qp->b_actf = bp->b_actf;
388		splx(s);
389	} else {
390		/* nothing to do */
391		splx(s);
392		return;
393	}
394
395	/* changed media? */
396	if (!(cd->flags	& MCDVALID)) {
397		MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
398		return;
399	}
400
401	p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
402
403	cd->flags |= MCDMBXBSY;
404	cd->mbx.unit = unit;
405	cd->mbx.port = cd->iobase;
406	cd->mbx.retry = MCD_RETRYS;
407	cd->mbx.bp = bp;
408	cd->mbx.p_offset = p->p_offset;
409
410	/* calling the read routine */
411	mcd_doread(MCD_S_BEGIN,&(cd->mbx));
412	/* triggers mcd_start, when successful finished */
413	return;
414}
415
416int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
417{
418	struct mcd_data *cd;
419	int unit,part;
420
421	unit = mcd_unit(dev);
422	part = mcd_part(dev);
423	cd = mcd_data + unit;
424
425#ifdef MCDMINI
426	return ENOTTY;
427#else
428	if (!(cd->flags & MCDVALID))
429		return EIO;
430MCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
431
432	switch (cmd) {
433	case DIOCSBAD:
434		return EINVAL;
435	case DIOCGDINFO:
436	case DIOCGPART:
437	case DIOCWDINFO:
438	case DIOCSDINFO:
439	case DIOCWLABEL:
440		return ENOTTY;
441	case CDIOCPLAYTRACKS:
442		return mcd_playtracks(unit, (struct ioc_play_track *) addr);
443	case CDIOCPLAYBLOCKS:
444		return mcd_play(unit, (struct mcd_read2 *) addr);
445	case CDIOCREADSUBCHANNEL:
446		return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
447	case CDIOREADTOCHEADER:
448		return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
449	case CDIOREADTOCENTRYS:
450		return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr);
451	case CDIOCSETPATCH:
452	case CDIOCGETVOL:
453	case CDIOCSETVOL:
454	case CDIOCSETMONO:
455	case CDIOCSETSTERIO:
456	case CDIOCSETMUTE:
457	case CDIOCSETLEFT:
458	case CDIOCSETRIGHT:
459		return EINVAL;
460	case CDIOCRESUME:
461		return mcd_resume(unit);
462	case CDIOCPAUSE:
463		return mcd_pause(unit);
464	case CDIOCSTART:
465		return EINVAL;
466	case CDIOCSTOP:
467		return mcd_stop(unit);
468	case CDIOCEJECT:
469		return EINVAL;
470	case CDIOCSETDEBUG:
471		cd->debug = 1;
472		return 0;
473	case CDIOCCLRDEBUG:
474		cd->debug = 0;
475		return 0;
476	case CDIOCRESET:
477		return EINVAL;
478	default:
479		return ENOTTY;
480	}
481	/*NOTREACHED*/
482#endif /*!MCDMINI*/
483}
484
485/* this could have been taken from scsi/cd.c, but it is not clear
486 * whether the scsi cd driver is linked in
487 */
488static int mcd_getdisklabel(int unit)
489{
490	struct mcd_data *cd = mcd_data + unit;
491
492	if (cd->flags & MCDLABEL)
493		return -1;
494
495	bzero(&cd->dlabel,sizeof(struct disklabel));
496	strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16);
497	strncpy(cd->dlabel.d_packname,"unknown        ",16);
498	cd->dlabel.d_secsize 	= cd->blksize;
499	cd->dlabel.d_nsectors	= 100;
500	cd->dlabel.d_ntracks	= 1;
501	cd->dlabel.d_ncylinders	= (cd->disksize/100)+1;
502	cd->dlabel.d_secpercyl	= 100;
503	cd->dlabel.d_secperunit	= cd->disksize;
504	cd->dlabel.d_rpm	= 300;
505	cd->dlabel.d_interleave	= 1;
506	cd->dlabel.d_flags	= D_REMOVABLE;
507	cd->dlabel.d_npartitions= 1;
508	cd->dlabel.d_partitions[0].p_offset = 0;
509	cd->dlabel.d_partitions[0].p_size = cd->disksize;
510	cd->dlabel.d_partitions[0].p_fstype = 9;
511	cd->dlabel.d_magic	= DISKMAGIC;
512	cd->dlabel.d_magic2	= DISKMAGIC;
513	cd->dlabel.d_checksum	= dkcksum(&cd->dlabel);
514
515	cd->flags |= MCDLABEL;
516	return 0;
517}
518
519int mcdsize(dev_t dev)
520{
521	int size;
522	int unit = mcd_unit(dev);
523	struct mcd_data *cd = mcd_data + unit;
524
525	if (mcd_volinfo(unit) >= 0) {
526		cd->blksize = MCDBLK;
527		size = msf2hsg(cd->volinfo.vol_msf);
528		cd->disksize = size * (MCDBLK/DEV_BSIZE);
529		return 0;
530	}
531	return -1;
532}
533
534/***************************************************************
535 * lower level of driver starts here
536 **************************************************************/
537
538#ifdef NOTDEF
539static char
540irqs[] = {
541	0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
542	0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
543};
544
545static char
546drqs[] = {
547	0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
548};
549#endif
550
551static void
552mcd_configure(struct mcd_data *cd)
553{
554	outb(cd->iobase+mcd_config,cd->config);
555}
556
557/* Wait for non-busy - return 0 on timeout */
558static int
559twiddle_thumbs(int port, int unit, int count, char *whine)
560{
561	int i;
562
563	for (i = 0; i < count; i++) {
564		if (!(inb(port+MCD_FLAGS) & MCD_ST_BUSY)) {
565			return 1;
566		}
567	}
568#ifdef MCD_TO_WARNING_ON
569	printf("mcd%d: timeout %s\n", unit, whine);
570#endif
571	return 0;
572}
573
574/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
575
576int
577mcd_probe(struct isa_device *dev)
578{
579	int port = dev->id_iobase;
580	int unit = dev->id_unit;
581	int i, j;
582	int status;
583	unsigned char stbytes[3];
584
585	mcd_data[unit].flags = MCDPROBING;
586
587#ifdef NOTDEF
588	/* get irq/drq configuration word */
589	mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
590#else
591	mcd_data[unit].config = 0;
592#endif
593
594	/* send a reset */
595	outb(port+MCD_FLAGS, M_RESET);
596
597	/*
598	 * delay awhile by getting any pending garbage (old data) and
599	 * throwing it away.
600	 */
601	for (i = 1000000; i != 0; i--) {
602		inb(port+MCD_FLAGS);
603	}
604
605	/* Get status */
606	outb(port+MCD_DATA, MCD_CMDGETSTAT);
607	if (!twiddle_thumbs(port, unit, 1000000, "getting status")) {
608		return 0;	/* Timeout */
609	}
610	status = inb(port+MCD_DATA);
611	if (status != MCDCDABSENT && status != MCDCDPRESENT &&
612		status != MCDSOPEN && status != MCDSCLOSED)
613		return 0;	/* Not actually a Mitsumi drive here */
614	/* Get version information */
615	outb(port+MCD_DATA, MCD_CMDCONTINFO);
616	for (j = 0; j < 3; j++) {
617		if (!twiddle_thumbs(port, unit, 3000, "getting version info")) {
618			return 0;
619		}
620		stbytes[j] = (inb(port+MCD_DATA) & 0xFF);
621	}
622	printf("mcd%d: version information is %x %c %x\n", unit,
623		stbytes[0], stbytes[1], stbytes[2]);
624	if (stbytes[1] >= 4) {
625		outb(port+MCD_CTRL, M_PICKLE);
626		printf("mcd%d: Adjusted for newer drive model\n", unit);
627	}
628	return 4;
629}
630
631
632static int
633mcd_waitrdy(int port,int dly)
634{
635	int i;
636
637	/* wait until xfer port senses data ready */
638	for (i=0; i<dly; i++) {
639		if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0) {
640			DELAY(10);
641			return 0;
642		}
643		DELAY(1);
644	}
645	return -1;
646}
647
648static int
649mcd_getreply(int unit,int dly)
650{
651	int	i;
652	struct	mcd_data *cd = mcd_data + unit;
653	int	port = cd->iobase;
654
655	/* wait data to become ready */
656	if (mcd_waitrdy(port,dly)<0) {
657#ifdef MCD_TO_WARNING_ON
658		printf("mcd%d: timeout getreply\n",unit);
659#endif
660		return -1;
661	}
662
663	/* get the data */
664	return inb(port+mcd_status) & 0xFF;
665}
666
667static int
668mcd_getstat(int unit,int sflg)
669{
670	int	i;
671	struct	mcd_data *cd = mcd_data + unit;
672	int	port = cd->iobase;
673
674	/* get the status */
675	if (sflg)
676		outb(port+mcd_command, MCD_CMDGETSTAT);
677	i = mcd_getreply(unit,DELAY_GETREPLY);
678	if (i<0) return -1;
679
680	cd->status = i;
681
682	mcd_setflags(unit,cd);
683	return cd->status;
684}
685
686static void
687mcd_setflags(int unit, struct mcd_data *cd)
688{
689	/* check flags */
690	if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
691		MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
692		cd->flags &= ~MCDVALID;
693	}
694
695#ifndef MCDMINI
696	if (cd->status & MCDAUDIOBSY)
697		cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
698	else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
699		cd->audio_status = CD_AS_PLAY_COMPLETED;
700#endif
701}
702
703static int
704mcd_get(int unit, char *buf, int nmax)
705{
706	int port = mcd_data[unit].iobase;
707	int i,k;
708
709	for (i=0; i<nmax; i++) {
710		/* wait for data */
711		if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
712#ifdef MCD_TO_WARNING_ON
713			printf("mcd%d: timeout mcd_get\n",unit);
714#endif
715			return -1;
716		}
717		buf[i] = k;
718	}
719	return i;
720}
721
722static int
723mcd_send(int unit, int cmd,int nretrys)
724{
725	int i,k;
726	int port = mcd_data[unit].iobase;
727
728/*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
729	for (i=0; i<nretrys; i++) {
730		outb(port+mcd_command, cmd);
731		if ((k=mcd_getstat(unit,0)) != -1) {
732			break;
733		}
734	}
735	if (i == nretrys) {
736		printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
737		return -1;
738	}
739/*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
740	return 0;
741}
742
743static int
744bcd2bin(bcd_t b)
745{
746	return (b >> 4) * 10 + (b & 15);
747}
748
749static bcd_t
750bin2bcd(int b)
751{
752	return ((b / 10) << 4) | (b % 10);
753}
754
755static void
756hsg2msf(int hsg, bcd_t *msf)
757{
758	hsg += 150;
759	M_msf(msf) = bin2bcd(hsg / 4500);
760	hsg %= 4500;
761	S_msf(msf) = bin2bcd(hsg / 75);
762	F_msf(msf) = bin2bcd(hsg % 75);
763}
764
765static int
766msf2hsg(bcd_t *msf)
767{
768	return (bcd2bin(M_msf(msf)) * 60 +
769		bcd2bin(S_msf(msf))) * 75 +
770		bcd2bin(F_msf(msf)) - 150;
771}
772
773static int
774mcd_volinfo(int unit)
775{
776	struct mcd_data *cd = mcd_data + unit;
777	int i;
778
779/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
780
781	/* Get the status, in case the disc has been changed */
782	if (mcd_getstat(unit, 1) < 0) return EIO;
783
784	/* Just return if we already have it */
785	if (cd->flags & MCDVOLINFO) return 0;
786
787	/* send volume info command */
788	if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
789		return -1;
790
791	/* get data */
792	if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
793		printf("mcd%d: mcd_volinfo: error read data\n",unit);
794		return -1;
795	}
796
797	if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
798		cd->flags |= MCDVOLINFO;	/* volinfo is OK */
799		return 0;
800	}
801
802	return -1;
803}
804
805void
806mcdintr(unit)
807	int unit;
808{
809	int	port = mcd_data[unit].iobase;
810	u_int	i;
811
812	MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0);
813
814	/* just read out status and ignore the rest */
815	if ((inb(port+mcd_xfer)&0xFF) != 0xFF) {
816		i = inb(port+mcd_status);
817	}
818}
819
820/* state machine to process read requests
821 * initialize with MCD_S_BEGIN: calculate sizes, and read status
822 * MCD_S_WAITSTAT: wait for status reply, set mode
823 * MCD_S_WAITMODE: waits for status reply from set mode, set read command
824 * MCD_S_WAITREAD: wait for read ready, read data
825 */
826static struct mcd_mbx *mbxsave;
827
828static void
829mcd_doread(int state, struct mcd_mbx *mbxin)
830{
831	struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
832	int	unit = mbx->unit;
833	int	port = mbx->port;
834	struct	buf *bp = mbx->bp;
835	struct	mcd_data *cd = mcd_data + unit;
836
837	int	rm,i,k;
838	struct mcd_read2 rbuf;
839	int	blknum;
840	caddr_t	addr;
841
842loop:
843	switch (state) {
844	case MCD_S_BEGIN:
845		mbx = mbxsave = mbxin;
846
847	case MCD_S_BEGIN1:
848		/* get status */
849		outb(port+mcd_command, MCD_CMDGETSTAT);
850		mbx->count = RDELAY_WAITSTAT;
851		timeout((timeout_func_t)mcd_doread,
852			(caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
853		return;
854	case MCD_S_WAITSTAT:
855		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT);
856		if (mbx->count-- >= 0) {
857			if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
858				timeout((timeout_func_t)mcd_doread,
859				    (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
860				return;
861			}
862			mcd_setflags(unit,cd);
863			MCD_TRACE("got WAITSTAT delay=%d\n",
864				RDELAY_WAITSTAT-mbx->count,0,0,0);
865			/* reject, if audio active */
866			if (cd->status & MCDAUDIOBSY) {
867				printf("mcd%d: audio is active\n",unit);
868				goto readerr;
869			}
870
871			/* to check for raw/cooked mode */
872			if (cd->flags & MCDREADRAW) {
873				rm = MCD_MD_RAW;
874				mbx->sz = MCDRBLK;
875			} else {
876				rm = MCD_MD_COOKED;
877				mbx->sz = cd->blksize;
878			}
879
880			mbx->count = RDELAY_WAITMODE;
881
882			mcd_put(port+mcd_command, MCD_CMDSETMODE);
883			mcd_put(port+mcd_command, rm);
884			timeout((timeout_func_t)mcd_doread,
885				(caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */
886			return;
887		} else {
888#ifdef MCD_TO_WARNING_ON
889			printf("mcd%d: timeout getstatus\n",unit);
890#endif
891			goto readerr;
892		}
893
894	case MCD_S_WAITMODE:
895		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE);
896		if (mbx->count-- < 0) {
897#ifdef MCD_TO_WARNING_ON
898			printf("mcd%d: timeout set mode\n",unit);
899#endif
900			goto readerr;
901		}
902		if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
903			timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100);
904			return;
905		}
906		mcd_setflags(unit,cd);
907		MCD_TRACE("got WAITMODE delay=%d\n",
908			RDELAY_WAITMODE-mbx->count,0,0,0);
909		/* for first block */
910		mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
911		mbx->skip = 0;
912
913nextblock:
914		blknum 	= (bp->b_blkno / (mbx->sz/DEV_BSIZE))
915			+ mbx->p_offset + mbx->skip/mbx->sz;
916
917		MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",
918			blknum,bp,0,0);
919
920		/* build parameter block */
921		hsg2msf(blknum,rbuf.start_msf);
922
923		/* send the read command */
924		mcd_put(port+mcd_command,MCD_CMDREAD2);
925		mcd_put(port+mcd_command,rbuf.start_msf[0]);
926		mcd_put(port+mcd_command,rbuf.start_msf[1]);
927		mcd_put(port+mcd_command,rbuf.start_msf[2]);
928		mcd_put(port+mcd_command,0);
929		mcd_put(port+mcd_command,0);
930		mcd_put(port+mcd_command,1);
931		mbx->count = RDELAY_WAITREAD;
932		timeout((timeout_func_t)mcd_doread,
933			(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
934		return;
935	case MCD_S_WAITREAD:
936		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD);
937		if (mbx->count-- > 0) {
938			k = inb(port+mcd_xfer);
939			if ((k & 2)==0) {
940				MCD_TRACE("got data delay=%d\n",
941					RDELAY_WAITREAD-mbx->count,0,0,0);
942				/* data is ready */
943				addr	= bp->b_un.b_addr + mbx->skip;
944				outb(port+mcd_ctl2,0x04);	/* XXX */
945				for (i=0; i<mbx->sz; i++)
946					*addr++	= inb(port+mcd_rdata);
947				outb(port+mcd_ctl2,0x0c);	/* XXX */
948
949				if (--mbx->nblk > 0) {
950					mbx->skip += mbx->sz;
951					goto nextblock;
952				}
953
954				/* return buffer */
955				bp->b_resid = 0;
956				biodone(bp);
957
958				cd->flags &= ~MCDMBXBSY;
959				mcd_start(mbx->unit);
960				return;
961			}
962			if ((k & 4)==0)
963				mcd_getstat(unit,0);
964			timeout((timeout_func_t)mcd_doread,
965				(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
966			return;
967		} else {
968#ifdef MCD_TO_WARNING_ON
969			printf("mcd%d: timeout read data\n",unit);
970#endif
971			goto readerr;
972		}
973	}
974
975readerr:
976	if (mbx->retry-- > 0) {
977#ifdef MCD_TO_WARNING_ON
978		printf("mcd%d: retrying\n",unit);
979#endif
980		state = MCD_S_BEGIN1;
981		goto loop;
982	}
983
984	/* invalidate the buffer */
985	bp->b_flags |= B_ERROR;
986	bp->b_resid = bp->b_bcount;
987	biodone(bp);
988	mcd_start(mbx->unit);
989	return;
990
991#ifdef NOTDEF
992	printf("mcd%d: unit timeout, resetting\n",mbx->unit);
993	outb(mbx->port+mcd_reset,MCD_CMDRESET);
994	DELAY(300000);
995	(void)mcd_getstat(mbx->unit,1);
996	(void)mcd_getstat(mbx->unit,1);
997	/*cd->status &= ~MCDDSKCHNG; */
998	cd->debug = 1; /* preventive set debug mode */
999
1000#endif
1001
1002}
1003
1004#ifndef MCDMINI
1005static int
1006mcd_setmode(int unit, int mode)
1007{
1008	struct mcd_data *cd = mcd_data + unit;
1009	int port = cd->iobase;
1010	int retry;
1011
1012#ifdef DEBUG
1013	printf("mcd%d: setting mode to %d\n", unit, mode);
1014#endif
1015	for(retry=0; retry<MCD_RETRYS; retry++)
1016	{
1017		outb(port+mcd_command, MCD_CMDSETMODE);
1018		outb(port+mcd_command, mode);
1019		if (mcd_getstat(unit, 0) != -1) return 0;
1020	}
1021
1022	return -1;
1023}
1024
1025static int
1026mcd_toc_header(int unit, struct ioc_toc_header *th)
1027{
1028	struct mcd_data *cd = mcd_data + unit;
1029
1030	if (mcd_volinfo(unit) < 0) {
1031		return ENXIO;
1032	}
1033
1034	th->len = msf2hsg(cd->volinfo.vol_msf);
1035	th->starting_track = bcd2bin(cd->volinfo.trk_low);
1036	th->ending_track = bcd2bin(cd->volinfo.trk_high);
1037
1038	return 0;
1039}
1040
1041static int
1042mcd_read_toc(int unit)
1043{
1044	struct mcd_data *cd = mcd_data + unit;
1045	struct ioc_toc_header th;
1046	struct mcd_qchninfo q;
1047	int rc, trk, idx, retry;
1048
1049	/* Only read TOC if needed */
1050	if (cd->flags & MCDTOC) {
1051		return 0;
1052	}
1053
1054#ifdef DEBUG
1055	printf("mcd%d: reading toc header\n", unit);
1056#endif
1057	if (mcd_toc_header(unit, &th) != 0) {
1058		return ENXIO;
1059	}
1060
1061#ifdef DEBUG
1062	printf("mcd%d: stopping play\n", unit);
1063#endif
1064	if ((rc=mcd_stop(unit)) != 0) {
1065		return rc;
1066	}
1067
1068	/* try setting the mode twice */
1069	if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
1070		return EIO;
1071	}
1072	if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
1073		return EIO;
1074	}
1075
1076#ifdef DEBUG
1077	printf("mcd%d: get_toc reading qchannel info\n",unit);
1078#endif
1079	for(trk=th.starting_track; trk<=th.ending_track; trk++)
1080		cd->toc[trk].idx_no = 0;
1081	trk = th.ending_track - th.starting_track + 1;
1082	for(retry=0; retry<300 && trk>0; retry++)
1083	{
1084		if (mcd_getqchan(unit, &q) < 0) break;
1085		idx = bcd2bin(q.idx_no);
1086		if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0) {
1087			if (cd->toc[idx].idx_no == 0) {
1088				cd->toc[idx] = q;
1089				trk--;
1090			}
1091		}
1092	}
1093
1094	if (mcd_setmode(unit, MCD_MD_COOKED) != 0) {
1095		return EIO;
1096	}
1097
1098	if (trk != 0) {
1099		return ENXIO;
1100	}
1101
1102	/* add a fake last+1 */
1103	idx = th.ending_track + 1;
1104	cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
1105	cd->toc[idx].trk_no = 0;
1106	cd->toc[idx].idx_no = 0xAA;
1107	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
1108	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
1109	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
1110
1111	cd->flags |= MCDTOC;
1112
1113	return 0;
1114}
1115
1116static int
1117mcd_toc_entry(int unit, struct ioc_read_toc_entry *te)
1118{
1119	struct mcd_data *cd = mcd_data + unit;
1120	struct ret_toc {
1121		struct ioc_toc_header th;
1122		struct cd_toc_entry rt;
1123	} ret_toc;
1124	struct ioc_toc_header th;
1125	int rc, i;
1126
1127	/* Make sure we have a valid toc */
1128	if ((rc=mcd_read_toc(unit)) != 0) {
1129		return rc;
1130	}
1131
1132	/* find the toc to copy*/
1133	i = te->starting_track;
1134	if (i == MCD_LASTPLUS1) {
1135		i = bcd2bin(cd->volinfo.trk_high) + 1;
1136	}
1137
1138	/* verify starting track */
1139	if (i < bcd2bin(cd->volinfo.trk_low) ||
1140		i > bcd2bin(cd->volinfo.trk_high)+1) {
1141		return EINVAL;
1142	}
1143
1144	/* do we have room */
1145	if (te->data_len < sizeof(struct ioc_toc_header) +
1146		sizeof(struct cd_toc_entry)) {
1147		return EINVAL;
1148	}
1149
1150	/* Copy the toc header */
1151	if (mcd_toc_header(unit, &th) < 0) {
1152		return EIO;
1153	}
1154	ret_toc.th = th;
1155
1156	/* copy the toc data */
1157	ret_toc.rt.control = cd->toc[i].ctrl_adr;
1158	ret_toc.rt.addr_type = te->address_format;
1159	ret_toc.rt.track = i;
1160	if (te->address_format == CD_MSF_FORMAT) {
1161		ret_toc.rt.addr.addr[1] = cd->toc[i].hd_pos_msf[0];
1162		ret_toc.rt.addr.addr[2] = cd->toc[i].hd_pos_msf[1];
1163		ret_toc.rt.addr.addr[3] = cd->toc[i].hd_pos_msf[2];
1164	}
1165
1166	/* copy the data back */
1167	copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
1168		+ sizeof(struct ioc_toc_header));
1169
1170	return 0;
1171}
1172
1173static int
1174mcd_stop(int unit)
1175{
1176	struct mcd_data *cd = mcd_data + unit;
1177
1178	if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) {
1179		return ENXIO;
1180	}
1181	cd->audio_status = CD_AS_PLAY_COMPLETED;
1182	return 0;
1183}
1184
1185static int
1186mcd_getqchan(int unit, struct mcd_qchninfo *q)
1187{
1188	struct mcd_data *cd = mcd_data + unit;
1189
1190	if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) {
1191		return -1;
1192	}
1193	if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) {
1194		return -1;
1195	}
1196#ifdef DEBUG
1197	if (cd->debug) {
1198		printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
1199		unit,
1200		q->ctrl_adr, q->trk_no, q->idx_no,
1201		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
1202		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
1203	}
1204#endif
1205	return 0;
1206}
1207
1208static int
1209mcd_subchan(int unit, struct ioc_read_subchannel *sc)
1210{
1211	struct mcd_data *cd = mcd_data + unit;
1212	struct mcd_qchninfo q;
1213	struct cd_sub_channel_info data;
1214
1215#ifdef DEBUG
1216	printf("mcd%d: subchan af=%d, df=%d\n", unit,
1217		sc->address_format,
1218		sc->data_format);
1219#endif
1220	if (sc->address_format != CD_MSF_FORMAT) {
1221		return EIO;
1222	}
1223	if (sc->data_format != CD_CURRENT_POSITION) {
1224		return EIO;
1225	}
1226	if (mcd_getqchan(unit, &q) < 0) {
1227		return EIO;
1228	}
1229
1230	data.header.audio_status = cd->audio_status;
1231	data.what.position.data_format = CD_MSF_FORMAT;
1232	data.what.position.track_number = bcd2bin(q.trk_no);
1233
1234	if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0) {
1235		return EFAULT;
1236	}
1237	return 0;
1238}
1239
1240static int
1241mcd_playtracks(int unit, struct ioc_play_track *pt)
1242{
1243	struct mcd_data *cd = mcd_data + unit;
1244	struct mcd_read2 pb;
1245	int a = pt->start_track;
1246	int z = pt->end_track;
1247	int rc;
1248
1249	if ((rc = mcd_read_toc(unit)) != 0) {
1250		return rc;
1251	}
1252#ifdef DEBUG
1253	printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
1254		a, pt->start_index, z, pt->end_index);
1255#endif
1256
1257	if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
1258	    z < cd->volinfo.trk_low || z > cd->volinfo.trk_high) {
1259		return EINVAL;
1260	}
1261
1262	pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
1263	pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
1264	pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
1265	pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
1266	pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
1267	pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
1268
1269	return mcd_play(unit, &pb);
1270}
1271
1272static int
1273mcd_play(int unit, struct mcd_read2 *pb)
1274{
1275	struct mcd_data *cd = mcd_data + unit;
1276	int port = cd->iobase;
1277	int retry, st;
1278
1279	cd->lastpb = *pb;
1280	for(retry=0; retry<MCD_RETRYS; retry++) {
1281		outb(port+mcd_command, MCD_CMDREAD2);
1282		outb(port+mcd_command, pb->start_msf[0]);
1283		outb(port+mcd_command, pb->start_msf[1]);
1284		outb(port+mcd_command, pb->start_msf[2]);
1285		outb(port+mcd_command, pb->end_msf[0]);
1286		outb(port+mcd_command, pb->end_msf[1]);
1287		outb(port+mcd_command, pb->end_msf[2]);
1288		if ((st=mcd_getstat(unit, 0)) != -1) {
1289			break;
1290		}
1291	}
1292
1293#ifdef DEBUG
1294	if (cd->debug) {
1295		printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st);
1296	}
1297#endif
1298	if (st == -1) {
1299		return ENXIO;
1300	}
1301	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
1302	return 0;
1303}
1304
1305static int
1306mcd_pause(int unit)
1307{
1308	struct mcd_data *cd = mcd_data + unit;
1309	struct mcd_qchninfo q;
1310	int rc;
1311
1312	/* Verify current status */
1313	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) {
1314#ifdef DEBUG
1315		printf("mcd%d: pause attempted when not playing\n", unit);
1316#endif
1317		return EINVAL;
1318	}
1319
1320	/* Get the current position */
1321	if (mcd_getqchan(unit, &q) < 0) {
1322		return EIO;
1323	}
1324
1325	/* Copy it into lastpb */
1326	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
1327	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
1328	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
1329
1330	/* Stop playing */
1331	if ((rc=mcd_stop(unit)) != 0) {
1332		return rc;
1333	}
1334
1335	/* Set the proper status and exit */
1336	cd->audio_status = CD_AS_PLAY_PAUSED;
1337	return 0;
1338}
1339
1340static int
1341mcd_resume(int unit)
1342{
1343	struct mcd_data *cd = mcd_data + unit;
1344
1345	if (cd->audio_status != CD_AS_PLAY_PAUSED) {
1346		return EINVAL;
1347	}
1348	return mcd_play(unit, &cd->lastpb);
1349}
1350#endif /*!MCDMINI*/
1351
1352#endif /* NMCD > 0 */
1353