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