mcd.c revision 1097
1578Srgrimes/*
2578Srgrimes * Copyright 1993 by Holger Veit (data part)
3578Srgrimes * Copyright 1993 by Brian Moore (audio part)
4978Sjkh * Changes Copyright 1993 by Gary Clark II
5578Srgrimes * All rights reserved.
6578Srgrimes *
7578Srgrimes * Redistribution and use in source and binary forms, with or without
8578Srgrimes * modification, are permitted provided that the following conditions
9578Srgrimes * are met:
10578Srgrimes * 1. Redistributions of source code must retain the above copyright
11578Srgrimes *    notice, this list of conditions and the following disclaimer.
12578Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
13578Srgrimes *    notice, this list of conditions and the following disclaimer in the
14578Srgrimes *    documentation and/or other materials provided with the distribution.
15578Srgrimes * 3. All advertising materials mentioning features or use of this software
16578Srgrimes *    must display the following acknowledgement:
17578Srgrimes *	This software was developed by Holger Veit and Brian Moore
18578Srgrimes *      for use with "386BSD" and similar operating systems.
19578Srgrimes *    "Similar operating systems" includes mainly non-profit oriented
20578Srgrimes *    systems for research and education, including but not restricted to
21578Srgrimes *    "NetBSD", "FreeBSD", "Mach" (by CMU).
22578Srgrimes * 4. Neither the name of the developer(s) nor the name "386BSD"
23578Srgrimes *    may be used to endorse or promote products derived from this
24578Srgrimes *    software without specific prior written permission.
25578Srgrimes *
26578Srgrimes * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
27578Srgrimes * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28578Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29578Srgrimes * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER(S) BE
30578Srgrimes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
31578Srgrimes * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
32578Srgrimes * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
33578Srgrimes * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34578Srgrimes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35578Srgrimes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36578Srgrimes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37578Srgrimes *
381097Srgrimes *	$Id: mcd.c,v 1.7 1994/01/22 18:00:54 ats Exp $
39578Srgrimes */
40578Srgrimesstatic char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
41578Srgrimes
42578Srgrimes#include "mcd.h"
43578Srgrimes#if NMCD > 0
44578Srgrimes#include "types.h"
45578Srgrimes#include "param.h"
46578Srgrimes#include "systm.h"
47578Srgrimes#include "conf.h"
48578Srgrimes#include "file.h"
49578Srgrimes#include "buf.h"
50578Srgrimes#include "stat.h"
51578Srgrimes#include "uio.h"
52578Srgrimes#include "ioctl.h"
53578Srgrimes#include "cdio.h"
54578Srgrimes#include "errno.h"
55578Srgrimes#include "dkbad.h"
56578Srgrimes#include "disklabel.h"
57578Srgrimes#include "i386/isa/isa.h"
58578Srgrimes#include "i386/isa/isa_device.h"
59578Srgrimes#include "mcdreg.h"
60578Srgrimes
61578Srgrimes/* user definable options */
62578Srgrimes/*#define MCD_TO_WARNING_ON*/	/* define to get timeout messages */
63578Srgrimes/*#define MCDMINI*/ 		/* define for a mini configuration for boot kernel */
64578Srgrimes
65578Srgrimes
66578Srgrimes#ifdef MCDMINI
67578Srgrimes#define MCD_TRACE(fmt,a,b,c,d)
68578Srgrimes#ifdef MCD_TO_WARNING_ON
69578Srgrimes#undef MCD_TO_WARNING_ON
70578Srgrimes#endif
71578Srgrimes#else
72978Sjkh#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);}}
73578Srgrimes#endif
74578Srgrimes
75578Srgrimes#define mcd_part(dev)	((minor(dev)) & 7)
76578Srgrimes#define mcd_unit(dev)	(((minor(dev)) & 0x38) >> 3)
77578Srgrimes#define mcd_phys(dev)	(((minor(dev)) & 0x40) >> 6)
78578Srgrimes#define RAW_PART	3
79578Srgrimes
80578Srgrimes/* flags */
81578Srgrimes#define MCDOPEN		0x0001	/* device opened */
82578Srgrimes#define MCDVALID	0x0002	/* parameters loaded */
83578Srgrimes#define MCDINIT		0x0004	/* device is init'd */
84578Srgrimes#define MCDWAIT		0x0008	/* waiting for something */
85578Srgrimes#define MCDLABEL	0x0010	/* label is read */
86578Srgrimes#define	MCDPROBING	0x0020	/* probing */
87578Srgrimes#define	MCDREADRAW	0x0040	/* read raw mode (2352 bytes) */
88578Srgrimes#define	MCDVOLINFO	0x0080	/* already read volinfo */
89578Srgrimes#define	MCDTOC		0x0100	/* already read toc */
90578Srgrimes#define	MCDMBXBSY	0x0200	/* local mbx is busy */
91578Srgrimes
92578Srgrimes/* status */
93578Srgrimes#define	MCDAUDIOBSY	MCD_ST_AUDIOBSY		/* playing audio */
94578Srgrimes#define MCDDSKCHNG	MCD_ST_DSKCHNG		/* sensed change of disk */
95578Srgrimes#define MCDDSKIN	MCD_ST_DSKIN		/* sensed disk in drive */
96578Srgrimes#define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
97578Srgrimes
98578Srgrimes/* toc */
99578Srgrimes#define MCD_MAXTOCS	104	/* from the Linux driver */
100578Srgrimes#define MCD_LASTPLUS1	170	/* special toc entry */
101578Srgrimes
102578Srgrimesstruct mcd_mbx {
103578Srgrimes	short		unit;
104578Srgrimes	short		port;
105578Srgrimes	short		retry;
106578Srgrimes	short		nblk;
107578Srgrimes	int		sz;
108578Srgrimes	u_long		skip;
109578Srgrimes	struct buf	*bp;
110578Srgrimes	int		p_offset;
111578Srgrimes	short		count;
112578Srgrimes};
113578Srgrimes
114578Srgrimesstruct mcd_data {
115578Srgrimes	short	config;
116578Srgrimes	short	flags;
117578Srgrimes	short	status;
118578Srgrimes	int	blksize;
119578Srgrimes	u_long	disksize;
120578Srgrimes	int	iobase;
121578Srgrimes	struct disklabel dlabel;
122578Srgrimes	int	partflags[MAXPARTITIONS];
123578Srgrimes	int	openflags;
124578Srgrimes	struct mcd_volinfo volinfo;
125578Srgrimes#ifndef MCDMINI
126578Srgrimes	struct mcd_qchninfo toc[MCD_MAXTOCS];
127578Srgrimes	short	audio_status;
128578Srgrimes	struct mcd_read2 lastpb;
129578Srgrimes#endif
130578Srgrimes	short	debug;
131578Srgrimes	struct buf head;		/* head of buf queue */
132578Srgrimes	struct mcd_mbx mbx;
133578Srgrimes} mcd_data[NMCD];
134578Srgrimes
135578Srgrimes/* reader state machine */
136578Srgrimes#define MCD_S_BEGIN	0
137578Srgrimes#define MCD_S_BEGIN1	1
138578Srgrimes#define MCD_S_WAITSTAT	2
139578Srgrimes#define MCD_S_WAITMODE	3
140578Srgrimes#define MCD_S_WAITREAD	4
141578Srgrimes
142578Srgrimes/* prototypes */
143578Srgrimesint	mcdopen(dev_t dev);
144578Srgrimesint	mcdclose(dev_t dev);
145798Swollmanvoid	mcdstrategy(struct buf *bp);
146578Srgrimesint	mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
147578Srgrimesint	mcdsize(dev_t dev);
148578Srgrimesstatic	void	mcd_done(struct mcd_mbx *mbx);
149578Srgrimesstatic	void	mcd_start(int unit);
150578Srgrimesstatic	int	mcd_getdisklabel(int unit);
151578Srgrimesstatic	void	mcd_configure(struct mcd_data *cd);
152578Srgrimesstatic	int	mcd_get(int unit, char *buf, int nmax);
153578Srgrimesstatic	void	mcd_setflags(int unit,struct mcd_data *cd);
154578Srgrimesstatic	int	mcd_getstat(int unit,int sflg);
155578Srgrimesstatic	int	mcd_send(int unit, int cmd,int nretrys);
156578Srgrimesstatic	int	bcd2bin(bcd_t b);
157578Srgrimesstatic	bcd_t	bin2bcd(int b);
158578Srgrimesstatic	void	hsg2msf(int hsg, bcd_t *msf);
159578Srgrimesstatic	int	msf2hsg(bcd_t *msf);
160578Srgrimesstatic	int	mcd_volinfo(int unit);
161578Srgrimesstatic	int	mcd_waitrdy(int port,int dly);
162978Sjkhstatic 	void	mcd_doread(int state, struct mcd_mbx *mbxin);
163578Srgrimes#ifndef MCDMINI
164578Srgrimesstatic	int 	mcd_setmode(int unit, int mode);
165578Srgrimesstatic	int	mcd_getqchan(int unit, struct mcd_qchninfo *q);
166578Srgrimesstatic	int	mcd_subchan(int unit, struct ioc_read_subchannel *sc);
167578Srgrimesstatic	int	mcd_toc_header(int unit, struct ioc_toc_header *th);
168578Srgrimesstatic	int	mcd_read_toc(int unit);
169578Srgrimesstatic	int	mcd_toc_entry(int unit, struct ioc_read_toc_entry *te);
170578Srgrimesstatic	int	mcd_stop(int unit);
171578Srgrimesstatic	int	mcd_playtracks(int unit, struct ioc_play_track *pt);
172578Srgrimesstatic	int	mcd_play(int unit, struct mcd_read2 *pb);
173578Srgrimesstatic	int	mcd_pause(int unit);
174578Srgrimesstatic	int	mcd_resume(int unit);
175578Srgrimes#endif
176578Srgrimes
177578Srgrimesextern	int	hz;
178578Srgrimesextern	int	mcd_probe(struct isa_device *dev);
179578Srgrimesextern	int	mcd_attach(struct isa_device *dev);
180578Srgrimesstruct	isa_driver	mcddriver = { mcd_probe, mcd_attach, "mcd" };
181578Srgrimes
182578Srgrimes#define mcd_put(port,byte)	outb(port,byte)
183578Srgrimes
184578Srgrimes#define MCD_RETRYS	5
185578Srgrimes#define MCD_RDRETRYS	8
186578Srgrimes
187578Srgrimes#define MCDBLK	2048	/* for cooked mode */
188578Srgrimes#define MCDRBLK	2352	/* for raw mode */
189578Srgrimes
190578Srgrimes/* several delays */
191578Srgrimes#define RDELAY_WAITSTAT	300
192578Srgrimes#define RDELAY_WAITMODE	300
193578Srgrimes#define RDELAY_WAITREAD	800
194578Srgrimes
195578Srgrimes#define DELAY_STATUS	10000l		/* 10000 * 1us */
196578Srgrimes#define DELAY_GETREPLY	200000l		/* 200000 * 2us */
197578Srgrimes#define DELAY_SEEKREAD	20000l		/* 20000 * 1us */
198578Srgrimes#define mcd_delay	DELAY
199578Srgrimes
200578Srgrimesint mcd_attach(struct isa_device *dev)
201578Srgrimes{
202578Srgrimes	struct mcd_data *cd = mcd_data + dev->id_unit;
203578Srgrimes	int i;
204578Srgrimes
205578Srgrimes	cd->iobase = dev->id_iobase;
206578Srgrimes	cd->flags |= MCDINIT;
207578Srgrimes	cd->openflags = 0;
208578Srgrimes	for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
209578Srgrimes
210578Srgrimes#ifdef NOTYET
211578Srgrimes	/* wire controller for interrupts and dma */
212578Srgrimes	mcd_configure(cd);
213578Srgrimes#endif
214578Srgrimes
215578Srgrimes	return 1;
216578Srgrimes}
217578Srgrimes
218578Srgrimesint mcdopen(dev_t dev)
219578Srgrimes{
220578Srgrimes	int unit,part,phys;
221578Srgrimes	struct mcd_data *cd;
222578Srgrimes
223578Srgrimes	unit = mcd_unit(dev);
224578Srgrimes	if (unit >= NMCD)
225578Srgrimes		return ENXIO;
226578Srgrimes
227578Srgrimes	cd = mcd_data + unit;
228578Srgrimes	part = mcd_part(dev);
229578Srgrimes	phys = mcd_phys(dev);
230578Srgrimes
231578Srgrimes	/* not initialized*/
232578Srgrimes	if (!(cd->flags & MCDINIT))
233578Srgrimes		return ENXIO;
234578Srgrimes
235578Srgrimes	/* invalidated in the meantime? mark all open part's invalid */
236578Srgrimes	if (!(cd->flags & MCDVALID) && cd->openflags)
237578Srgrimes		return ENXIO;
238578Srgrimes
239578Srgrimes	if (mcd_getstat(unit,1) < 0)
240578Srgrimes		return ENXIO;
241578Srgrimes
242578Srgrimes	/* XXX get a default disklabel */
243578Srgrimes	mcd_getdisklabel(unit);
244578Srgrimes
245578Srgrimes	if (mcdsize(dev) < 0) {
246578Srgrimes		printf("mcd%d: failed to get disk size\n",unit);
247578Srgrimes		return ENXIO;
248578Srgrimes	} else
249578Srgrimes		cd->flags |= MCDVALID;
250578Srgrimes
251578SrgrimesMCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
252578Srgrimes	part,cd->disksize,cd->blksize,0);
253578Srgrimes
254578Srgrimes	if (part == RAW_PART ||
255578Srgrimes		(part < cd->dlabel.d_npartitions &&
256578Srgrimes		cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
257578Srgrimes		cd->partflags[part] |= MCDOPEN;
258578Srgrimes		cd->openflags |= (1<<part);
259578Srgrimes		if (part == RAW_PART && phys != 0)
260578Srgrimes			cd->partflags[part] |= MCDREADRAW;
261578Srgrimes		return 0;
262578Srgrimes	}
263578Srgrimes
264578Srgrimes	return ENXIO;
265578Srgrimes}
266578Srgrimes
267578Srgrimesint mcdclose(dev_t dev)
268578Srgrimes{
269578Srgrimes	int unit,part,phys;
270578Srgrimes	struct mcd_data *cd;
271578Srgrimes
272578Srgrimes	unit = mcd_unit(dev);
273578Srgrimes	if (unit >= NMCD)
274578Srgrimes		return ENXIO;
275578Srgrimes
276578Srgrimes	cd = mcd_data + unit;
277578Srgrimes	part = mcd_part(dev);
278578Srgrimes	phys = mcd_phys(dev);
279578Srgrimes
280578Srgrimes	if (!(cd->flags & MCDINIT))
281578Srgrimes		return ENXIO;
282578Srgrimes
283578Srgrimes	mcd_getstat(unit,1);	/* get status */
284578Srgrimes
285578Srgrimes	/* close channel */
286578Srgrimes	cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
287578Srgrimes	cd->openflags &= ~(1<<part);
288578Srgrimes	MCD_TRACE("close: partition=%d\n",part,0,0,0);
289578Srgrimes
290578Srgrimes	return 0;
291578Srgrimes}
292578Srgrimes
293798Swollmanvoid
294798Swollmanmcdstrategy(struct buf *bp)
295578Srgrimes{
296578Srgrimes	struct mcd_data *cd;
297578Srgrimes	struct buf *qp;
298578Srgrimes	int s;
299578Srgrimes
300578Srgrimes	int unit = mcd_unit(bp->b_dev);
301578Srgrimes
302578Srgrimes	cd = mcd_data + unit;
303578Srgrimes
304578Srgrimes	/* test validity */
305578Srgrimes/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
306578Srgrimes	bp,unit,bp->b_blkno,bp->b_bcount);*/
307578Srgrimes	if (unit >= NMCD || bp->b_blkno < 0) {
308578Srgrimes		printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
309578Srgrimes			unit, bp->b_blkno, bp->b_bcount);
310578Srgrimes		pg("mcd: mcdstratregy failure");
311578Srgrimes		bp->b_error = EINVAL;
312578Srgrimes		bp->b_flags |= B_ERROR;
313578Srgrimes		goto bad;
314578Srgrimes	}
315578Srgrimes
316578Srgrimes	/* if device invalidated (e.g. media change, door open), error */
317578Srgrimes	if (!(cd->flags & MCDVALID)) {
318578SrgrimesMCD_TRACE("strategy: drive not valid\n",0,0,0,0);
319578Srgrimes		bp->b_error = EIO;
320578Srgrimes		goto bad;
321578Srgrimes	}
322578Srgrimes
323578Srgrimes	/* read only */
324578Srgrimes	if (!(bp->b_flags & B_READ)) {
325578Srgrimes		bp->b_error = EROFS;
326578Srgrimes		goto bad;
327578Srgrimes	}
328578Srgrimes
329578Srgrimes	/* no data to read */
330578Srgrimes	if (bp->b_bcount == 0)
331578Srgrimes		goto done;
332578Srgrimes
333578Srgrimes	/* for non raw access, check partition limits */
334578Srgrimes	if (mcd_part(bp->b_dev) != RAW_PART) {
335578Srgrimes		if (!(cd->flags & MCDLABEL)) {
336578Srgrimes			bp->b_error = EIO;
337578Srgrimes			goto bad;
338578Srgrimes		}
339578Srgrimes		/* adjust transfer if necessary */
340578Srgrimes		if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
341578Srgrimes			goto done;
342578Srgrimes		}
343578Srgrimes	}
344578Srgrimes
345578Srgrimes	/* queue it */
346578Srgrimes	qp = &cd->head;
347578Srgrimes	s = splbio();
348578Srgrimes	disksort(qp,bp);
349578Srgrimes	splx(s);
350578Srgrimes
351578Srgrimes	/* now check whether we can perform processing */
352578Srgrimes	mcd_start(unit);
353578Srgrimes	return;
354578Srgrimes
355578Srgrimesbad:
356578Srgrimes	bp->b_flags |= B_ERROR;
357578Srgrimesdone:
358578Srgrimes	bp->b_resid = bp->b_bcount;
359578Srgrimes	biodone(bp);
360578Srgrimes	return;
361578Srgrimes}
362578Srgrimes
363578Srgrimesstatic void mcd_start(int unit)
364578Srgrimes{
365578Srgrimes	struct mcd_data *cd = mcd_data + unit;
366578Srgrimes	struct buf *bp, *qp = &cd->head;
367578Srgrimes	struct partition *p;
368578Srgrimes	int part;
369578Srgrimes	register s = splbio();
370578Srgrimes
371578Srgrimes	if (cd->flags & MCDMBXBSY)
372578Srgrimes		return;
373578Srgrimes
374578Srgrimes	if ((bp = qp->b_actf) != 0) {
375578Srgrimes		/* block found to process, dequeue */
376578Srgrimes		/*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
377578Srgrimes		qp->b_actf = bp->av_forw;
378578Srgrimes		splx(s);
379578Srgrimes	} else {
380578Srgrimes		/* nothing to do */
381578Srgrimes		splx(s);
382578Srgrimes		return;
383578Srgrimes	}
384578Srgrimes
385578Srgrimes	/* changed media? */
386578Srgrimes	if (!(cd->flags	& MCDVALID)) {
387578Srgrimes		MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
388578Srgrimes		return;
389578Srgrimes	}
390578Srgrimes
391578Srgrimes	p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
392578Srgrimes
393578Srgrimes	cd->flags |= MCDMBXBSY;
394578Srgrimes	cd->mbx.unit = unit;
395578Srgrimes	cd->mbx.port = cd->iobase;
396578Srgrimes	cd->mbx.retry = MCD_RETRYS;
397578Srgrimes	cd->mbx.bp = bp;
398578Srgrimes	cd->mbx.p_offset = p->p_offset;
399578Srgrimes
400578Srgrimes	/* calling the read routine */
401978Sjkh	mcd_doread(MCD_S_BEGIN,&(cd->mbx));
402578Srgrimes	/* triggers mcd_start, when successful finished */
403578Srgrimes	return;
404578Srgrimes}
405578Srgrimes
406578Srgrimesint mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
407578Srgrimes{
408578Srgrimes	struct mcd_data *cd;
409578Srgrimes	int unit,part;
410578Srgrimes
411578Srgrimes	unit = mcd_unit(dev);
412578Srgrimes	part = mcd_part(dev);
413578Srgrimes	cd = mcd_data + unit;
414578Srgrimes
415578Srgrimes#ifdef MCDMINI
416578Srgrimes	return ENOTTY;
417578Srgrimes#else
418578Srgrimes	if (!(cd->flags & MCDVALID))
419578Srgrimes		return EIO;
420578SrgrimesMCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
421578Srgrimes
422578Srgrimes	switch (cmd) {
423578Srgrimes	case DIOCSBAD:
424578Srgrimes		return EINVAL;
425578Srgrimes	case DIOCGDINFO:
426578Srgrimes	case DIOCGPART:
427578Srgrimes	case DIOCWDINFO:
428578Srgrimes	case DIOCSDINFO:
429578Srgrimes	case DIOCWLABEL:
430578Srgrimes		return ENOTTY;
431578Srgrimes	case CDIOCPLAYTRACKS:
432578Srgrimes		return mcd_playtracks(unit, (struct ioc_play_track *) addr);
433578Srgrimes	case CDIOCPLAYBLOCKS:
434578Srgrimes		return mcd_play(unit, (struct mcd_read2 *) addr);
435578Srgrimes	case CDIOCREADSUBCHANNEL:
436578Srgrimes		return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
437578Srgrimes	case CDIOREADTOCHEADER:
438578Srgrimes		return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
439578Srgrimes	case CDIOREADTOCENTRYS:
440578Srgrimes		return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr);
441578Srgrimes	case CDIOCSETPATCH:
442578Srgrimes	case CDIOCGETVOL:
443578Srgrimes	case CDIOCSETVOL:
444578Srgrimes	case CDIOCSETMONO:
445578Srgrimes	case CDIOCSETSTERIO:
446578Srgrimes	case CDIOCSETMUTE:
447578Srgrimes	case CDIOCSETLEFT:
448578Srgrimes	case CDIOCSETRIGHT:
449578Srgrimes		return EINVAL;
450578Srgrimes	case CDIOCRESUME:
451578Srgrimes		return mcd_resume(unit);
452578Srgrimes	case CDIOCPAUSE:
453578Srgrimes		return mcd_pause(unit);
454578Srgrimes	case CDIOCSTART:
455578Srgrimes		return EINVAL;
456578Srgrimes	case CDIOCSTOP:
457578Srgrimes		return mcd_stop(unit);
458578Srgrimes	case CDIOCEJECT:
459578Srgrimes		return EINVAL;
460578Srgrimes	case CDIOCSETDEBUG:
461578Srgrimes		cd->debug = 1;
462578Srgrimes		return 0;
463578Srgrimes	case CDIOCCLRDEBUG:
464578Srgrimes		cd->debug = 0;
465578Srgrimes		return 0;
466578Srgrimes	case CDIOCRESET:
467578Srgrimes		return EINVAL;
468578Srgrimes	default:
469578Srgrimes		return ENOTTY;
470578Srgrimes	}
471578Srgrimes	/*NOTREACHED*/
472578Srgrimes#endif /*!MCDMINI*/
473578Srgrimes}
474578Srgrimes
475578Srgrimes/* this could have been taken from scsi/cd.c, but it is not clear
476578Srgrimes * whether the scsi cd driver is linked in
477578Srgrimes */
478578Srgrimesstatic int mcd_getdisklabel(int unit)
479578Srgrimes{
480578Srgrimes	struct mcd_data *cd = mcd_data + unit;
481578Srgrimes
482578Srgrimes	if (cd->flags & MCDLABEL)
483578Srgrimes		return -1;
484578Srgrimes
485578Srgrimes	bzero(&cd->dlabel,sizeof(struct disklabel));
486578Srgrimes	strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16);
487578Srgrimes	strncpy(cd->dlabel.d_packname,"unknown        ",16);
488578Srgrimes	cd->dlabel.d_secsize 	= cd->blksize;
489578Srgrimes	cd->dlabel.d_nsectors	= 100;
490578Srgrimes	cd->dlabel.d_ntracks	= 1;
491578Srgrimes	cd->dlabel.d_ncylinders	= (cd->disksize/100)+1;
492578Srgrimes	cd->dlabel.d_secpercyl	= 100;
493578Srgrimes	cd->dlabel.d_secperunit	= cd->disksize;
494578Srgrimes	cd->dlabel.d_rpm	= 300;
495578Srgrimes	cd->dlabel.d_interleave	= 1;
496578Srgrimes	cd->dlabel.d_flags	= D_REMOVABLE;
497578Srgrimes	cd->dlabel.d_npartitions= 1;
498578Srgrimes	cd->dlabel.d_partitions[0].p_offset = 0;
499578Srgrimes	cd->dlabel.d_partitions[0].p_size = cd->disksize;
500578Srgrimes	cd->dlabel.d_partitions[0].p_fstype = 9;
501578Srgrimes	cd->dlabel.d_magic	= DISKMAGIC;
502578Srgrimes	cd->dlabel.d_magic2	= DISKMAGIC;
503578Srgrimes	cd->dlabel.d_checksum	= dkcksum(&cd->dlabel);
504578Srgrimes
505578Srgrimes	cd->flags |= MCDLABEL;
506578Srgrimes	return 0;
507578Srgrimes}
508578Srgrimes
509578Srgrimesint mcdsize(dev_t dev)
510578Srgrimes{
511578Srgrimes	int size;
512578Srgrimes	int unit = mcd_unit(dev);
513578Srgrimes	struct mcd_data *cd = mcd_data + unit;
514578Srgrimes
515578Srgrimes	if (mcd_volinfo(unit) >= 0) {
516578Srgrimes		cd->blksize = MCDBLK;
517578Srgrimes		size = msf2hsg(cd->volinfo.vol_msf);
518578Srgrimes		cd->disksize = size * (MCDBLK/DEV_BSIZE);
519578Srgrimes		return 0;
520578Srgrimes	}
521578Srgrimes	return -1;
522578Srgrimes}
523578Srgrimes
524578Srgrimes/***************************************************************
525578Srgrimes * lower level of driver starts here
526578Srgrimes **************************************************************/
527578Srgrimes
528578Srgrimes#ifdef NOTDEF
529578Srgrimesstatic char irqs[] = {
530578Srgrimes	0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
531578Srgrimes	0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
532578Srgrimes};
533578Srgrimes
534578Srgrimesstatic char drqs[] = {
535578Srgrimes	0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
536578Srgrimes};
537578Srgrimes#endif
538578Srgrimes
539578Srgrimesstatic void mcd_configure(struct mcd_data *cd)
540578Srgrimes{
541578Srgrimes	outb(cd->iobase+mcd_config,cd->config);
542578Srgrimes}
543578Srgrimes
544978Sjkh/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
545578Srgrimes
546578Srgrimesint mcd_probe(struct isa_device *dev)
547578Srgrimes{
548578Srgrimes	int port = dev->id_iobase;
549578Srgrimes	int unit = dev->id_unit;
550978Sjkh	int i,j;
551578Srgrimes	int st;
552578Srgrimes	int check;
553578Srgrimes	int junk;
554578Srgrimes
555578Srgrimes	mcd_data[unit].flags = MCDPROBING;
556578Srgrimes
557578Srgrimes#ifdef NOTDEF
558578Srgrimes	/* get irq/drq configuration word */
559578Srgrimes	mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
560578Srgrimes#else
561578Srgrimes	mcd_data[unit].config = 0;
562578Srgrimes#endif
563578Srgrimes
564578Srgrimes	/* send a reset */
565978Sjkh	outb(port+MCD_FLAGS,M_RESET);
566982Snate	DELAY(3000);
567578Srgrimes
568978Sjkh        for (j=3; j != 0; j--)  {
569578Srgrimes
570978Sjkh	/* get any pending garbage (old data) and throw away...*/
571978Sjkh	for (i=10; i != 0; i--) {
572978Sjkh		inb(port+MCD_DATA);
573978Sjkh        }
574578Srgrimes
575578Srgrimes	DELAY (2000);
576578Srgrimes	outb(port+MCD_DATA,MCD_CMDCONTINFO);
577982Snate	for (i = 0; i < 30000; i++) {
578978Sjkh      	   if ((inb(port+MCD_FLAGS) & M_STATUS_AVAIL) == M_STATUS_AVAIL)
579978Sjkh            {
580982Snate      		DELAY (600);
581978Sjkh		st = inb(port+MCD_DATA);
582978Sjkh		DELAY (500);
583978Sjkh		check = inb(port+MCD_DATA);
584978Sjkh		DELAY (500);
585978Sjkh		junk = inb(port+MCD_DATA);	/* What is byte used for?!?!? */
586978Sjkh
587978Sjkh		if (check = 'M') {
588578Srgrimes#ifdef DEBUG
589578Srgrimes		printf("Mitsumi drive detected\n");
590578Srgrimes#endif
591578Srgrimes		return 4;
592978Sjkh		} else {
593578Srgrimes		printf("Mitsumi drive NOT detected\n");
594578Srgrimes		return 0;
595978Sjkh	       }
596978Sjkh           }
597978Sjkh       }
598978Sjkh   }
5991000Sats   return 0;
600578Srgrimes}
601578Srgrimes
602978Sjkh
603578Srgrimesstatic int mcd_waitrdy(int port,int dly)
604578Srgrimes{
605578Srgrimes	int i;
606578Srgrimes
607578Srgrimes	/* wait until xfer port senses data ready */
608578Srgrimes	for (i=0; i<dly; i++) {
609578Srgrimes		if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0)
610578Srgrimes			return 0;
611578Srgrimes		mcd_delay(1);
612578Srgrimes	}
613578Srgrimes	return -1;
614578Srgrimes}
615578Srgrimes
616578Srgrimesstatic int mcd_getreply(int unit,int dly)
617578Srgrimes{
618578Srgrimes	int	i;
619578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
620578Srgrimes	int	port = cd->iobase;
621578Srgrimes
622578Srgrimes	/* wait data to become ready */
623578Srgrimes	if (mcd_waitrdy(port,dly)<0) {
624578Srgrimes#ifdef MCD_TO_WARNING_ON
625578Srgrimes		printf("mcd%d: timeout getreply\n",unit);
626578Srgrimes#endif
627578Srgrimes		return -1;
628578Srgrimes	}
629578Srgrimes
630578Srgrimes	/* get the data */
631578Srgrimes	return inb(port+mcd_status) & 0xFF;
632578Srgrimes}
633578Srgrimes
634578Srgrimesstatic int mcd_getstat(int unit,int sflg)
635578Srgrimes{
636578Srgrimes	int	i;
637578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
638578Srgrimes	int	port = cd->iobase;
639578Srgrimes
640578Srgrimes	/* get the status */
641578Srgrimes	if (sflg)
642578Srgrimes		outb(port+mcd_command, MCD_CMDGETSTAT);
643578Srgrimes	i = mcd_getreply(unit,DELAY_GETREPLY);
644578Srgrimes	if (i<0) return -1;
645578Srgrimes
646578Srgrimes	cd->status = i;
647578Srgrimes
648578Srgrimes	mcd_setflags(unit,cd);
649578Srgrimes	return cd->status;
650578Srgrimes}
651578Srgrimes
652578Srgrimesstatic void mcd_setflags(int unit, struct mcd_data *cd)
653578Srgrimes{
654578Srgrimes	/* check flags */
655578Srgrimes	if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
656578Srgrimes		MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
657578Srgrimes		cd->flags &= ~MCDVALID;
658578Srgrimes	}
659578Srgrimes
660578Srgrimes#ifndef MCDMINI
661578Srgrimes	if (cd->status & MCDAUDIOBSY)
662578Srgrimes		cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
663578Srgrimes	else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
664578Srgrimes		cd->audio_status = CD_AS_PLAY_COMPLETED;
665578Srgrimes#endif
666578Srgrimes}
667578Srgrimes
668578Srgrimesstatic int mcd_get(int unit, char *buf, int nmax)
669578Srgrimes{
670578Srgrimes	int port = mcd_data[unit].iobase;
671578Srgrimes	int i,k;
672578Srgrimes
673578Srgrimes	for (i=0; i<nmax; i++) {
674578Srgrimes
675578Srgrimes		/* wait for data */
676578Srgrimes		if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
677578Srgrimes#ifdef MCD_TO_WARNING_ON
678578Srgrimes			printf("mcd%d: timeout mcd_get\n",unit);
679578Srgrimes#endif
680578Srgrimes			return -1;
681578Srgrimes		}
682578Srgrimes		buf[i] = k;
683578Srgrimes	}
684578Srgrimes	return i;
685578Srgrimes}
686578Srgrimes
687578Srgrimesstatic int mcd_send(int unit, int cmd,int nretrys)
688578Srgrimes{
689578Srgrimes	int i,k;
690578Srgrimes	int port = mcd_data[unit].iobase;
691578Srgrimes
692578Srgrimes/*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
693578Srgrimes	for (i=0; i<nretrys; i++) {
694578Srgrimes		outb(port+mcd_command, cmd);
695578Srgrimes		if ((k=mcd_getstat(unit,0)) != -1)
696578Srgrimes			break;
697578Srgrimes	}
698578Srgrimes	if (i == nretrys) {
699578Srgrimes		printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
700578Srgrimes		return -1;
701578Srgrimes	}
702578Srgrimes/*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
703578Srgrimes	return 0;
704578Srgrimes}
705578Srgrimes
706578Srgrimesstatic int bcd2bin(bcd_t b)
707578Srgrimes{
708578Srgrimes	return (b >> 4) * 10 + (b & 15);
709578Srgrimes}
710578Srgrimes
711578Srgrimesstatic bcd_t bin2bcd(int b)
712578Srgrimes{
713578Srgrimes	return ((b / 10) << 4) | (b % 10);
714578Srgrimes}
715578Srgrimes
716578Srgrimesstatic void hsg2msf(int hsg, bcd_t *msf)
717578Srgrimes{
718578Srgrimes	hsg += 150;
719578Srgrimes	M_msf(msf) = bin2bcd(hsg / 4500);
720578Srgrimes	hsg %= 4500;
721578Srgrimes	S_msf(msf) = bin2bcd(hsg / 75);
722578Srgrimes	F_msf(msf) = bin2bcd(hsg % 75);
723578Srgrimes}
724578Srgrimes
725578Srgrimesstatic int msf2hsg(bcd_t *msf)
726578Srgrimes{
727578Srgrimes	return (bcd2bin(M_msf(msf)) * 60 +
728578Srgrimes		bcd2bin(S_msf(msf))) * 75 +
729578Srgrimes		bcd2bin(F_msf(msf)) - 150;
730578Srgrimes}
731578Srgrimes
732578Srgrimesstatic int mcd_volinfo(int unit)
733578Srgrimes{
734578Srgrimes	struct mcd_data *cd = mcd_data + unit;
735578Srgrimes	int i;
736578Srgrimes
737578Srgrimes/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
738578Srgrimes
739578Srgrimes	/* Get the status, in case the disc has been changed */
740578Srgrimes	if (mcd_getstat(unit, 1) < 0) return EIO;
741578Srgrimes
742578Srgrimes	/* Just return if we already have it */
743578Srgrimes	if (cd->flags & MCDVOLINFO) return 0;
744578Srgrimes
745578Srgrimes	/* send volume info command */
746578Srgrimes	if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
747578Srgrimes		return -1;
748578Srgrimes
749578Srgrimes	/* get data */
750578Srgrimes	if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
751578Srgrimes		printf("mcd%d: mcd_volinfo: error read data\n",unit);
752578Srgrimes		return -1;
753578Srgrimes	}
754578Srgrimes
755578Srgrimes	if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
756578Srgrimes		cd->flags |= MCDVOLINFO;	/* volinfo is OK */
757578Srgrimes		return 0;
758578Srgrimes	}
759578Srgrimes
760578Srgrimes	return -1;
761578Srgrimes}
762578Srgrimes
763798Swollmanvoid
764798Swollmanmcdintr(unit)
765798Swollman	int unit;
766578Srgrimes{
767578Srgrimes	int	port = mcd_data[unit].iobase;
768578Srgrimes	u_int	i;
769578Srgrimes
770578Srgrimes	MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0);
771578Srgrimes
772578Srgrimes	/* just read out status and ignore the rest */
773578Srgrimes	if ((inb(port+mcd_xfer)&0xFF) != 0xFF) {
774578Srgrimes		i = inb(port+mcd_status);
775578Srgrimes	}
776578Srgrimes}
777578Srgrimes
778578Srgrimes/* state machine to process read requests
779578Srgrimes * initialize with MCD_S_BEGIN: calculate sizes, and read status
780578Srgrimes * MCD_S_WAITSTAT: wait for status reply, set mode
781578Srgrimes * MCD_S_WAITMODE: waits for status reply from set mode, set read command
782578Srgrimes * MCD_S_WAITREAD: wait for read ready, read data
783578Srgrimes */
784578Srgrimesstatic struct mcd_mbx *mbxsave;
785578Srgrimes
786978Sjkhstatic void mcd_doread(int state, struct mcd_mbx *mbxin)
787578Srgrimes{
788578Srgrimes	struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
789578Srgrimes	int	unit = mbx->unit;
790578Srgrimes	int	port = mbx->port;
791578Srgrimes	struct	buf *bp = mbx->bp;
792578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
793578Srgrimes
794578Srgrimes	int	rm,i,k;
795578Srgrimes	struct mcd_read2 rbuf;
796578Srgrimes	int	blknum;
797578Srgrimes	caddr_t	addr;
798578Srgrimes
799578Srgrimesloop:
800578Srgrimes	switch (state) {
801578Srgrimes	case MCD_S_BEGIN:
802578Srgrimes		mbx = mbxsave = mbxin;
803578Srgrimes
804578Srgrimes	case MCD_S_BEGIN1:
805578Srgrimes		/* get status */
806578Srgrimes		outb(port+mcd_command, MCD_CMDGETSTAT);
807578Srgrimes		mbx->count = RDELAY_WAITSTAT;
808798Swollman		timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
809578Srgrimes		return;
810578Srgrimes	case MCD_S_WAITSTAT:
8111000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT);
812578Srgrimes		if (mbx->count-- >= 0) {
813578Srgrimes			if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
814798Swollman				timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
815578Srgrimes				return;
816578Srgrimes			}
817578Srgrimes			mcd_setflags(unit,cd);
818578Srgrimes			MCD_TRACE("got WAITSTAT delay=%d\n",RDELAY_WAITSTAT-mbx->count,0,0,0);
819578Srgrimes			/* reject, if audio active */
820578Srgrimes			if (cd->status & MCDAUDIOBSY) {
821578Srgrimes				printf("mcd%d: audio is active\n",unit);
822578Srgrimes				goto readerr;
823578Srgrimes			}
824578Srgrimes
825578Srgrimes			/* to check for raw/cooked mode */
826578Srgrimes			if (cd->flags & MCDREADRAW) {
827578Srgrimes				rm = MCD_MD_RAW;
828578Srgrimes				mbx->sz = MCDRBLK;
829578Srgrimes			} else {
830578Srgrimes				rm = MCD_MD_COOKED;
831578Srgrimes				mbx->sz = cd->blksize;
832578Srgrimes			}
833578Srgrimes
834578Srgrimes			mbx->count = RDELAY_WAITMODE;
835578Srgrimes
836578Srgrimes			mcd_put(port+mcd_command, MCD_CMDSETMODE);
837578Srgrimes			mcd_put(port+mcd_command, rm);
838798Swollman			timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */
839578Srgrimes			return;
840578Srgrimes		} else {
841578Srgrimes#ifdef MCD_TO_WARNING_ON
842578Srgrimes			printf("mcd%d: timeout getstatus\n",unit);
843578Srgrimes#endif
844578Srgrimes			goto readerr;
845578Srgrimes		}
846578Srgrimes
847578Srgrimes	case MCD_S_WAITMODE:
8481000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE);
849578Srgrimes		if (mbx->count-- < 0) {
850578Srgrimes#ifdef MCD_TO_WARNING_ON
851578Srgrimes			printf("mcd%d: timeout set mode\n",unit);
852578Srgrimes#endif
853578Srgrimes			goto readerr;
854578Srgrimes		}
855578Srgrimes		if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
856798Swollman			timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100);
857578Srgrimes			return;
858578Srgrimes		}
859578Srgrimes		mcd_setflags(unit,cd);
860578Srgrimes		MCD_TRACE("got WAITMODE delay=%d\n",RDELAY_WAITMODE-mbx->count,0,0,0);
861578Srgrimes		/* for first block */
862578Srgrimes		mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
863578Srgrimes		mbx->skip = 0;
864578Srgrimes
865578Srgrimesnextblock:
866578Srgrimes		blknum 	= (bp->b_blkno / (mbx->sz/DEV_BSIZE))
867578Srgrimes			+ mbx->p_offset + mbx->skip/mbx->sz;
868578Srgrimes
869578Srgrimes		MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",blknum,bp,0,0);
870578Srgrimes
871578Srgrimes		/* build parameter block */
872578Srgrimes		hsg2msf(blknum,rbuf.start_msf);
873578Srgrimes
874578Srgrimes		/* send the read command */
875578Srgrimes		mcd_put(port+mcd_command,MCD_CMDREAD2);
876578Srgrimes		mcd_put(port+mcd_command,rbuf.start_msf[0]);
877578Srgrimes		mcd_put(port+mcd_command,rbuf.start_msf[1]);
878578Srgrimes		mcd_put(port+mcd_command,rbuf.start_msf[2]);
879578Srgrimes		mcd_put(port+mcd_command,0);
880578Srgrimes		mcd_put(port+mcd_command,0);
881578Srgrimes		mcd_put(port+mcd_command,1);
882578Srgrimes		mbx->count = RDELAY_WAITREAD;
883798Swollman		timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
884578Srgrimes		return;
885578Srgrimes	case MCD_S_WAITREAD:
8861000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD);
887578Srgrimes		if (mbx->count-- > 0) {
888578Srgrimes			k = inb(port+mcd_xfer);
889578Srgrimes			if ((k & 2)==0) {
890578Srgrimes			MCD_TRACE("got data delay=%d\n",RDELAY_WAITREAD-mbx->count,0,0,0);
891578Srgrimes				/* data is ready */
892578Srgrimes				addr	= bp->b_un.b_addr + mbx->skip;
893578Srgrimes				outb(port+mcd_ctl2,0x04);	/* XXX */
894578Srgrimes				for (i=0; i<mbx->sz; i++)
895578Srgrimes					*addr++	= inb(port+mcd_rdata);
896578Srgrimes				outb(port+mcd_ctl2,0x0c);	/* XXX */
897578Srgrimes
898578Srgrimes				if (--mbx->nblk > 0) {
899578Srgrimes					mbx->skip += mbx->sz;
900578Srgrimes					goto nextblock;
901578Srgrimes				}
902578Srgrimes
903578Srgrimes				/* return buffer */
904578Srgrimes				bp->b_resid = 0;
905578Srgrimes				biodone(bp);
906578Srgrimes
907578Srgrimes				cd->flags &= ~MCDMBXBSY;
908578Srgrimes				mcd_start(mbx->unit);
909578Srgrimes				return;
910578Srgrimes			}
911578Srgrimes			if ((k & 4)==0)
912578Srgrimes				mcd_getstat(unit,0);
913798Swollman			timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
914578Srgrimes			return;
915578Srgrimes		} else {
916578Srgrimes#ifdef MCD_TO_WARNING_ON
917578Srgrimes			printf("mcd%d: timeout read data\n",unit);
918578Srgrimes#endif
919578Srgrimes			goto readerr;
920578Srgrimes		}
921578Srgrimes	}
922578Srgrimes
923578Srgrimesreaderr:
924578Srgrimes	if (mbx->retry-- > 0) {
925578Srgrimes#ifdef MCD_TO_WARNING_ON
926578Srgrimes		printf("mcd%d: retrying\n",unit);
927578Srgrimes#endif
928578Srgrimes		state = MCD_S_BEGIN1;
929578Srgrimes		goto loop;
930578Srgrimes	}
931578Srgrimes
932578Srgrimes	/* invalidate the buffer */
933578Srgrimes	bp->b_flags |= B_ERROR;
934578Srgrimes	bp->b_resid = bp->b_bcount;
935578Srgrimes	biodone(bp);
936578Srgrimes	mcd_start(mbx->unit);
937578Srgrimes	return;
938578Srgrimes
939578Srgrimes#ifdef NOTDEF
940578Srgrimes	printf("mcd%d: unit timeout, resetting\n",mbx->unit);
941578Srgrimes	outb(mbx->port+mcd_reset,MCD_CMDRESET);
942578Srgrimes	DELAY(300000);
943578Srgrimes	(void)mcd_getstat(mbx->unit,1);
944578Srgrimes	(void)mcd_getstat(mbx->unit,1);
945578Srgrimes	/*cd->status &= ~MCDDSKCHNG; */
946578Srgrimes	cd->debug = 1; /* preventive set debug mode */
947578Srgrimes
948578Srgrimes#endif
949578Srgrimes
950578Srgrimes}
951578Srgrimes
952578Srgrimes#ifndef MCDMINI
953578Srgrimesstatic int mcd_setmode(int unit, int mode)
954578Srgrimes{
955578Srgrimes	struct mcd_data *cd = mcd_data + unit;
956578Srgrimes	int port = cd->iobase;
957578Srgrimes	int retry;
958578Srgrimes
959578Srgrimes	printf("mcd%d: setting mode to %d\n", unit, mode);
960578Srgrimes	for(retry=0; retry<MCD_RETRYS; retry++)
961578Srgrimes	{
962578Srgrimes		outb(port+mcd_command, MCD_CMDSETMODE);
963578Srgrimes		outb(port+mcd_command, mode);
964578Srgrimes		if (mcd_getstat(unit, 0) != -1) return 0;
965578Srgrimes	}
966578Srgrimes
967578Srgrimes	return -1;
968578Srgrimes}
969578Srgrimes
970578Srgrimesstatic int mcd_toc_header(int unit, struct ioc_toc_header *th)
971578Srgrimes{
972578Srgrimes	struct mcd_data *cd = mcd_data + unit;
973578Srgrimes
974578Srgrimes	if (mcd_volinfo(unit) < 0)
975578Srgrimes		return ENXIO;
976578Srgrimes
977578Srgrimes	th->len = msf2hsg(cd->volinfo.vol_msf);
978578Srgrimes	th->starting_track = bcd2bin(cd->volinfo.trk_low);
979578Srgrimes	th->ending_track = bcd2bin(cd->volinfo.trk_high);
980578Srgrimes
981578Srgrimes	return 0;
982578Srgrimes}
983578Srgrimes
984578Srgrimesstatic int mcd_read_toc(int unit)
985578Srgrimes{
986578Srgrimes	struct mcd_data *cd = mcd_data + unit;
987578Srgrimes	struct ioc_toc_header th;
988578Srgrimes	struct mcd_qchninfo q;
989578Srgrimes	int rc, trk, idx, retry;
990578Srgrimes
991578Srgrimes	/* Only read TOC if needed */
992578Srgrimes	if (cd->flags & MCDTOC) return 0;
993578Srgrimes
994578Srgrimes	printf("mcd%d: reading toc header\n", unit);
995578Srgrimes	if (mcd_toc_header(unit, &th) != 0)
996578Srgrimes		return ENXIO;
997578Srgrimes
998578Srgrimes	printf("mcd%d: stopping play\n", unit);
999578Srgrimes	if ((rc=mcd_stop(unit)) != 0)
1000578Srgrimes		return rc;
1001578Srgrimes
1002578Srgrimes	/* try setting the mode twice */
1003578Srgrimes	if (mcd_setmode(unit, MCD_MD_TOC) != 0)
1004578Srgrimes		return EIO;
1005578Srgrimes	if (mcd_setmode(unit, MCD_MD_TOC) != 0)
1006578Srgrimes		return EIO;
1007578Srgrimes
1008578Srgrimes	printf("mcd%d: get_toc reading qchannel info\n",unit);
1009578Srgrimes	for(trk=th.starting_track; trk<=th.ending_track; trk++)
1010578Srgrimes		cd->toc[trk].idx_no = 0;
1011578Srgrimes	trk = th.ending_track - th.starting_track + 1;
1012578Srgrimes	for(retry=0; retry<300 && trk>0; retry++)
1013578Srgrimes	{
1014578Srgrimes		if (mcd_getqchan(unit, &q) < 0) break;
1015578Srgrimes		idx = bcd2bin(q.idx_no);
1016578Srgrimes		if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0)
1017578Srgrimes			if (cd->toc[idx].idx_no == 0)
1018578Srgrimes			{
1019578Srgrimes				cd->toc[idx] = q;
1020578Srgrimes				trk--;
1021578Srgrimes			}
1022578Srgrimes	}
1023578Srgrimes
1024578Srgrimes	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1025578Srgrimes		return EIO;
1026578Srgrimes
1027578Srgrimes	if (trk != 0) return ENXIO;
1028578Srgrimes
1029578Srgrimes	/* add a fake last+1 */
1030578Srgrimes	idx = th.ending_track + 1;
1031578Srgrimes	cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
1032578Srgrimes	cd->toc[idx].trk_no = 0;
1033578Srgrimes	cd->toc[idx].idx_no = 0xAA;
1034578Srgrimes	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
1035578Srgrimes	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
1036578Srgrimes	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
1037578Srgrimes
1038578Srgrimes	cd->flags |= MCDTOC;
1039578Srgrimes
1040578Srgrimes	return 0;
1041578Srgrimes}
1042578Srgrimes
1043578Srgrimesstatic int mcd_toc_entry(int unit, struct ioc_read_toc_entry *te)
1044578Srgrimes{
1045578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1046578Srgrimes	struct ret_toc
1047578Srgrimes	{
1048578Srgrimes		struct ioc_toc_header th;
1049578Srgrimes		struct cd_toc_entry rt;
1050578Srgrimes	} ret_toc;
1051578Srgrimes	struct ioc_toc_header th;
1052578Srgrimes	int rc, i;
1053578Srgrimes
1054578Srgrimes	/* Make sure we have a valid toc */
1055578Srgrimes	if ((rc=mcd_read_toc(unit)) != 0)
1056578Srgrimes		return rc;
1057578Srgrimes
1058578Srgrimes	/* find the toc to copy*/
1059578Srgrimes	i = te->starting_track;
1060578Srgrimes	if (i == MCD_LASTPLUS1)
1061578Srgrimes		i = bcd2bin(cd->volinfo.trk_high) + 1;
1062578Srgrimes
1063578Srgrimes	/* verify starting track */
1064578Srgrimes	if (i < bcd2bin(cd->volinfo.trk_low) ||
1065578Srgrimes		i > bcd2bin(cd->volinfo.trk_high)+1)
1066578Srgrimes		return EINVAL;
1067578Srgrimes
1068578Srgrimes	/* do we have room */
1069578Srgrimes	if (te->data_len < sizeof(struct ioc_toc_header) +
1070578Srgrimes		sizeof(struct cd_toc_entry)) return EINVAL;
1071578Srgrimes
1072578Srgrimes	/* Copy the toc header */
1073578Srgrimes	if (mcd_toc_header(unit, &th) < 0) return EIO;
1074578Srgrimes	ret_toc.th = th;
1075578Srgrimes
1076578Srgrimes	/* copy the toc data */
1077578Srgrimes	ret_toc.rt.control = cd->toc[i].ctrl_adr;
1078578Srgrimes	ret_toc.rt.addr_type = te->address_format;
1079578Srgrimes	ret_toc.rt.track = i;
1080578Srgrimes	if (te->address_format == CD_MSF_FORMAT)
1081578Srgrimes	{
10821097Srgrimes		ret_toc.rt.addr.addr[1] = cd->toc[i].hd_pos_msf[0];
10831097Srgrimes		ret_toc.rt.addr.addr[2] = cd->toc[i].hd_pos_msf[1];
10841097Srgrimes		ret_toc.rt.addr.addr[3] = cd->toc[i].hd_pos_msf[2];
1085578Srgrimes	}
1086578Srgrimes
1087578Srgrimes	/* copy the data back */
1088578Srgrimes	copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
1089578Srgrimes		+ sizeof(struct ioc_toc_header));
1090578Srgrimes
1091578Srgrimes	return 0;
1092578Srgrimes}
1093578Srgrimes
1094578Srgrimesstatic int mcd_stop(int unit)
1095578Srgrimes{
1096578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1097578Srgrimes
1098578Srgrimes	if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0)
1099578Srgrimes		return ENXIO;
1100578Srgrimes	cd->audio_status = CD_AS_PLAY_COMPLETED;
1101578Srgrimes	return 0;
1102578Srgrimes}
1103578Srgrimes
1104578Srgrimesstatic int mcd_getqchan(int unit, struct mcd_qchninfo *q)
1105578Srgrimes{
1106578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1107578Srgrimes
1108578Srgrimes	if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0)
1109578Srgrimes		return -1;
1110578Srgrimes	if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0)
1111578Srgrimes		return -1;
1112578Srgrimes	if (cd->debug)
1113578Srgrimes	printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
1114578Srgrimes		unit,
1115578Srgrimes		q->ctrl_adr, q->trk_no, q->idx_no,
1116578Srgrimes		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
1117578Srgrimes		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
1118578Srgrimes	return 0;
1119578Srgrimes}
1120578Srgrimes
1121578Srgrimesstatic int mcd_subchan(int unit, struct ioc_read_subchannel *sc)
1122578Srgrimes{
1123578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1124578Srgrimes	struct mcd_qchninfo q;
1125578Srgrimes	struct cd_sub_channel_info data;
1126578Srgrimes
1127578Srgrimes	printf("mcd%d: subchan af=%d, df=%d\n", unit,
1128578Srgrimes		sc->address_format,
1129578Srgrimes		sc->data_format);
1130578Srgrimes	if (sc->address_format != CD_MSF_FORMAT) return EIO;
1131578Srgrimes	if (sc->data_format != CD_CURRENT_POSITION) return EIO;
1132578Srgrimes
1133578Srgrimes	if (mcd_getqchan(unit, &q) < 0) return EIO;
1134578Srgrimes
1135578Srgrimes	data.header.audio_status = cd->audio_status;
1136578Srgrimes	data.what.position.data_format = CD_MSF_FORMAT;
1137578Srgrimes	data.what.position.track_number = bcd2bin(q.trk_no);
1138578Srgrimes
1139578Srgrimes	if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0)
1140578Srgrimes		return EFAULT;
1141578Srgrimes	return 0;
1142578Srgrimes}
1143578Srgrimes
1144578Srgrimesstatic int mcd_playtracks(int unit, struct ioc_play_track *pt)
1145578Srgrimes{
1146578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1147578Srgrimes	struct mcd_read2 pb;
1148578Srgrimes	int a = pt->start_track;
1149578Srgrimes	int z = pt->end_track;
1150578Srgrimes	int rc;
1151578Srgrimes
1152578Srgrimes	if ((rc = mcd_read_toc(unit)) != 0) return rc;
1153578Srgrimes
1154578Srgrimes	printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
1155578Srgrimes		a, pt->start_index, z, pt->end_index);
1156578Srgrimes
1157578Srgrimes	if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
1158578Srgrimes		z < cd->volinfo.trk_low || z > cd->volinfo.trk_high)
1159578Srgrimes		return EINVAL;
1160578Srgrimes
1161578Srgrimes	pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
1162578Srgrimes	pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
1163578Srgrimes	pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
1164578Srgrimes	pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
1165578Srgrimes	pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
1166578Srgrimes	pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
1167578Srgrimes
1168578Srgrimes	return mcd_play(unit, &pb);
1169578Srgrimes}
1170578Srgrimes
1171578Srgrimesstatic int mcd_play(int unit, struct mcd_read2 *pb)
1172578Srgrimes{
1173578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1174578Srgrimes	int port = cd->iobase;
1175578Srgrimes	int retry, st;
1176578Srgrimes
1177578Srgrimes	cd->lastpb = *pb;
1178578Srgrimes	for(retry=0; retry<MCD_RETRYS; retry++)
1179578Srgrimes	{
1180578Srgrimes		outb(port+mcd_command, MCD_CMDREAD2);
1181578Srgrimes		outb(port+mcd_command, pb->start_msf[0]);
1182578Srgrimes		outb(port+mcd_command, pb->start_msf[1]);
1183578Srgrimes		outb(port+mcd_command, pb->start_msf[2]);
1184578Srgrimes		outb(port+mcd_command, pb->end_msf[0]);
1185578Srgrimes		outb(port+mcd_command, pb->end_msf[1]);
1186578Srgrimes		outb(port+mcd_command, pb->end_msf[2]);
1187578Srgrimes		if ((st=mcd_getstat(unit, 0)) != -1) break;
1188578Srgrimes	}
1189578Srgrimes
1190578Srgrimes	if (cd->debug)
1191578Srgrimes	printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st);
1192578Srgrimes	if (st == -1) return ENXIO;
1193578Srgrimes	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
1194578Srgrimes	return 0;
1195578Srgrimes}
1196578Srgrimes
1197578Srgrimesstatic int mcd_pause(int unit)
1198578Srgrimes{
1199578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1200578Srgrimes	struct mcd_qchninfo q;
1201578Srgrimes	int rc;
1202578Srgrimes
1203578Srgrimes	/* Verify current status */
1204578Srgrimes	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS)
1205578Srgrimes	{
1206578Srgrimes		printf("mcd%d: pause attempted when not playing\n", unit);
1207578Srgrimes		return EINVAL;
1208578Srgrimes	}
1209578Srgrimes
1210578Srgrimes	/* Get the current position */
1211578Srgrimes	if (mcd_getqchan(unit, &q) < 0) return EIO;
1212578Srgrimes
1213578Srgrimes	/* Copy it into lastpb */
1214578Srgrimes	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
1215578Srgrimes	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
1216578Srgrimes	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
1217578Srgrimes
1218578Srgrimes	/* Stop playing */
1219578Srgrimes	if ((rc=mcd_stop(unit)) != 0) return rc;
1220578Srgrimes
1221578Srgrimes	/* Set the proper status and exit */
1222578Srgrimes	cd->audio_status = CD_AS_PLAY_PAUSED;
1223578Srgrimes	return 0;
1224578Srgrimes}
1225578Srgrimes
1226578Srgrimesstatic int mcd_resume(int unit)
1227578Srgrimes{
1228578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1229578Srgrimes
1230578Srgrimes	if (cd->audio_status != CD_AS_PLAY_PAUSED) return EINVAL;
1231578Srgrimes	return mcd_play(unit, &cd->lastpb);
1232578Srgrimes}
1233578Srgrimes#endif /*!MCDMINI*/
1234578Srgrimes
1235578Srgrimes#endif /* NMCD > 0 */
1236