mcd.c revision 1197
1578Srgrimes/*
2578Srgrimes * Copyright 1993 by Holger Veit (data part)
3578Srgrimes * Copyright 1993 by Brian Moore (audio part)
41197Srgrimes * Changes Copyright 1993 by Gary Clark II
51197Srgrimes *
61197Srgrimes * Rewrote probe routine to work on newer Mitsumi drives.
71197Srgrimes * Additional changes (C) 1994 by Jordan K. Hubbard
81197Srgrimes *
9578Srgrimes * All rights reserved.
10578Srgrimes *
11578Srgrimes * Redistribution and use in source and binary forms, with or without
12578Srgrimes * modification, are permitted provided that the following conditions
13578Srgrimes * are met:
14578Srgrimes * 1. Redistributions of source code must retain the above copyright
15578Srgrimes *    notice, this list of conditions and the following disclaimer.
16578Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
17578Srgrimes *    notice, this list of conditions and the following disclaimer in the
18578Srgrimes *    documentation and/or other materials provided with the distribution.
19578Srgrimes * 3. All advertising materials mentioning features or use of this software
20578Srgrimes *    must display the following acknowledgement:
21578Srgrimes *	This software was developed by Holger Veit and Brian Moore
221197Srgrimes *	for use with "386BSD" and similar operating systems.
23578Srgrimes *    "Similar operating systems" includes mainly non-profit oriented
24578Srgrimes *    systems for research and education, including but not restricted to
25578Srgrimes *    "NetBSD", "FreeBSD", "Mach" (by CMU).
26578Srgrimes * 4. Neither the name of the developer(s) nor the name "386BSD"
27578Srgrimes *    may be used to endorse or promote products derived from this
28578Srgrimes *    software without specific prior written permission.
29578Srgrimes *
30578Srgrimes * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
31578Srgrimes * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32578Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33578Srgrimes * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER(S) BE
34578Srgrimes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
35578Srgrimes * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
36578Srgrimes * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
37578Srgrimes * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
38578Srgrimes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
39578Srgrimes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
40578Srgrimes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41578Srgrimes *
421197Srgrimes *	$Id: mcd.c,v 1.9 1994/02/07 15:46:22 davidg Exp $
43578Srgrimes */
44578Srgrimesstatic char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
45578Srgrimes
46578Srgrimes#include "mcd.h"
47578Srgrimes#if NMCD > 0
48578Srgrimes#include "types.h"
49578Srgrimes#include "param.h"
50578Srgrimes#include "systm.h"
51578Srgrimes#include "conf.h"
52578Srgrimes#include "file.h"
53578Srgrimes#include "buf.h"
54578Srgrimes#include "stat.h"
55578Srgrimes#include "uio.h"
56578Srgrimes#include "ioctl.h"
57578Srgrimes#include "cdio.h"
58578Srgrimes#include "errno.h"
59578Srgrimes#include "dkbad.h"
60578Srgrimes#include "disklabel.h"
61578Srgrimes#include "i386/isa/isa.h"
62578Srgrimes#include "i386/isa/isa_device.h"
63578Srgrimes#include "mcdreg.h"
64578Srgrimes
65578Srgrimes/* user definable options */
66578Srgrimes/*#define MCD_TO_WARNING_ON*/	/* define to get timeout messages */
67578Srgrimes/*#define MCDMINI*/ 		/* define for a mini configuration for boot kernel */
68578Srgrimes
69578Srgrimes
70578Srgrimes#ifdef MCDMINI
71578Srgrimes#define MCD_TRACE(fmt,a,b,c,d)
72578Srgrimes#ifdef MCD_TO_WARNING_ON
73578Srgrimes#undef MCD_TO_WARNING_ON
74578Srgrimes#endif
75578Srgrimes#else
76978Sjkh#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);}}
77578Srgrimes#endif
78578Srgrimes
79578Srgrimes#define mcd_part(dev)	((minor(dev)) & 7)
80578Srgrimes#define mcd_unit(dev)	(((minor(dev)) & 0x38) >> 3)
81578Srgrimes#define mcd_phys(dev)	(((minor(dev)) & 0x40) >> 6)
82578Srgrimes#define RAW_PART	3
83578Srgrimes
84578Srgrimes/* flags */
85578Srgrimes#define MCDOPEN		0x0001	/* device opened */
86578Srgrimes#define MCDVALID	0x0002	/* parameters loaded */
87578Srgrimes#define MCDINIT		0x0004	/* device is init'd */
88578Srgrimes#define MCDWAIT		0x0008	/* waiting for something */
89578Srgrimes#define MCDLABEL	0x0010	/* label is read */
90578Srgrimes#define	MCDPROBING	0x0020	/* probing */
91578Srgrimes#define	MCDREADRAW	0x0040	/* read raw mode (2352 bytes) */
92578Srgrimes#define	MCDVOLINFO	0x0080	/* already read volinfo */
93578Srgrimes#define	MCDTOC		0x0100	/* already read toc */
94578Srgrimes#define	MCDMBXBSY	0x0200	/* local mbx is busy */
95578Srgrimes
96578Srgrimes/* status */
97578Srgrimes#define	MCDAUDIOBSY	MCD_ST_AUDIOBSY		/* playing audio */
98578Srgrimes#define MCDDSKCHNG	MCD_ST_DSKCHNG		/* sensed change of disk */
99578Srgrimes#define MCDDSKIN	MCD_ST_DSKIN		/* sensed disk in drive */
100578Srgrimes#define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
101578Srgrimes
102578Srgrimes/* toc */
103578Srgrimes#define MCD_MAXTOCS	104	/* from the Linux driver */
104578Srgrimes#define MCD_LASTPLUS1	170	/* special toc entry */
105578Srgrimes
106578Srgrimesstruct mcd_mbx {
107578Srgrimes	short		unit;
108578Srgrimes	short		port;
109578Srgrimes	short		retry;
110578Srgrimes	short		nblk;
111578Srgrimes	int		sz;
112578Srgrimes	u_long		skip;
113578Srgrimes	struct buf	*bp;
114578Srgrimes	int		p_offset;
115578Srgrimes	short		count;
116578Srgrimes};
117578Srgrimes
118578Srgrimesstruct mcd_data {
119578Srgrimes	short	config;
120578Srgrimes	short	flags;
121578Srgrimes	short	status;
122578Srgrimes	int	blksize;
123578Srgrimes	u_long	disksize;
124578Srgrimes	int	iobase;
125578Srgrimes	struct disklabel dlabel;
126578Srgrimes	int	partflags[MAXPARTITIONS];
127578Srgrimes	int	openflags;
128578Srgrimes	struct mcd_volinfo volinfo;
129578Srgrimes#ifndef MCDMINI
130578Srgrimes	struct mcd_qchninfo toc[MCD_MAXTOCS];
131578Srgrimes	short	audio_status;
132578Srgrimes	struct mcd_read2 lastpb;
133578Srgrimes#endif
134578Srgrimes	short	debug;
135578Srgrimes	struct buf head;		/* head of buf queue */
136578Srgrimes	struct mcd_mbx mbx;
137578Srgrimes} mcd_data[NMCD];
138578Srgrimes
139578Srgrimes/* reader state machine */
140578Srgrimes#define MCD_S_BEGIN	0
141578Srgrimes#define MCD_S_BEGIN1	1
142578Srgrimes#define MCD_S_WAITSTAT	2
143578Srgrimes#define MCD_S_WAITMODE	3
144578Srgrimes#define MCD_S_WAITREAD	4
145578Srgrimes
146578Srgrimes/* prototypes */
147578Srgrimesint	mcdopen(dev_t dev);
148578Srgrimesint	mcdclose(dev_t dev);
149798Swollmanvoid	mcdstrategy(struct buf *bp);
150578Srgrimesint	mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
151578Srgrimesint	mcdsize(dev_t dev);
152578Srgrimesstatic	void	mcd_done(struct mcd_mbx *mbx);
153578Srgrimesstatic	void	mcd_start(int unit);
154578Srgrimesstatic	int	mcd_getdisklabel(int unit);
155578Srgrimesstatic	void	mcd_configure(struct mcd_data *cd);
156578Srgrimesstatic	int	mcd_get(int unit, char *buf, int nmax);
157578Srgrimesstatic	void	mcd_setflags(int unit,struct mcd_data *cd);
158578Srgrimesstatic	int	mcd_getstat(int unit,int sflg);
159578Srgrimesstatic	int	mcd_send(int unit, int cmd,int nretrys);
160578Srgrimesstatic	int	bcd2bin(bcd_t b);
161578Srgrimesstatic	bcd_t	bin2bcd(int b);
162578Srgrimesstatic	void	hsg2msf(int hsg, bcd_t *msf);
163578Srgrimesstatic	int	msf2hsg(bcd_t *msf);
164578Srgrimesstatic	int	mcd_volinfo(int unit);
165578Srgrimesstatic	int	mcd_waitrdy(int port,int dly);
166978Sjkhstatic 	void	mcd_doread(int state, struct mcd_mbx *mbxin);
167578Srgrimes#ifndef MCDMINI
168578Srgrimesstatic	int 	mcd_setmode(int unit, int mode);
169578Srgrimesstatic	int	mcd_getqchan(int unit, struct mcd_qchninfo *q);
170578Srgrimesstatic	int	mcd_subchan(int unit, struct ioc_read_subchannel *sc);
171578Srgrimesstatic	int	mcd_toc_header(int unit, struct ioc_toc_header *th);
172578Srgrimesstatic	int	mcd_read_toc(int unit);
173578Srgrimesstatic	int	mcd_toc_entry(int unit, struct ioc_read_toc_entry *te);
174578Srgrimesstatic	int	mcd_stop(int unit);
175578Srgrimesstatic	int	mcd_playtracks(int unit, struct ioc_play_track *pt);
176578Srgrimesstatic	int	mcd_play(int unit, struct mcd_read2 *pb);
177578Srgrimesstatic	int	mcd_pause(int unit);
178578Srgrimesstatic	int	mcd_resume(int unit);
179578Srgrimes#endif
180578Srgrimes
181578Srgrimesextern	int	hz;
182578Srgrimesextern	int	mcd_probe(struct isa_device *dev);
183578Srgrimesextern	int	mcd_attach(struct isa_device *dev);
184578Srgrimesstruct	isa_driver	mcddriver = { mcd_probe, mcd_attach, "mcd" };
185578Srgrimes
186578Srgrimes#define mcd_put(port,byte)	outb(port,byte)
187578Srgrimes
188578Srgrimes#define MCD_RETRYS	5
189578Srgrimes#define MCD_RDRETRYS	8
190578Srgrimes
191578Srgrimes#define MCDBLK	2048	/* for cooked mode */
192578Srgrimes#define MCDRBLK	2352	/* for raw mode */
193578Srgrimes
194578Srgrimes/* several delays */
195578Srgrimes#define RDELAY_WAITSTAT	300
196578Srgrimes#define RDELAY_WAITMODE	300
197578Srgrimes#define RDELAY_WAITREAD	800
198578Srgrimes
199578Srgrimes#define DELAY_STATUS	10000l		/* 10000 * 1us */
200578Srgrimes#define DELAY_GETREPLY	200000l		/* 200000 * 2us */
201578Srgrimes#define DELAY_SEEKREAD	20000l		/* 20000 * 1us */
202578Srgrimes#define mcd_delay	DELAY
203578Srgrimes
204578Srgrimesint mcd_attach(struct isa_device *dev)
205578Srgrimes{
206578Srgrimes	struct mcd_data *cd = mcd_data + dev->id_unit;
207578Srgrimes	int i;
208578Srgrimes
209578Srgrimes	cd->iobase = dev->id_iobase;
210578Srgrimes	cd->flags |= MCDINIT;
211578Srgrimes	cd->openflags = 0;
212578Srgrimes	for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
213578Srgrimes
214578Srgrimes#ifdef NOTYET
215578Srgrimes	/* wire controller for interrupts and dma */
216578Srgrimes	mcd_configure(cd);
217578Srgrimes#endif
218578Srgrimes
219578Srgrimes	return 1;
220578Srgrimes}
221578Srgrimes
222578Srgrimesint mcdopen(dev_t dev)
223578Srgrimes{
224578Srgrimes	int unit,part,phys;
225578Srgrimes	struct mcd_data *cd;
226578Srgrimes
227578Srgrimes	unit = mcd_unit(dev);
228578Srgrimes	if (unit >= NMCD)
229578Srgrimes		return ENXIO;
230578Srgrimes
231578Srgrimes	cd = mcd_data + unit;
232578Srgrimes	part = mcd_part(dev);
233578Srgrimes	phys = mcd_phys(dev);
234578Srgrimes
235578Srgrimes	/* not initialized*/
236578Srgrimes	if (!(cd->flags & MCDINIT))
237578Srgrimes		return ENXIO;
238578Srgrimes
239578Srgrimes	/* invalidated in the meantime? mark all open part's invalid */
240578Srgrimes	if (!(cd->flags & MCDVALID) && cd->openflags)
241578Srgrimes		return ENXIO;
242578Srgrimes
243578Srgrimes	if (mcd_getstat(unit,1) < 0)
244578Srgrimes		return ENXIO;
245578Srgrimes
246578Srgrimes	/* XXX get a default disklabel */
247578Srgrimes	mcd_getdisklabel(unit);
248578Srgrimes
249578Srgrimes	if (mcdsize(dev) < 0) {
250578Srgrimes		printf("mcd%d: failed to get disk size\n",unit);
251578Srgrimes		return ENXIO;
252578Srgrimes	} else
253578Srgrimes		cd->flags |= MCDVALID;
254578Srgrimes
255578SrgrimesMCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
256578Srgrimes	part,cd->disksize,cd->blksize,0);
257578Srgrimes
258578Srgrimes	if (part == RAW_PART ||
259578Srgrimes		(part < cd->dlabel.d_npartitions &&
260578Srgrimes		cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
261578Srgrimes		cd->partflags[part] |= MCDOPEN;
262578Srgrimes		cd->openflags |= (1<<part);
263578Srgrimes		if (part == RAW_PART && phys != 0)
264578Srgrimes			cd->partflags[part] |= MCDREADRAW;
265578Srgrimes		return 0;
266578Srgrimes	}
267578Srgrimes
268578Srgrimes	return ENXIO;
269578Srgrimes}
270578Srgrimes
271578Srgrimesint mcdclose(dev_t dev)
272578Srgrimes{
273578Srgrimes	int unit,part,phys;
274578Srgrimes	struct mcd_data *cd;
275578Srgrimes
276578Srgrimes	unit = mcd_unit(dev);
277578Srgrimes	if (unit >= NMCD)
278578Srgrimes		return ENXIO;
279578Srgrimes
280578Srgrimes	cd = mcd_data + unit;
281578Srgrimes	part = mcd_part(dev);
282578Srgrimes	phys = mcd_phys(dev);
283578Srgrimes
284578Srgrimes	if (!(cd->flags & MCDINIT))
285578Srgrimes		return ENXIO;
286578Srgrimes
287578Srgrimes	mcd_getstat(unit,1);	/* get status */
288578Srgrimes
289578Srgrimes	/* close channel */
290578Srgrimes	cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
291578Srgrimes	cd->openflags &= ~(1<<part);
292578Srgrimes	MCD_TRACE("close: partition=%d\n",part,0,0,0);
293578Srgrimes
294578Srgrimes	return 0;
295578Srgrimes}
296578Srgrimes
297798Swollmanvoid
298798Swollmanmcdstrategy(struct buf *bp)
299578Srgrimes{
300578Srgrimes	struct mcd_data *cd;
301578Srgrimes	struct buf *qp;
302578Srgrimes	int s;
303578Srgrimes
304578Srgrimes	int unit = mcd_unit(bp->b_dev);
305578Srgrimes
306578Srgrimes	cd = mcd_data + unit;
307578Srgrimes
308578Srgrimes	/* test validity */
309578Srgrimes/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
310578Srgrimes	bp,unit,bp->b_blkno,bp->b_bcount);*/
311578Srgrimes	if (unit >= NMCD || bp->b_blkno < 0) {
312578Srgrimes		printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
313578Srgrimes			unit, bp->b_blkno, bp->b_bcount);
314578Srgrimes		pg("mcd: mcdstratregy failure");
315578Srgrimes		bp->b_error = EINVAL;
316578Srgrimes		bp->b_flags |= B_ERROR;
317578Srgrimes		goto bad;
318578Srgrimes	}
319578Srgrimes
320578Srgrimes	/* if device invalidated (e.g. media change, door open), error */
321578Srgrimes	if (!(cd->flags & MCDVALID)) {
322578SrgrimesMCD_TRACE("strategy: drive not valid\n",0,0,0,0);
323578Srgrimes		bp->b_error = EIO;
324578Srgrimes		goto bad;
325578Srgrimes	}
326578Srgrimes
327578Srgrimes	/* read only */
328578Srgrimes	if (!(bp->b_flags & B_READ)) {
329578Srgrimes		bp->b_error = EROFS;
330578Srgrimes		goto bad;
331578Srgrimes	}
332578Srgrimes
333578Srgrimes	/* no data to read */
334578Srgrimes	if (bp->b_bcount == 0)
335578Srgrimes		goto done;
336578Srgrimes
337578Srgrimes	/* for non raw access, check partition limits */
338578Srgrimes	if (mcd_part(bp->b_dev) != RAW_PART) {
339578Srgrimes		if (!(cd->flags & MCDLABEL)) {
340578Srgrimes			bp->b_error = EIO;
341578Srgrimes			goto bad;
342578Srgrimes		}
343578Srgrimes		/* adjust transfer if necessary */
344578Srgrimes		if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
345578Srgrimes			goto done;
346578Srgrimes		}
347578Srgrimes	}
348578Srgrimes
349578Srgrimes	/* queue it */
350578Srgrimes	qp = &cd->head;
351578Srgrimes	s = splbio();
352578Srgrimes	disksort(qp,bp);
353578Srgrimes	splx(s);
354578Srgrimes
355578Srgrimes	/* now check whether we can perform processing */
356578Srgrimes	mcd_start(unit);
357578Srgrimes	return;
358578Srgrimes
359578Srgrimesbad:
360578Srgrimes	bp->b_flags |= B_ERROR;
361578Srgrimesdone:
362578Srgrimes	bp->b_resid = bp->b_bcount;
363578Srgrimes	biodone(bp);
364578Srgrimes	return;
365578Srgrimes}
366578Srgrimes
367578Srgrimesstatic void mcd_start(int unit)
368578Srgrimes{
369578Srgrimes	struct mcd_data *cd = mcd_data + unit;
370578Srgrimes	struct buf *bp, *qp = &cd->head;
371578Srgrimes	struct partition *p;
372578Srgrimes	int part;
373578Srgrimes	register s = splbio();
374578Srgrimes
375578Srgrimes	if (cd->flags & MCDMBXBSY)
376578Srgrimes		return;
377578Srgrimes
378578Srgrimes	if ((bp = qp->b_actf) != 0) {
379578Srgrimes		/* block found to process, dequeue */
380578Srgrimes		/*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
381578Srgrimes		qp->b_actf = bp->av_forw;
382578Srgrimes		splx(s);
383578Srgrimes	} else {
384578Srgrimes		/* nothing to do */
385578Srgrimes		splx(s);
386578Srgrimes		return;
387578Srgrimes	}
388578Srgrimes
389578Srgrimes	/* changed media? */
390578Srgrimes	if (!(cd->flags	& MCDVALID)) {
391578Srgrimes		MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
392578Srgrimes		return;
393578Srgrimes	}
394578Srgrimes
395578Srgrimes	p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
396578Srgrimes
397578Srgrimes	cd->flags |= MCDMBXBSY;
398578Srgrimes	cd->mbx.unit = unit;
399578Srgrimes	cd->mbx.port = cd->iobase;
400578Srgrimes	cd->mbx.retry = MCD_RETRYS;
401578Srgrimes	cd->mbx.bp = bp;
402578Srgrimes	cd->mbx.p_offset = p->p_offset;
403578Srgrimes
404578Srgrimes	/* calling the read routine */
405978Sjkh	mcd_doread(MCD_S_BEGIN,&(cd->mbx));
406578Srgrimes	/* triggers mcd_start, when successful finished */
407578Srgrimes	return;
408578Srgrimes}
409578Srgrimes
410578Srgrimesint mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
411578Srgrimes{
412578Srgrimes	struct mcd_data *cd;
413578Srgrimes	int unit,part;
414578Srgrimes
415578Srgrimes	unit = mcd_unit(dev);
416578Srgrimes	part = mcd_part(dev);
417578Srgrimes	cd = mcd_data + unit;
418578Srgrimes
419578Srgrimes#ifdef MCDMINI
420578Srgrimes	return ENOTTY;
421578Srgrimes#else
422578Srgrimes	if (!(cd->flags & MCDVALID))
423578Srgrimes		return EIO;
424578SrgrimesMCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
425578Srgrimes
426578Srgrimes	switch (cmd) {
427578Srgrimes	case DIOCSBAD:
428578Srgrimes		return EINVAL;
429578Srgrimes	case DIOCGDINFO:
430578Srgrimes	case DIOCGPART:
431578Srgrimes	case DIOCWDINFO:
432578Srgrimes	case DIOCSDINFO:
433578Srgrimes	case DIOCWLABEL:
434578Srgrimes		return ENOTTY;
435578Srgrimes	case CDIOCPLAYTRACKS:
436578Srgrimes		return mcd_playtracks(unit, (struct ioc_play_track *) addr);
437578Srgrimes	case CDIOCPLAYBLOCKS:
438578Srgrimes		return mcd_play(unit, (struct mcd_read2 *) addr);
439578Srgrimes	case CDIOCREADSUBCHANNEL:
440578Srgrimes		return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
441578Srgrimes	case CDIOREADTOCHEADER:
442578Srgrimes		return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
443578Srgrimes	case CDIOREADTOCENTRYS:
444578Srgrimes		return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr);
445578Srgrimes	case CDIOCSETPATCH:
446578Srgrimes	case CDIOCGETVOL:
447578Srgrimes	case CDIOCSETVOL:
448578Srgrimes	case CDIOCSETMONO:
449578Srgrimes	case CDIOCSETSTERIO:
450578Srgrimes	case CDIOCSETMUTE:
451578Srgrimes	case CDIOCSETLEFT:
452578Srgrimes	case CDIOCSETRIGHT:
453578Srgrimes		return EINVAL;
454578Srgrimes	case CDIOCRESUME:
455578Srgrimes		return mcd_resume(unit);
456578Srgrimes	case CDIOCPAUSE:
457578Srgrimes		return mcd_pause(unit);
458578Srgrimes	case CDIOCSTART:
459578Srgrimes		return EINVAL;
460578Srgrimes	case CDIOCSTOP:
461578Srgrimes		return mcd_stop(unit);
462578Srgrimes	case CDIOCEJECT:
463578Srgrimes		return EINVAL;
464578Srgrimes	case CDIOCSETDEBUG:
465578Srgrimes		cd->debug = 1;
466578Srgrimes		return 0;
467578Srgrimes	case CDIOCCLRDEBUG:
468578Srgrimes		cd->debug = 0;
469578Srgrimes		return 0;
470578Srgrimes	case CDIOCRESET:
471578Srgrimes		return EINVAL;
472578Srgrimes	default:
473578Srgrimes		return ENOTTY;
474578Srgrimes	}
475578Srgrimes	/*NOTREACHED*/
476578Srgrimes#endif /*!MCDMINI*/
477578Srgrimes}
478578Srgrimes
479578Srgrimes/* this could have been taken from scsi/cd.c, but it is not clear
480578Srgrimes * whether the scsi cd driver is linked in
481578Srgrimes */
482578Srgrimesstatic int mcd_getdisklabel(int unit)
483578Srgrimes{
484578Srgrimes	struct mcd_data *cd = mcd_data + unit;
485578Srgrimes
486578Srgrimes	if (cd->flags & MCDLABEL)
487578Srgrimes		return -1;
488578Srgrimes
489578Srgrimes	bzero(&cd->dlabel,sizeof(struct disklabel));
490578Srgrimes	strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16);
491578Srgrimes	strncpy(cd->dlabel.d_packname,"unknown        ",16);
492578Srgrimes	cd->dlabel.d_secsize 	= cd->blksize;
493578Srgrimes	cd->dlabel.d_nsectors	= 100;
494578Srgrimes	cd->dlabel.d_ntracks	= 1;
495578Srgrimes	cd->dlabel.d_ncylinders	= (cd->disksize/100)+1;
496578Srgrimes	cd->dlabel.d_secpercyl	= 100;
497578Srgrimes	cd->dlabel.d_secperunit	= cd->disksize;
498578Srgrimes	cd->dlabel.d_rpm	= 300;
499578Srgrimes	cd->dlabel.d_interleave	= 1;
500578Srgrimes	cd->dlabel.d_flags	= D_REMOVABLE;
501578Srgrimes	cd->dlabel.d_npartitions= 1;
502578Srgrimes	cd->dlabel.d_partitions[0].p_offset = 0;
503578Srgrimes	cd->dlabel.d_partitions[0].p_size = cd->disksize;
504578Srgrimes	cd->dlabel.d_partitions[0].p_fstype = 9;
505578Srgrimes	cd->dlabel.d_magic	= DISKMAGIC;
506578Srgrimes	cd->dlabel.d_magic2	= DISKMAGIC;
507578Srgrimes	cd->dlabel.d_checksum	= dkcksum(&cd->dlabel);
508578Srgrimes
509578Srgrimes	cd->flags |= MCDLABEL;
510578Srgrimes	return 0;
511578Srgrimes}
512578Srgrimes
513578Srgrimesint mcdsize(dev_t dev)
514578Srgrimes{
515578Srgrimes	int size;
516578Srgrimes	int unit = mcd_unit(dev);
517578Srgrimes	struct mcd_data *cd = mcd_data + unit;
518578Srgrimes
519578Srgrimes	if (mcd_volinfo(unit) >= 0) {
520578Srgrimes		cd->blksize = MCDBLK;
521578Srgrimes		size = msf2hsg(cd->volinfo.vol_msf);
522578Srgrimes		cd->disksize = size * (MCDBLK/DEV_BSIZE);
523578Srgrimes		return 0;
524578Srgrimes	}
525578Srgrimes	return -1;
526578Srgrimes}
527578Srgrimes
528578Srgrimes/***************************************************************
529578Srgrimes * lower level of driver starts here
530578Srgrimes **************************************************************/
531578Srgrimes
532578Srgrimes#ifdef NOTDEF
5331197Srgrimesstatic char
5341197Srgrimesirqs[] = {
535578Srgrimes	0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
536578Srgrimes	0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
537578Srgrimes};
538578Srgrimes
5391197Srgrimesstatic char
5401197Srgrimesdrqs[] = {
541578Srgrimes	0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
542578Srgrimes};
543578Srgrimes#endif
544578Srgrimes
5451197Srgrimesstatic void
5461197Srgrimesmcd_configure(struct mcd_data *cd)
547578Srgrimes{
548578Srgrimes	outb(cd->iobase+mcd_config,cd->config);
549578Srgrimes}
550578Srgrimes
5511197Srgrimes/* Wait for non-busy - return 0 on timeout */
5521197Srgrimesstatic int
5531197Srgrimestwiddle_thumbs(int port, int unit, int count, char *whine)
5541197Srgrimes{
5551197Srgrimes	int i;
5561197Srgrimes
5571197Srgrimes	for (i = 0; i < count; i++) {
5581197Srgrimes		if (!(inb(port+MCD_FLAGS) & MCD_ST_BUSY)) {
5591197Srgrimes			return 1;
5601197Srgrimes		}
5611197Srgrimes	}
5621197Srgrimes#ifdef MCD_TO_WARNING_ON
5631197Srgrimes	printf("mcd%d: timeout %s\n", unit, whine);
5641197Srgrimes#endif
5651197Srgrimes	return 0;
5661197Srgrimes}
5671197Srgrimes
568978Sjkh/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
569578Srgrimes
5701197Srgrimesint
5711197Srgrimesmcd_probe(struct isa_device *dev)
572578Srgrimes{
573578Srgrimes	int port = dev->id_iobase;
574578Srgrimes	int unit = dev->id_unit;
5751197Srgrimes	int i, j;
5761197Srgrimes	int status;
5771197Srgrimes	unsigned char stbytes[3];
578578Srgrimes
579578Srgrimes	mcd_data[unit].flags = MCDPROBING;
580578Srgrimes
581578Srgrimes#ifdef NOTDEF
582578Srgrimes	/* get irq/drq configuration word */
583578Srgrimes	mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
584578Srgrimes#else
585578Srgrimes	mcd_data[unit].config = 0;
586578Srgrimes#endif
587578Srgrimes
588578Srgrimes	/* send a reset */
5891197Srgrimes	outb(port+MCD_FLAGS, M_RESET);
590578Srgrimes
5911197Srgrimes	/*
5921197Srgrimes	 * delay awhile by getting any pending garbage (old data) and
5931197Srgrimes	 * throwing it away.
5941197Srgrimes	 */
5951197Srgrimes	for (i = 1000000; i != 0; i--) {
5961197Srgrimes		inb(port+MCD_FLAGS);
5971197Srgrimes	}
598578Srgrimes
5991197Srgrimes	/* Get status */
6001197Srgrimes	outb(port+MCD_DATA, MCD_CMDGETSTAT);
6011197Srgrimes	if (!twiddle_thumbs(port, unit, 1000000, "getting status")) {
6021197Srgrimes		return 0;	/* Timeout */
6031197Srgrimes	}
6041197Srgrimes	status = inb(port+MCD_DATA);
605578Srgrimes
6061197Srgrimes	/* Get version information */
6071197Srgrimes	outb(port+MCD_DATA, MCD_CMDCONTINFO);
6081197Srgrimes	for (j = 0; j < 3; j++) {
6091197Srgrimes		if (!twiddle_thumbs(port, unit, 3000, "getting version info")) {
6101197Srgrimes			return 0;
6111197Srgrimes		}
6121197Srgrimes		stbytes[j] = (inb(port+MCD_DATA) & 0xFF);
6131197Srgrimes	}
6141197Srgrimes	printf("mcd%d: version information is %x %c %x\n", unit,
6151197Srgrimes		stbytes[0], stbytes[1], stbytes[2]);
6161197Srgrimes	if (stbytes[1] >= 4) {
6171197Srgrimes		outb(port+MCD_CTRL, M_PICKLE);
6181197Srgrimes		printf("mcd%d: Adjusted for newer drive model\n", unit);
6191197Srgrimes	}
6201197Srgrimes	return 4;
621578Srgrimes}
622578Srgrimes
623978Sjkh
6241197Srgrimesstatic int
6251197Srgrimesmcd_waitrdy(int port,int dly)
626578Srgrimes{
627578Srgrimes	int i;
628578Srgrimes
629578Srgrimes	/* wait until xfer port senses data ready */
630578Srgrimes	for (i=0; i<dly; i++) {
631578Srgrimes		if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0)
632578Srgrimes			return 0;
633578Srgrimes		mcd_delay(1);
634578Srgrimes	}
635578Srgrimes	return -1;
636578Srgrimes}
637578Srgrimes
6381197Srgrimesstatic int
6391197Srgrimesmcd_getreply(int unit,int dly)
640578Srgrimes{
641578Srgrimes	int	i;
642578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
643578Srgrimes	int	port = cd->iobase;
644578Srgrimes
645578Srgrimes	/* wait data to become ready */
646578Srgrimes	if (mcd_waitrdy(port,dly)<0) {
647578Srgrimes#ifdef MCD_TO_WARNING_ON
648578Srgrimes		printf("mcd%d: timeout getreply\n",unit);
649578Srgrimes#endif
650578Srgrimes		return -1;
651578Srgrimes	}
652578Srgrimes
653578Srgrimes	/* get the data */
654578Srgrimes	return inb(port+mcd_status) & 0xFF;
655578Srgrimes}
656578Srgrimes
6571197Srgrimesstatic int
6581197Srgrimesmcd_getstat(int unit,int sflg)
659578Srgrimes{
660578Srgrimes	int	i;
661578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
662578Srgrimes	int	port = cd->iobase;
663578Srgrimes
664578Srgrimes	/* get the status */
665578Srgrimes	if (sflg)
666578Srgrimes		outb(port+mcd_command, MCD_CMDGETSTAT);
667578Srgrimes	i = mcd_getreply(unit,DELAY_GETREPLY);
668578Srgrimes	if (i<0) return -1;
669578Srgrimes
670578Srgrimes	cd->status = i;
671578Srgrimes
672578Srgrimes	mcd_setflags(unit,cd);
673578Srgrimes	return cd->status;
674578Srgrimes}
675578Srgrimes
6761197Srgrimesstatic void
6771197Srgrimesmcd_setflags(int unit, struct mcd_data *cd)
678578Srgrimes{
679578Srgrimes	/* check flags */
680578Srgrimes	if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
681578Srgrimes		MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
682578Srgrimes		cd->flags &= ~MCDVALID;
683578Srgrimes	}
684578Srgrimes
685578Srgrimes#ifndef MCDMINI
686578Srgrimes	if (cd->status & MCDAUDIOBSY)
687578Srgrimes		cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
688578Srgrimes	else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
689578Srgrimes		cd->audio_status = CD_AS_PLAY_COMPLETED;
690578Srgrimes#endif
691578Srgrimes}
692578Srgrimes
6931197Srgrimesstatic int
6941197Srgrimesmcd_get(int unit, char *buf, int nmax)
695578Srgrimes{
696578Srgrimes	int port = mcd_data[unit].iobase;
697578Srgrimes	int i,k;
698578Srgrimes
699578Srgrimes	for (i=0; i<nmax; i++) {
700578Srgrimes		/* wait for data */
701578Srgrimes		if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
702578Srgrimes#ifdef MCD_TO_WARNING_ON
703578Srgrimes			printf("mcd%d: timeout mcd_get\n",unit);
704578Srgrimes#endif
705578Srgrimes			return -1;
706578Srgrimes		}
707578Srgrimes		buf[i] = k;
708578Srgrimes	}
709578Srgrimes	return i;
710578Srgrimes}
711578Srgrimes
7121197Srgrimesstatic int
7131197Srgrimesmcd_send(int unit, int cmd,int nretrys)
714578Srgrimes{
715578Srgrimes	int i,k;
716578Srgrimes	int port = mcd_data[unit].iobase;
717578Srgrimes
718578Srgrimes/*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
719578Srgrimes	for (i=0; i<nretrys; i++) {
720578Srgrimes		outb(port+mcd_command, cmd);
7211197Srgrimes		if ((k=mcd_getstat(unit,0)) != -1) {
722578Srgrimes			break;
7231197Srgrimes		}
724578Srgrimes	}
725578Srgrimes	if (i == nretrys) {
726578Srgrimes		printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
727578Srgrimes		return -1;
728578Srgrimes	}
729578Srgrimes/*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
730578Srgrimes	return 0;
731578Srgrimes}
732578Srgrimes
7331197Srgrimesstatic int
7341197Srgrimesbcd2bin(bcd_t b)
735578Srgrimes{
736578Srgrimes	return (b >> 4) * 10 + (b & 15);
737578Srgrimes}
738578Srgrimes
7391197Srgrimesstatic bcd_t
7401197Srgrimesbin2bcd(int b)
741578Srgrimes{
742578Srgrimes	return ((b / 10) << 4) | (b % 10);
743578Srgrimes}
744578Srgrimes
7451197Srgrimesstatic void
7461197Srgrimeshsg2msf(int hsg, bcd_t *msf)
747578Srgrimes{
748578Srgrimes	hsg += 150;
749578Srgrimes	M_msf(msf) = bin2bcd(hsg / 4500);
750578Srgrimes	hsg %= 4500;
751578Srgrimes	S_msf(msf) = bin2bcd(hsg / 75);
752578Srgrimes	F_msf(msf) = bin2bcd(hsg % 75);
753578Srgrimes}
754578Srgrimes
7551197Srgrimesstatic int
7561197Srgrimesmsf2hsg(bcd_t *msf)
757578Srgrimes{
758578Srgrimes	return (bcd2bin(M_msf(msf)) * 60 +
759578Srgrimes		bcd2bin(S_msf(msf))) * 75 +
760578Srgrimes		bcd2bin(F_msf(msf)) - 150;
761578Srgrimes}
762578Srgrimes
7631197Srgrimesstatic int
7641197Srgrimesmcd_volinfo(int unit)
765578Srgrimes{
766578Srgrimes	struct mcd_data *cd = mcd_data + unit;
767578Srgrimes	int i;
768578Srgrimes
769578Srgrimes/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
770578Srgrimes
771578Srgrimes	/* Get the status, in case the disc has been changed */
772578Srgrimes	if (mcd_getstat(unit, 1) < 0) return EIO;
773578Srgrimes
774578Srgrimes	/* Just return if we already have it */
775578Srgrimes	if (cd->flags & MCDVOLINFO) return 0;
776578Srgrimes
777578Srgrimes	/* send volume info command */
778578Srgrimes	if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
779578Srgrimes		return -1;
780578Srgrimes
781578Srgrimes	/* get data */
782578Srgrimes	if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
783578Srgrimes		printf("mcd%d: mcd_volinfo: error read data\n",unit);
784578Srgrimes		return -1;
785578Srgrimes	}
786578Srgrimes
787578Srgrimes	if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
788578Srgrimes		cd->flags |= MCDVOLINFO;	/* volinfo is OK */
789578Srgrimes		return 0;
790578Srgrimes	}
791578Srgrimes
792578Srgrimes	return -1;
793578Srgrimes}
794578Srgrimes
795798Swollmanvoid
796798Swollmanmcdintr(unit)
797798Swollman	int unit;
798578Srgrimes{
799578Srgrimes	int	port = mcd_data[unit].iobase;
800578Srgrimes	u_int	i;
801578Srgrimes
802578Srgrimes	MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0);
803578Srgrimes
804578Srgrimes	/* just read out status and ignore the rest */
805578Srgrimes	if ((inb(port+mcd_xfer)&0xFF) != 0xFF) {
806578Srgrimes		i = inb(port+mcd_status);
807578Srgrimes	}
808578Srgrimes}
809578Srgrimes
810578Srgrimes/* state machine to process read requests
811578Srgrimes * initialize with MCD_S_BEGIN: calculate sizes, and read status
812578Srgrimes * MCD_S_WAITSTAT: wait for status reply, set mode
813578Srgrimes * MCD_S_WAITMODE: waits for status reply from set mode, set read command
814578Srgrimes * MCD_S_WAITREAD: wait for read ready, read data
815578Srgrimes */
816578Srgrimesstatic struct mcd_mbx *mbxsave;
817578Srgrimes
8181197Srgrimesstatic void
8191197Srgrimesmcd_doread(int state, struct mcd_mbx *mbxin)
820578Srgrimes{
821578Srgrimes	struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
822578Srgrimes	int	unit = mbx->unit;
823578Srgrimes	int	port = mbx->port;
824578Srgrimes	struct	buf *bp = mbx->bp;
825578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
826578Srgrimes
827578Srgrimes	int	rm,i,k;
828578Srgrimes	struct mcd_read2 rbuf;
829578Srgrimes	int	blknum;
830578Srgrimes	caddr_t	addr;
831578Srgrimes
832578Srgrimesloop:
833578Srgrimes	switch (state) {
834578Srgrimes	case MCD_S_BEGIN:
835578Srgrimes		mbx = mbxsave = mbxin;
836578Srgrimes
837578Srgrimes	case MCD_S_BEGIN1:
838578Srgrimes		/* get status */
839578Srgrimes		outb(port+mcd_command, MCD_CMDGETSTAT);
840578Srgrimes		mbx->count = RDELAY_WAITSTAT;
8411197Srgrimes		timeout((timeout_func_t)mcd_doread,
8421197Srgrimes			(caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
843578Srgrimes		return;
844578Srgrimes	case MCD_S_WAITSTAT:
8451000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT);
846578Srgrimes		if (mbx->count-- >= 0) {
847578Srgrimes			if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
8481197Srgrimes				timeout((timeout_func_t)mcd_doread,
8491197Srgrimes				    (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
850578Srgrimes				return;
851578Srgrimes			}
852578Srgrimes			mcd_setflags(unit,cd);
8531197Srgrimes			MCD_TRACE("got WAITSTAT delay=%d\n",
8541197Srgrimes				RDELAY_WAITSTAT-mbx->count,0,0,0);
855578Srgrimes			/* reject, if audio active */
856578Srgrimes			if (cd->status & MCDAUDIOBSY) {
857578Srgrimes				printf("mcd%d: audio is active\n",unit);
858578Srgrimes				goto readerr;
859578Srgrimes			}
860578Srgrimes
861578Srgrimes			/* to check for raw/cooked mode */
862578Srgrimes			if (cd->flags & MCDREADRAW) {
863578Srgrimes				rm = MCD_MD_RAW;
864578Srgrimes				mbx->sz = MCDRBLK;
865578Srgrimes			} else {
866578Srgrimes				rm = MCD_MD_COOKED;
867578Srgrimes				mbx->sz = cd->blksize;
868578Srgrimes			}
869578Srgrimes
870578Srgrimes			mbx->count = RDELAY_WAITMODE;
871578Srgrimes
872578Srgrimes			mcd_put(port+mcd_command, MCD_CMDSETMODE);
873578Srgrimes			mcd_put(port+mcd_command, rm);
8741197Srgrimes			timeout((timeout_func_t)mcd_doread,
8751197Srgrimes				(caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */
876578Srgrimes			return;
877578Srgrimes		} else {
878578Srgrimes#ifdef MCD_TO_WARNING_ON
879578Srgrimes			printf("mcd%d: timeout getstatus\n",unit);
880578Srgrimes#endif
881578Srgrimes			goto readerr;
882578Srgrimes		}
883578Srgrimes
884578Srgrimes	case MCD_S_WAITMODE:
8851000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE);
886578Srgrimes		if (mbx->count-- < 0) {
887578Srgrimes#ifdef MCD_TO_WARNING_ON
888578Srgrimes			printf("mcd%d: timeout set mode\n",unit);
889578Srgrimes#endif
890578Srgrimes			goto readerr;
891578Srgrimes		}
892578Srgrimes		if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
893798Swollman			timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100);
894578Srgrimes			return;
895578Srgrimes		}
896578Srgrimes		mcd_setflags(unit,cd);
8971197Srgrimes		MCD_TRACE("got WAITMODE delay=%d\n",
8981197Srgrimes			RDELAY_WAITMODE-mbx->count,0,0,0);
899578Srgrimes		/* for first block */
900578Srgrimes		mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
901578Srgrimes		mbx->skip = 0;
902578Srgrimes
903578Srgrimesnextblock:
904578Srgrimes		blknum 	= (bp->b_blkno / (mbx->sz/DEV_BSIZE))
905578Srgrimes			+ mbx->p_offset + mbx->skip/mbx->sz;
906578Srgrimes
9071197Srgrimes		MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",
9081197Srgrimes			blknum,bp,0,0);
909578Srgrimes
910578Srgrimes		/* build parameter block */
911578Srgrimes		hsg2msf(blknum,rbuf.start_msf);
912578Srgrimes
913578Srgrimes		/* send the read command */
914578Srgrimes		mcd_put(port+mcd_command,MCD_CMDREAD2);
915578Srgrimes		mcd_put(port+mcd_command,rbuf.start_msf[0]);
916578Srgrimes		mcd_put(port+mcd_command,rbuf.start_msf[1]);
917578Srgrimes		mcd_put(port+mcd_command,rbuf.start_msf[2]);
918578Srgrimes		mcd_put(port+mcd_command,0);
919578Srgrimes		mcd_put(port+mcd_command,0);
920578Srgrimes		mcd_put(port+mcd_command,1);
921578Srgrimes		mbx->count = RDELAY_WAITREAD;
9221197Srgrimes		timeout((timeout_func_t)mcd_doread,
9231197Srgrimes			(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
924578Srgrimes		return;
925578Srgrimes	case MCD_S_WAITREAD:
9261000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD);
927578Srgrimes		if (mbx->count-- > 0) {
928578Srgrimes			k = inb(port+mcd_xfer);
929578Srgrimes			if ((k & 2)==0) {
9301197Srgrimes				MCD_TRACE("got data delay=%d\n",
9311197Srgrimes					RDELAY_WAITREAD-mbx->count,0,0,0);
932578Srgrimes				/* data is ready */
933578Srgrimes				addr	= bp->b_un.b_addr + mbx->skip;
934578Srgrimes				outb(port+mcd_ctl2,0x04);	/* XXX */
935578Srgrimes				for (i=0; i<mbx->sz; i++)
936578Srgrimes					*addr++	= inb(port+mcd_rdata);
937578Srgrimes				outb(port+mcd_ctl2,0x0c);	/* XXX */
938578Srgrimes
939578Srgrimes				if (--mbx->nblk > 0) {
940578Srgrimes					mbx->skip += mbx->sz;
941578Srgrimes					goto nextblock;
942578Srgrimes				}
943578Srgrimes
944578Srgrimes				/* return buffer */
945578Srgrimes				bp->b_resid = 0;
946578Srgrimes				biodone(bp);
947578Srgrimes
948578Srgrimes				cd->flags &= ~MCDMBXBSY;
949578Srgrimes				mcd_start(mbx->unit);
950578Srgrimes				return;
951578Srgrimes			}
952578Srgrimes			if ((k & 4)==0)
953578Srgrimes				mcd_getstat(unit,0);
9541197Srgrimes			timeout((timeout_func_t)mcd_doread,
9551197Srgrimes				(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
956578Srgrimes			return;
957578Srgrimes		} else {
958578Srgrimes#ifdef MCD_TO_WARNING_ON
959578Srgrimes			printf("mcd%d: timeout read data\n",unit);
960578Srgrimes#endif
961578Srgrimes			goto readerr;
962578Srgrimes		}
963578Srgrimes	}
964578Srgrimes
965578Srgrimesreaderr:
966578Srgrimes	if (mbx->retry-- > 0) {
967578Srgrimes#ifdef MCD_TO_WARNING_ON
968578Srgrimes		printf("mcd%d: retrying\n",unit);
969578Srgrimes#endif
970578Srgrimes		state = MCD_S_BEGIN1;
971578Srgrimes		goto loop;
972578Srgrimes	}
973578Srgrimes
974578Srgrimes	/* invalidate the buffer */
975578Srgrimes	bp->b_flags |= B_ERROR;
976578Srgrimes	bp->b_resid = bp->b_bcount;
977578Srgrimes	biodone(bp);
978578Srgrimes	mcd_start(mbx->unit);
979578Srgrimes	return;
980578Srgrimes
981578Srgrimes#ifdef NOTDEF
982578Srgrimes	printf("mcd%d: unit timeout, resetting\n",mbx->unit);
983578Srgrimes	outb(mbx->port+mcd_reset,MCD_CMDRESET);
984578Srgrimes	DELAY(300000);
985578Srgrimes	(void)mcd_getstat(mbx->unit,1);
986578Srgrimes	(void)mcd_getstat(mbx->unit,1);
987578Srgrimes	/*cd->status &= ~MCDDSKCHNG; */
988578Srgrimes	cd->debug = 1; /* preventive set debug mode */
989578Srgrimes
990578Srgrimes#endif
991578Srgrimes
992578Srgrimes}
993578Srgrimes
994578Srgrimes#ifndef MCDMINI
9951197Srgrimesstatic int
9961197Srgrimesmcd_setmode(int unit, int mode)
997578Srgrimes{
998578Srgrimes	struct mcd_data *cd = mcd_data + unit;
999578Srgrimes	int port = cd->iobase;
1000578Srgrimes	int retry;
1001578Srgrimes
1002578Srgrimes	printf("mcd%d: setting mode to %d\n", unit, mode);
1003578Srgrimes	for(retry=0; retry<MCD_RETRYS; retry++)
1004578Srgrimes	{
1005578Srgrimes		outb(port+mcd_command, MCD_CMDSETMODE);
1006578Srgrimes		outb(port+mcd_command, mode);
1007578Srgrimes		if (mcd_getstat(unit, 0) != -1) return 0;
1008578Srgrimes	}
1009578Srgrimes
1010578Srgrimes	return -1;
1011578Srgrimes}
1012578Srgrimes
10131197Srgrimesstatic int
10141197Srgrimesmcd_toc_header(int unit, struct ioc_toc_header *th)
1015578Srgrimes{
1016578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1017578Srgrimes
10181197Srgrimes	if (mcd_volinfo(unit) < 0) {
1019578Srgrimes		return ENXIO;
10201197Srgrimes	}
1021578Srgrimes
1022578Srgrimes	th->len = msf2hsg(cd->volinfo.vol_msf);
1023578Srgrimes	th->starting_track = bcd2bin(cd->volinfo.trk_low);
1024578Srgrimes	th->ending_track = bcd2bin(cd->volinfo.trk_high);
1025578Srgrimes
1026578Srgrimes	return 0;
1027578Srgrimes}
1028578Srgrimes
10291197Srgrimesstatic int
10301197Srgrimesmcd_read_toc(int unit)
1031578Srgrimes{
1032578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1033578Srgrimes	struct ioc_toc_header th;
1034578Srgrimes	struct mcd_qchninfo q;
1035578Srgrimes	int rc, trk, idx, retry;
1036578Srgrimes
1037578Srgrimes	/* Only read TOC if needed */
10381197Srgrimes	if (cd->flags & MCDTOC) {
10391197Srgrimes		return 0;
10401197Srgrimes	}
1041578Srgrimes
1042578Srgrimes	printf("mcd%d: reading toc header\n", unit);
10431197Srgrimes	if (mcd_toc_header(unit, &th) != 0) {
1044578Srgrimes		return ENXIO;
10451197Srgrimes	}
1046578Srgrimes
1047578Srgrimes	printf("mcd%d: stopping play\n", unit);
10481197Srgrimes	if ((rc=mcd_stop(unit)) != 0) {
1049578Srgrimes		return rc;
10501197Srgrimes	}
1051578Srgrimes
1052578Srgrimes	/* try setting the mode twice */
10531197Srgrimes	if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
1054578Srgrimes		return EIO;
10551197Srgrimes	}
10561197Srgrimes	if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
1057578Srgrimes		return EIO;
10581197Srgrimes	}
1059578Srgrimes
1060578Srgrimes	printf("mcd%d: get_toc reading qchannel info\n",unit);
1061578Srgrimes	for(trk=th.starting_track; trk<=th.ending_track; trk++)
1062578Srgrimes		cd->toc[trk].idx_no = 0;
1063578Srgrimes	trk = th.ending_track - th.starting_track + 1;
1064578Srgrimes	for(retry=0; retry<300 && trk>0; retry++)
1065578Srgrimes	{
1066578Srgrimes		if (mcd_getqchan(unit, &q) < 0) break;
1067578Srgrimes		idx = bcd2bin(q.idx_no);
10681197Srgrimes		if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0) {
10691197Srgrimes			if (cd->toc[idx].idx_no == 0) {
1070578Srgrimes				cd->toc[idx] = q;
1071578Srgrimes				trk--;
1072578Srgrimes			}
10731197Srgrimes		}
1074578Srgrimes	}
1075578Srgrimes
10761197Srgrimes	if (mcd_setmode(unit, MCD_MD_COOKED) != 0) {
1077578Srgrimes		return EIO;
10781197Srgrimes	}
1079578Srgrimes
10801197Srgrimes	if (trk != 0) {
10811197Srgrimes		return ENXIO;
10821197Srgrimes	}
1083578Srgrimes
1084578Srgrimes	/* add a fake last+1 */
1085578Srgrimes	idx = th.ending_track + 1;
1086578Srgrimes	cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
1087578Srgrimes	cd->toc[idx].trk_no = 0;
1088578Srgrimes	cd->toc[idx].idx_no = 0xAA;
1089578Srgrimes	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
1090578Srgrimes	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
1091578Srgrimes	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
1092578Srgrimes
1093578Srgrimes	cd->flags |= MCDTOC;
1094578Srgrimes
1095578Srgrimes	return 0;
1096578Srgrimes}
1097578Srgrimes
10981197Srgrimesstatic int
10991197Srgrimesmcd_toc_entry(int unit, struct ioc_read_toc_entry *te)
1100578Srgrimes{
1101578Srgrimes	struct mcd_data *cd = mcd_data + unit;
11021197Srgrimes	struct ret_toc {
1103578Srgrimes		struct ioc_toc_header th;
1104578Srgrimes		struct cd_toc_entry rt;
1105578Srgrimes	} ret_toc;
1106578Srgrimes	struct ioc_toc_header th;
1107578Srgrimes	int rc, i;
1108578Srgrimes
1109578Srgrimes	/* Make sure we have a valid toc */
11101197Srgrimes	if ((rc=mcd_read_toc(unit)) != 0) {
1111578Srgrimes		return rc;
11121197Srgrimes	}
1113578Srgrimes
1114578Srgrimes	/* find the toc to copy*/
1115578Srgrimes	i = te->starting_track;
11161197Srgrimes	if (i == MCD_LASTPLUS1) {
1117578Srgrimes		i = bcd2bin(cd->volinfo.trk_high) + 1;
11181197Srgrimes	}
1119578Srgrimes
1120578Srgrimes	/* verify starting track */
1121578Srgrimes	if (i < bcd2bin(cd->volinfo.trk_low) ||
11221197Srgrimes		i > bcd2bin(cd->volinfo.trk_high)+1) {
1123578Srgrimes		return EINVAL;
11241197Srgrimes	}
1125578Srgrimes
1126578Srgrimes	/* do we have room */
1127578Srgrimes	if (te->data_len < sizeof(struct ioc_toc_header) +
11281197Srgrimes		sizeof(struct cd_toc_entry)) {
11291197Srgrimes		return EINVAL;
11301197Srgrimes	}
1131578Srgrimes
1132578Srgrimes	/* Copy the toc header */
11331197Srgrimes	if (mcd_toc_header(unit, &th) < 0) {
11341197Srgrimes		return EIO;
11351197Srgrimes	}
1136578Srgrimes	ret_toc.th = th;
1137578Srgrimes
1138578Srgrimes	/* copy the toc data */
1139578Srgrimes	ret_toc.rt.control = cd->toc[i].ctrl_adr;
1140578Srgrimes	ret_toc.rt.addr_type = te->address_format;
1141578Srgrimes	ret_toc.rt.track = i;
11421197Srgrimes	if (te->address_format == CD_MSF_FORMAT) {
11431097Srgrimes		ret_toc.rt.addr.addr[1] = cd->toc[i].hd_pos_msf[0];
11441097Srgrimes		ret_toc.rt.addr.addr[2] = cd->toc[i].hd_pos_msf[1];
11451097Srgrimes		ret_toc.rt.addr.addr[3] = cd->toc[i].hd_pos_msf[2];
1146578Srgrimes	}
1147578Srgrimes
1148578Srgrimes	/* copy the data back */
1149578Srgrimes	copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
1150578Srgrimes		+ sizeof(struct ioc_toc_header));
1151578Srgrimes
1152578Srgrimes	return 0;
1153578Srgrimes}
1154578Srgrimes
11551197Srgrimesstatic int
11561197Srgrimesmcd_stop(int unit)
1157578Srgrimes{
1158578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1159578Srgrimes
11601197Srgrimes	if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) {
1161578Srgrimes		return ENXIO;
11621197Srgrimes	}
1163578Srgrimes	cd->audio_status = CD_AS_PLAY_COMPLETED;
1164578Srgrimes	return 0;
1165578Srgrimes}
1166578Srgrimes
11671197Srgrimesstatic int
11681197Srgrimesmcd_getqchan(int unit, struct mcd_qchninfo *q)
1169578Srgrimes{
1170578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1171578Srgrimes
11721197Srgrimes	if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) {
1173578Srgrimes		return -1;
11741197Srgrimes	}
11751197Srgrimes	if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) {
1176578Srgrimes		return -1;
11771197Srgrimes	}
11781197Srgrimes	if (cd->debug) {
11791197Srgrimes		printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
1180578Srgrimes		unit,
1181578Srgrimes		q->ctrl_adr, q->trk_no, q->idx_no,
1182578Srgrimes		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
1183578Srgrimes		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
11841197Srgrimes	}
1185578Srgrimes	return 0;
1186578Srgrimes}
1187578Srgrimes
11881197Srgrimesstatic int
11891197Srgrimesmcd_subchan(int unit, struct ioc_read_subchannel *sc)
1190578Srgrimes{
1191578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1192578Srgrimes	struct mcd_qchninfo q;
1193578Srgrimes	struct cd_sub_channel_info data;
1194578Srgrimes
1195578Srgrimes	printf("mcd%d: subchan af=%d, df=%d\n", unit,
1196578Srgrimes		sc->address_format,
1197578Srgrimes		sc->data_format);
11981197Srgrimes	if (sc->address_format != CD_MSF_FORMAT) {
11991197Srgrimes		return EIO;
12001197Srgrimes	}
12011197Srgrimes	if (sc->data_format != CD_CURRENT_POSITION) {
12021197Srgrimes		return EIO;
12031197Srgrimes	}
12041197Srgrimes	if (mcd_getqchan(unit, &q) < 0) {
12051197Srgrimes		return EIO;
12061197Srgrimes	}
1207578Srgrimes
1208578Srgrimes	data.header.audio_status = cd->audio_status;
1209578Srgrimes	data.what.position.data_format = CD_MSF_FORMAT;
1210578Srgrimes	data.what.position.track_number = bcd2bin(q.trk_no);
1211578Srgrimes
12121197Srgrimes	if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0) {
1213578Srgrimes		return EFAULT;
12141197Srgrimes	}
1215578Srgrimes	return 0;
1216578Srgrimes}
1217578Srgrimes
12181197Srgrimesstatic int
12191197Srgrimesmcd_playtracks(int unit, struct ioc_play_track *pt)
1220578Srgrimes{
1221578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1222578Srgrimes	struct mcd_read2 pb;
1223578Srgrimes	int a = pt->start_track;
1224578Srgrimes	int z = pt->end_track;
1225578Srgrimes	int rc;
1226578Srgrimes
12271197Srgrimes	if ((rc = mcd_read_toc(unit)) != 0) {
12281197Srgrimes		return rc;
12291197Srgrimes	}
1230578Srgrimes	printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
1231578Srgrimes		a, pt->start_index, z, pt->end_index);
1232578Srgrimes
1233578Srgrimes	if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
12341197Srgrimes	    z < cd->volinfo.trk_low || z > cd->volinfo.trk_high) {
1235578Srgrimes		return EINVAL;
12361197Srgrimes	}
1237578Srgrimes
1238578Srgrimes	pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
1239578Srgrimes	pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
1240578Srgrimes	pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
1241578Srgrimes	pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
1242578Srgrimes	pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
1243578Srgrimes	pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
1244578Srgrimes
1245578Srgrimes	return mcd_play(unit, &pb);
1246578Srgrimes}
1247578Srgrimes
12481197Srgrimesstatic int
12491197Srgrimesmcd_play(int unit, struct mcd_read2 *pb)
1250578Srgrimes{
1251578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1252578Srgrimes	int port = cd->iobase;
1253578Srgrimes	int retry, st;
1254578Srgrimes
1255578Srgrimes	cd->lastpb = *pb;
12561197Srgrimes	for(retry=0; retry<MCD_RETRYS; retry++) {
1257578Srgrimes		outb(port+mcd_command, MCD_CMDREAD2);
1258578Srgrimes		outb(port+mcd_command, pb->start_msf[0]);
1259578Srgrimes		outb(port+mcd_command, pb->start_msf[1]);
1260578Srgrimes		outb(port+mcd_command, pb->start_msf[2]);
1261578Srgrimes		outb(port+mcd_command, pb->end_msf[0]);
1262578Srgrimes		outb(port+mcd_command, pb->end_msf[1]);
1263578Srgrimes		outb(port+mcd_command, pb->end_msf[2]);
12641197Srgrimes		if ((st=mcd_getstat(unit, 0)) != -1) {
12651197Srgrimes			break;
12661197Srgrimes		}
1267578Srgrimes	}
1268578Srgrimes
12691197Srgrimes	if (cd->debug) {
12701197Srgrimes		printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st);
12711197Srgrimes	}
12721197Srgrimes	if (st == -1) {
12731197Srgrimes		return ENXIO;
12741197Srgrimes	}
1275578Srgrimes	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
1276578Srgrimes	return 0;
1277578Srgrimes}
1278578Srgrimes
12791197Srgrimesstatic int
12801197Srgrimesmcd_pause(int unit)
1281578Srgrimes{
1282578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1283578Srgrimes	struct mcd_qchninfo q;
1284578Srgrimes	int rc;
1285578Srgrimes
1286578Srgrimes	/* Verify current status */
12871197Srgrimes	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) {
1288578Srgrimes		printf("mcd%d: pause attempted when not playing\n", unit);
1289578Srgrimes		return EINVAL;
1290578Srgrimes	}
1291578Srgrimes
1292578Srgrimes	/* Get the current position */
12931197Srgrimes	if (mcd_getqchan(unit, &q) < 0) {
12941197Srgrimes		return EIO;
12951197Srgrimes	}
1296578Srgrimes
1297578Srgrimes	/* Copy it into lastpb */
1298578Srgrimes	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
1299578Srgrimes	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
1300578Srgrimes	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
1301578Srgrimes
1302578Srgrimes	/* Stop playing */
13031197Srgrimes	if ((rc=mcd_stop(unit)) != 0) {
13041197Srgrimes		return rc;
13051197Srgrimes	}
1306578Srgrimes
1307578Srgrimes	/* Set the proper status and exit */
1308578Srgrimes	cd->audio_status = CD_AS_PLAY_PAUSED;
1309578Srgrimes	return 0;
1310578Srgrimes}
1311578Srgrimes
13121197Srgrimesstatic int
13131197Srgrimesmcd_resume(int unit)
1314578Srgrimes{
1315578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1316578Srgrimes
13171197Srgrimes	if (cd->audio_status != CD_AS_PLAY_PAUSED) {
13181197Srgrimes		return EINVAL;
13191197Srgrimes	}
1320578Srgrimes	return mcd_play(unit, &cd->lastpb);
1321578Srgrimes}
1322578Srgrimes#endif /*!MCDMINI*/
1323578Srgrimes
1324578Srgrimes#endif /* NMCD > 0 */
1325