mcd.c revision 1437
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 *
421437Sgclarkii *	$Id: mcd.c,v 1.15 1994/04/20 07:06:41 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)
821294Sats#define RAW_PART	0
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
1021241Sjkh/* These are apparently the different states a mitsumi can get up to */
1031241Sjkh#define MCDCDABSENT	0x0030
1041241Sjkh#define MCDCDPRESENT	0x0020
1051241Sjkh#define MCDSCLOSED	0x0080
1061241Sjkh#define MCDSOPEN	0x00a0
1071237Sjkh
108578Srgrimes/* toc */
109578Srgrimes#define MCD_MAXTOCS	104	/* from the Linux driver */
110578Srgrimes#define MCD_LASTPLUS1	170	/* special toc entry */
111578Srgrimes
112578Srgrimesstruct mcd_mbx {
113578Srgrimes	short		unit;
114578Srgrimes	short		port;
115578Srgrimes	short		retry;
116578Srgrimes	short		nblk;
117578Srgrimes	int		sz;
118578Srgrimes	u_long		skip;
119578Srgrimes	struct buf	*bp;
120578Srgrimes	int		p_offset;
121578Srgrimes	short		count;
122578Srgrimes};
123578Srgrimes
124578Srgrimesstruct mcd_data {
125578Srgrimes	short	config;
126578Srgrimes	short	flags;
127578Srgrimes	short	status;
128578Srgrimes	int	blksize;
129578Srgrimes	u_long	disksize;
130578Srgrimes	int	iobase;
131578Srgrimes	struct disklabel dlabel;
132578Srgrimes	int	partflags[MAXPARTITIONS];
133578Srgrimes	int	openflags;
134578Srgrimes	struct mcd_volinfo volinfo;
135578Srgrimes#ifndef MCDMINI
136578Srgrimes	struct mcd_qchninfo toc[MCD_MAXTOCS];
137578Srgrimes	short	audio_status;
138578Srgrimes	struct mcd_read2 lastpb;
139578Srgrimes#endif
140578Srgrimes	short	debug;
141578Srgrimes	struct buf head;		/* head of buf queue */
142578Srgrimes	struct mcd_mbx mbx;
143578Srgrimes} mcd_data[NMCD];
144578Srgrimes
145578Srgrimes/* reader state machine */
146578Srgrimes#define MCD_S_BEGIN	0
147578Srgrimes#define MCD_S_BEGIN1	1
148578Srgrimes#define MCD_S_WAITSTAT	2
149578Srgrimes#define MCD_S_WAITMODE	3
150578Srgrimes#define MCD_S_WAITREAD	4
151578Srgrimes
152578Srgrimes/* prototypes */
153578Srgrimesint	mcdopen(dev_t dev);
154578Srgrimesint	mcdclose(dev_t dev);
155798Swollmanvoid	mcdstrategy(struct buf *bp);
156578Srgrimesint	mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
157578Srgrimesint	mcdsize(dev_t dev);
158578Srgrimesstatic	void	mcd_done(struct mcd_mbx *mbx);
159578Srgrimesstatic	void	mcd_start(int unit);
160578Srgrimesstatic	int	mcd_getdisklabel(int unit);
161578Srgrimesstatic	void	mcd_configure(struct mcd_data *cd);
162578Srgrimesstatic	int	mcd_get(int unit, char *buf, int nmax);
163578Srgrimesstatic	void	mcd_setflags(int unit,struct mcd_data *cd);
164578Srgrimesstatic	int	mcd_getstat(int unit,int sflg);
165578Srgrimesstatic	int	mcd_send(int unit, int cmd,int nretrys);
166578Srgrimesstatic	int	bcd2bin(bcd_t b);
167578Srgrimesstatic	bcd_t	bin2bcd(int b);
168578Srgrimesstatic	void	hsg2msf(int hsg, bcd_t *msf);
169578Srgrimesstatic	int	msf2hsg(bcd_t *msf);
170578Srgrimesstatic	int	mcd_volinfo(int unit);
171578Srgrimesstatic	int	mcd_waitrdy(int port,int dly);
172978Sjkhstatic 	void	mcd_doread(int state, struct mcd_mbx *mbxin);
173578Srgrimes#ifndef MCDMINI
174578Srgrimesstatic	int 	mcd_setmode(int unit, int mode);
175578Srgrimesstatic	int	mcd_getqchan(int unit, struct mcd_qchninfo *q);
176578Srgrimesstatic	int	mcd_subchan(int unit, struct ioc_read_subchannel *sc);
177578Srgrimesstatic	int	mcd_toc_header(int unit, struct ioc_toc_header *th);
178578Srgrimesstatic	int	mcd_read_toc(int unit);
179578Srgrimesstatic	int	mcd_toc_entry(int unit, struct ioc_read_toc_entry *te);
180578Srgrimesstatic	int	mcd_stop(int unit);
181578Srgrimesstatic	int	mcd_playtracks(int unit, struct ioc_play_track *pt);
182578Srgrimesstatic	int	mcd_play(int unit, struct mcd_read2 *pb);
183578Srgrimesstatic	int	mcd_pause(int unit);
184578Srgrimesstatic	int	mcd_resume(int unit);
185578Srgrimes#endif
186578Srgrimes
187578Srgrimesextern	int	hz;
188578Srgrimesextern	int	mcd_probe(struct isa_device *dev);
189578Srgrimesextern	int	mcd_attach(struct isa_device *dev);
190578Srgrimesstruct	isa_driver	mcddriver = { mcd_probe, mcd_attach, "mcd" };
191578Srgrimes
192578Srgrimes#define mcd_put(port,byte)	outb(port,byte)
193578Srgrimes
194578Srgrimes#define MCD_RETRYS	5
195578Srgrimes#define MCD_RDRETRYS	8
196578Srgrimes
197578Srgrimes#define MCDBLK	2048	/* for cooked mode */
198578Srgrimes#define MCDRBLK	2352	/* for raw mode */
199578Srgrimes
200578Srgrimes/* several delays */
201578Srgrimes#define RDELAY_WAITSTAT	300
202578Srgrimes#define RDELAY_WAITMODE	300
203578Srgrimes#define RDELAY_WAITREAD	800
204578Srgrimes
205578Srgrimes#define DELAY_STATUS	10000l		/* 10000 * 1us */
206578Srgrimes#define DELAY_GETREPLY	200000l		/* 200000 * 2us */
207578Srgrimes#define DELAY_SEEKREAD	20000l		/* 20000 * 1us */
208578Srgrimes#define mcd_delay	DELAY
209578Srgrimes
210578Srgrimesint mcd_attach(struct isa_device *dev)
211578Srgrimes{
212578Srgrimes	struct mcd_data *cd = mcd_data + dev->id_unit;
213578Srgrimes	int i;
214578Srgrimes
215578Srgrimes	cd->iobase = dev->id_iobase;
216578Srgrimes	cd->flags |= MCDINIT;
217578Srgrimes	cd->openflags = 0;
218578Srgrimes	for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
219578Srgrimes
220578Srgrimes#ifdef NOTYET
221578Srgrimes	/* wire controller for interrupts and dma */
222578Srgrimes	mcd_configure(cd);
223578Srgrimes#endif
224578Srgrimes
225578Srgrimes	return 1;
226578Srgrimes}
227578Srgrimes
228578Srgrimesint mcdopen(dev_t dev)
229578Srgrimes{
230578Srgrimes	int unit,part,phys;
231578Srgrimes	struct mcd_data *cd;
232578Srgrimes
233578Srgrimes	unit = mcd_unit(dev);
234578Srgrimes	if (unit >= NMCD)
235578Srgrimes		return ENXIO;
236578Srgrimes
237578Srgrimes	cd = mcd_data + unit;
238578Srgrimes	part = mcd_part(dev);
239578Srgrimes	phys = mcd_phys(dev);
240578Srgrimes
241578Srgrimes	/* not initialized*/
242578Srgrimes	if (!(cd->flags & MCDINIT))
243578Srgrimes		return ENXIO;
244578Srgrimes
245578Srgrimes	/* invalidated in the meantime? mark all open part's invalid */
246578Srgrimes	if (!(cd->flags & MCDVALID) && cd->openflags)
247578Srgrimes		return ENXIO;
248578Srgrimes
249578Srgrimes	if (mcd_getstat(unit,1) < 0)
250578Srgrimes		return ENXIO;
251578Srgrimes
252578Srgrimes	/* XXX get a default disklabel */
253578Srgrimes	mcd_getdisklabel(unit);
254578Srgrimes
255578Srgrimes	if (mcdsize(dev) < 0) {
256578Srgrimes		printf("mcd%d: failed to get disk size\n",unit);
257578Srgrimes		return ENXIO;
258578Srgrimes	} else
259578Srgrimes		cd->flags |= MCDVALID;
260578Srgrimes
261578SrgrimesMCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
262578Srgrimes	part,cd->disksize,cd->blksize,0);
263578Srgrimes
264578Srgrimes	if (part == RAW_PART ||
265578Srgrimes		(part < cd->dlabel.d_npartitions &&
266578Srgrimes		cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
267578Srgrimes		cd->partflags[part] |= MCDOPEN;
268578Srgrimes		cd->openflags |= (1<<part);
269578Srgrimes		if (part == RAW_PART && phys != 0)
270578Srgrimes			cd->partflags[part] |= MCDREADRAW;
271578Srgrimes		return 0;
272578Srgrimes	}
273578Srgrimes
274578Srgrimes	return ENXIO;
275578Srgrimes}
276578Srgrimes
277578Srgrimesint mcdclose(dev_t dev)
278578Srgrimes{
279578Srgrimes	int unit,part,phys;
280578Srgrimes	struct mcd_data *cd;
281578Srgrimes
282578Srgrimes	unit = mcd_unit(dev);
283578Srgrimes	if (unit >= NMCD)
284578Srgrimes		return ENXIO;
285578Srgrimes
286578Srgrimes	cd = mcd_data + unit;
287578Srgrimes	part = mcd_part(dev);
288578Srgrimes	phys = mcd_phys(dev);
289578Srgrimes
290578Srgrimes	if (!(cd->flags & MCDINIT))
291578Srgrimes		return ENXIO;
292578Srgrimes
293578Srgrimes	mcd_getstat(unit,1);	/* get status */
294578Srgrimes
295578Srgrimes	/* close channel */
296578Srgrimes	cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
297578Srgrimes	cd->openflags &= ~(1<<part);
298578Srgrimes	MCD_TRACE("close: partition=%d\n",part,0,0,0);
299578Srgrimes
300578Srgrimes	return 0;
301578Srgrimes}
302578Srgrimes
303798Swollmanvoid
304798Swollmanmcdstrategy(struct buf *bp)
305578Srgrimes{
306578Srgrimes	struct mcd_data *cd;
307578Srgrimes	struct buf *qp;
308578Srgrimes	int s;
309578Srgrimes
310578Srgrimes	int unit = mcd_unit(bp->b_dev);
311578Srgrimes
312578Srgrimes	cd = mcd_data + unit;
313578Srgrimes
314578Srgrimes	/* test validity */
315578Srgrimes/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
316578Srgrimes	bp,unit,bp->b_blkno,bp->b_bcount);*/
317578Srgrimes	if (unit >= NMCD || bp->b_blkno < 0) {
318578Srgrimes		printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
319578Srgrimes			unit, bp->b_blkno, bp->b_bcount);
320578Srgrimes		pg("mcd: mcdstratregy failure");
321578Srgrimes		bp->b_error = EINVAL;
322578Srgrimes		bp->b_flags |= B_ERROR;
323578Srgrimes		goto bad;
324578Srgrimes	}
325578Srgrimes
326578Srgrimes	/* if device invalidated (e.g. media change, door open), error */
327578Srgrimes	if (!(cd->flags & MCDVALID)) {
328578SrgrimesMCD_TRACE("strategy: drive not valid\n",0,0,0,0);
329578Srgrimes		bp->b_error = EIO;
330578Srgrimes		goto bad;
331578Srgrimes	}
332578Srgrimes
333578Srgrimes	/* read only */
334578Srgrimes	if (!(bp->b_flags & B_READ)) {
335578Srgrimes		bp->b_error = EROFS;
336578Srgrimes		goto bad;
337578Srgrimes	}
338578Srgrimes
339578Srgrimes	/* no data to read */
340578Srgrimes	if (bp->b_bcount == 0)
341578Srgrimes		goto done;
342578Srgrimes
343578Srgrimes	/* for non raw access, check partition limits */
344578Srgrimes	if (mcd_part(bp->b_dev) != RAW_PART) {
345578Srgrimes		if (!(cd->flags & MCDLABEL)) {
346578Srgrimes			bp->b_error = EIO;
347578Srgrimes			goto bad;
348578Srgrimes		}
349578Srgrimes		/* adjust transfer if necessary */
350578Srgrimes		if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
351578Srgrimes			goto done;
352578Srgrimes		}
3531379Sdg	} else {
3541379Sdg		bp->b_pblkno = bp->b_blkno;
3551437Sgclarkii		bp->b_resid = 0;
356578Srgrimes	}
357578Srgrimes
358578Srgrimes	/* queue it */
359578Srgrimes	qp = &cd->head;
360578Srgrimes	s = splbio();
361578Srgrimes	disksort(qp,bp);
362578Srgrimes	splx(s);
363578Srgrimes
364578Srgrimes	/* now check whether we can perform processing */
365578Srgrimes	mcd_start(unit);
366578Srgrimes	return;
367578Srgrimes
368578Srgrimesbad:
369578Srgrimes	bp->b_flags |= B_ERROR;
370578Srgrimesdone:
371578Srgrimes	bp->b_resid = bp->b_bcount;
372578Srgrimes	biodone(bp);
373578Srgrimes	return;
374578Srgrimes}
375578Srgrimes
376578Srgrimesstatic void mcd_start(int unit)
377578Srgrimes{
378578Srgrimes	struct mcd_data *cd = mcd_data + unit;
379578Srgrimes	struct buf *bp, *qp = &cd->head;
380578Srgrimes	struct partition *p;
381578Srgrimes	int part;
382578Srgrimes	register s = splbio();
383578Srgrimes
384578Srgrimes	if (cd->flags & MCDMBXBSY)
385578Srgrimes		return;
386578Srgrimes
387578Srgrimes	if ((bp = qp->b_actf) != 0) {
388578Srgrimes		/* block found to process, dequeue */
389578Srgrimes		/*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
390578Srgrimes		qp->b_actf = bp->av_forw;
391578Srgrimes		splx(s);
392578Srgrimes	} else {
393578Srgrimes		/* nothing to do */
394578Srgrimes		splx(s);
395578Srgrimes		return;
396578Srgrimes	}
397578Srgrimes
398578Srgrimes	/* changed media? */
399578Srgrimes	if (!(cd->flags	& MCDVALID)) {
400578Srgrimes		MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
401578Srgrimes		return;
402578Srgrimes	}
403578Srgrimes
404578Srgrimes	p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
405578Srgrimes
406578Srgrimes	cd->flags |= MCDMBXBSY;
407578Srgrimes	cd->mbx.unit = unit;
408578Srgrimes	cd->mbx.port = cd->iobase;
409578Srgrimes	cd->mbx.retry = MCD_RETRYS;
410578Srgrimes	cd->mbx.bp = bp;
411578Srgrimes	cd->mbx.p_offset = p->p_offset;
412578Srgrimes
413578Srgrimes	/* calling the read routine */
414978Sjkh	mcd_doread(MCD_S_BEGIN,&(cd->mbx));
415578Srgrimes	/* triggers mcd_start, when successful finished */
416578Srgrimes	return;
417578Srgrimes}
418578Srgrimes
419578Srgrimesint mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
420578Srgrimes{
421578Srgrimes	struct mcd_data *cd;
422578Srgrimes	int unit,part;
423578Srgrimes
424578Srgrimes	unit = mcd_unit(dev);
425578Srgrimes	part = mcd_part(dev);
426578Srgrimes	cd = mcd_data + unit;
427578Srgrimes
428578Srgrimes#ifdef MCDMINI
429578Srgrimes	return ENOTTY;
430578Srgrimes#else
431578Srgrimes	if (!(cd->flags & MCDVALID))
432578Srgrimes		return EIO;
433578SrgrimesMCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
434578Srgrimes
435578Srgrimes	switch (cmd) {
436578Srgrimes	case DIOCSBAD:
437578Srgrimes		return EINVAL;
438578Srgrimes	case DIOCGDINFO:
439578Srgrimes	case DIOCGPART:
440578Srgrimes	case DIOCWDINFO:
441578Srgrimes	case DIOCSDINFO:
442578Srgrimes	case DIOCWLABEL:
443578Srgrimes		return ENOTTY;
444578Srgrimes	case CDIOCPLAYTRACKS:
445578Srgrimes		return mcd_playtracks(unit, (struct ioc_play_track *) addr);
446578Srgrimes	case CDIOCPLAYBLOCKS:
447578Srgrimes		return mcd_play(unit, (struct mcd_read2 *) addr);
448578Srgrimes	case CDIOCREADSUBCHANNEL:
449578Srgrimes		return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
450578Srgrimes	case CDIOREADTOCHEADER:
451578Srgrimes		return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
452578Srgrimes	case CDIOREADTOCENTRYS:
453578Srgrimes		return mcd_toc_entry(unit, (struct ioc_read_toc_entry *) addr);
454578Srgrimes	case CDIOCSETPATCH:
455578Srgrimes	case CDIOCGETVOL:
456578Srgrimes	case CDIOCSETVOL:
457578Srgrimes	case CDIOCSETMONO:
458578Srgrimes	case CDIOCSETSTERIO:
459578Srgrimes	case CDIOCSETMUTE:
460578Srgrimes	case CDIOCSETLEFT:
461578Srgrimes	case CDIOCSETRIGHT:
462578Srgrimes		return EINVAL;
463578Srgrimes	case CDIOCRESUME:
464578Srgrimes		return mcd_resume(unit);
465578Srgrimes	case CDIOCPAUSE:
466578Srgrimes		return mcd_pause(unit);
467578Srgrimes	case CDIOCSTART:
468578Srgrimes		return EINVAL;
469578Srgrimes	case CDIOCSTOP:
470578Srgrimes		return mcd_stop(unit);
471578Srgrimes	case CDIOCEJECT:
472578Srgrimes		return EINVAL;
473578Srgrimes	case CDIOCSETDEBUG:
474578Srgrimes		cd->debug = 1;
475578Srgrimes		return 0;
476578Srgrimes	case CDIOCCLRDEBUG:
477578Srgrimes		cd->debug = 0;
478578Srgrimes		return 0;
479578Srgrimes	case CDIOCRESET:
480578Srgrimes		return EINVAL;
481578Srgrimes	default:
482578Srgrimes		return ENOTTY;
483578Srgrimes	}
484578Srgrimes	/*NOTREACHED*/
485578Srgrimes#endif /*!MCDMINI*/
486578Srgrimes}
487578Srgrimes
488578Srgrimes/* this could have been taken from scsi/cd.c, but it is not clear
489578Srgrimes * whether the scsi cd driver is linked in
490578Srgrimes */
491578Srgrimesstatic int mcd_getdisklabel(int unit)
492578Srgrimes{
493578Srgrimes	struct mcd_data *cd = mcd_data + unit;
494578Srgrimes
495578Srgrimes	if (cd->flags & MCDLABEL)
496578Srgrimes		return -1;
497578Srgrimes
498578Srgrimes	bzero(&cd->dlabel,sizeof(struct disklabel));
499578Srgrimes	strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16);
500578Srgrimes	strncpy(cd->dlabel.d_packname,"unknown        ",16);
501578Srgrimes	cd->dlabel.d_secsize 	= cd->blksize;
502578Srgrimes	cd->dlabel.d_nsectors	= 100;
503578Srgrimes	cd->dlabel.d_ntracks	= 1;
504578Srgrimes	cd->dlabel.d_ncylinders	= (cd->disksize/100)+1;
505578Srgrimes	cd->dlabel.d_secpercyl	= 100;
506578Srgrimes	cd->dlabel.d_secperunit	= cd->disksize;
507578Srgrimes	cd->dlabel.d_rpm	= 300;
508578Srgrimes	cd->dlabel.d_interleave	= 1;
509578Srgrimes	cd->dlabel.d_flags	= D_REMOVABLE;
510578Srgrimes	cd->dlabel.d_npartitions= 1;
511578Srgrimes	cd->dlabel.d_partitions[0].p_offset = 0;
512578Srgrimes	cd->dlabel.d_partitions[0].p_size = cd->disksize;
513578Srgrimes	cd->dlabel.d_partitions[0].p_fstype = 9;
514578Srgrimes	cd->dlabel.d_magic	= DISKMAGIC;
515578Srgrimes	cd->dlabel.d_magic2	= DISKMAGIC;
516578Srgrimes	cd->dlabel.d_checksum	= dkcksum(&cd->dlabel);
517578Srgrimes
518578Srgrimes	cd->flags |= MCDLABEL;
519578Srgrimes	return 0;
520578Srgrimes}
521578Srgrimes
522578Srgrimesint mcdsize(dev_t dev)
523578Srgrimes{
524578Srgrimes	int size;
525578Srgrimes	int unit = mcd_unit(dev);
526578Srgrimes	struct mcd_data *cd = mcd_data + unit;
527578Srgrimes
528578Srgrimes	if (mcd_volinfo(unit) >= 0) {
529578Srgrimes		cd->blksize = MCDBLK;
530578Srgrimes		size = msf2hsg(cd->volinfo.vol_msf);
531578Srgrimes		cd->disksize = size * (MCDBLK/DEV_BSIZE);
532578Srgrimes		return 0;
533578Srgrimes	}
534578Srgrimes	return -1;
535578Srgrimes}
536578Srgrimes
537578Srgrimes/***************************************************************
538578Srgrimes * lower level of driver starts here
539578Srgrimes **************************************************************/
540578Srgrimes
541578Srgrimes#ifdef NOTDEF
5421197Srgrimesstatic char
5431197Srgrimesirqs[] = {
544578Srgrimes	0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
545578Srgrimes	0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
546578Srgrimes};
547578Srgrimes
5481197Srgrimesstatic char
5491197Srgrimesdrqs[] = {
550578Srgrimes	0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
551578Srgrimes};
552578Srgrimes#endif
553578Srgrimes
5541197Srgrimesstatic void
5551197Srgrimesmcd_configure(struct mcd_data *cd)
556578Srgrimes{
557578Srgrimes	outb(cd->iobase+mcd_config,cd->config);
558578Srgrimes}
559578Srgrimes
5601197Srgrimes/* Wait for non-busy - return 0 on timeout */
5611197Srgrimesstatic int
5621197Srgrimestwiddle_thumbs(int port, int unit, int count, char *whine)
5631197Srgrimes{
5641197Srgrimes	int i;
5651197Srgrimes
5661197Srgrimes	for (i = 0; i < count; i++) {
5671197Srgrimes		if (!(inb(port+MCD_FLAGS) & MCD_ST_BUSY)) {
5681197Srgrimes			return 1;
5691197Srgrimes		}
5701197Srgrimes	}
5711197Srgrimes#ifdef MCD_TO_WARNING_ON
5721197Srgrimes	printf("mcd%d: timeout %s\n", unit, whine);
5731197Srgrimes#endif
5741197Srgrimes	return 0;
5751197Srgrimes}
5761197Srgrimes
577978Sjkh/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
578578Srgrimes
5791197Srgrimesint
5801197Srgrimesmcd_probe(struct isa_device *dev)
581578Srgrimes{
582578Srgrimes	int port = dev->id_iobase;
583578Srgrimes	int unit = dev->id_unit;
5841197Srgrimes	int i, j;
5851197Srgrimes	int status;
5861197Srgrimes	unsigned char stbytes[3];
587578Srgrimes
588578Srgrimes	mcd_data[unit].flags = MCDPROBING;
589578Srgrimes
590578Srgrimes#ifdef NOTDEF
591578Srgrimes	/* get irq/drq configuration word */
592578Srgrimes	mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
593578Srgrimes#else
594578Srgrimes	mcd_data[unit].config = 0;
595578Srgrimes#endif
596578Srgrimes
597578Srgrimes	/* send a reset */
5981197Srgrimes	outb(port+MCD_FLAGS, M_RESET);
599578Srgrimes
6001197Srgrimes	/*
6011197Srgrimes	 * delay awhile by getting any pending garbage (old data) and
6021197Srgrimes	 * throwing it away.
6031197Srgrimes	 */
6041197Srgrimes	for (i = 1000000; i != 0; i--) {
6051197Srgrimes		inb(port+MCD_FLAGS);
6061197Srgrimes	}
607578Srgrimes
6081197Srgrimes	/* Get status */
6091197Srgrimes	outb(port+MCD_DATA, MCD_CMDGETSTAT);
6101197Srgrimes	if (!twiddle_thumbs(port, unit, 1000000, "getting status")) {
6111197Srgrimes		return 0;	/* Timeout */
6121197Srgrimes	}
6131197Srgrimes	status = inb(port+MCD_DATA);
6141241Sjkh	if (status != MCDCDABSENT && status != MCDCDPRESENT &&
6151241Sjkh		status != MCDSOPEN && status != MCDSCLOSED)
6161237Sjkh		return 0;	/* Not actually a Mitsumi drive here */
6171197Srgrimes	/* Get version information */
6181197Srgrimes	outb(port+MCD_DATA, MCD_CMDCONTINFO);
6191197Srgrimes	for (j = 0; j < 3; j++) {
6201197Srgrimes		if (!twiddle_thumbs(port, unit, 3000, "getting version info")) {
6211197Srgrimes			return 0;
6221197Srgrimes		}
6231197Srgrimes		stbytes[j] = (inb(port+MCD_DATA) & 0xFF);
6241197Srgrimes	}
6251197Srgrimes	printf("mcd%d: version information is %x %c %x\n", unit,
6261197Srgrimes		stbytes[0], stbytes[1], stbytes[2]);
6271197Srgrimes	if (stbytes[1] >= 4) {
6281197Srgrimes		outb(port+MCD_CTRL, M_PICKLE);
6291197Srgrimes		printf("mcd%d: Adjusted for newer drive model\n", unit);
6301197Srgrimes	}
6311197Srgrimes	return 4;
632578Srgrimes}
633578Srgrimes
634978Sjkh
6351197Srgrimesstatic int
6361197Srgrimesmcd_waitrdy(int port,int dly)
637578Srgrimes{
638578Srgrimes	int i;
639578Srgrimes
640578Srgrimes	/* wait until xfer port senses data ready */
641578Srgrimes	for (i=0; i<dly; i++) {
642578Srgrimes		if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0)
643578Srgrimes			return 0;
644578Srgrimes		mcd_delay(1);
645578Srgrimes	}
646578Srgrimes	return -1;
647578Srgrimes}
648578Srgrimes
6491197Srgrimesstatic int
6501197Srgrimesmcd_getreply(int unit,int dly)
651578Srgrimes{
652578Srgrimes	int	i;
653578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
654578Srgrimes	int	port = cd->iobase;
655578Srgrimes
656578Srgrimes	/* wait data to become ready */
657578Srgrimes	if (mcd_waitrdy(port,dly)<0) {
658578Srgrimes#ifdef MCD_TO_WARNING_ON
659578Srgrimes		printf("mcd%d: timeout getreply\n",unit);
660578Srgrimes#endif
661578Srgrimes		return -1;
662578Srgrimes	}
663578Srgrimes
664578Srgrimes	/* get the data */
665578Srgrimes	return inb(port+mcd_status) & 0xFF;
666578Srgrimes}
667578Srgrimes
6681197Srgrimesstatic int
6691197Srgrimesmcd_getstat(int unit,int sflg)
670578Srgrimes{
671578Srgrimes	int	i;
672578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
673578Srgrimes	int	port = cd->iobase;
674578Srgrimes
675578Srgrimes	/* get the status */
676578Srgrimes	if (sflg)
677578Srgrimes		outb(port+mcd_command, MCD_CMDGETSTAT);
678578Srgrimes	i = mcd_getreply(unit,DELAY_GETREPLY);
679578Srgrimes	if (i<0) return -1;
680578Srgrimes
681578Srgrimes	cd->status = i;
682578Srgrimes
683578Srgrimes	mcd_setflags(unit,cd);
684578Srgrimes	return cd->status;
685578Srgrimes}
686578Srgrimes
6871197Srgrimesstatic void
6881197Srgrimesmcd_setflags(int unit, struct mcd_data *cd)
689578Srgrimes{
690578Srgrimes	/* check flags */
691578Srgrimes	if (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) {
692578Srgrimes		MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n",0,0,0,0);
693578Srgrimes		cd->flags &= ~MCDVALID;
694578Srgrimes	}
695578Srgrimes
696578Srgrimes#ifndef MCDMINI
697578Srgrimes	if (cd->status & MCDAUDIOBSY)
698578Srgrimes		cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
699578Srgrimes	else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
700578Srgrimes		cd->audio_status = CD_AS_PLAY_COMPLETED;
701578Srgrimes#endif
702578Srgrimes}
703578Srgrimes
7041197Srgrimesstatic int
7051197Srgrimesmcd_get(int unit, char *buf, int nmax)
706578Srgrimes{
707578Srgrimes	int port = mcd_data[unit].iobase;
708578Srgrimes	int i,k;
709578Srgrimes
710578Srgrimes	for (i=0; i<nmax; i++) {
711578Srgrimes		/* wait for data */
712578Srgrimes		if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
713578Srgrimes#ifdef MCD_TO_WARNING_ON
714578Srgrimes			printf("mcd%d: timeout mcd_get\n",unit);
715578Srgrimes#endif
716578Srgrimes			return -1;
717578Srgrimes		}
718578Srgrimes		buf[i] = k;
719578Srgrimes	}
720578Srgrimes	return i;
721578Srgrimes}
722578Srgrimes
7231197Srgrimesstatic int
7241197Srgrimesmcd_send(int unit, int cmd,int nretrys)
725578Srgrimes{
726578Srgrimes	int i,k;
727578Srgrimes	int port = mcd_data[unit].iobase;
728578Srgrimes
729578Srgrimes/*MCD_TRACE("mcd_send: command = 0x%x\n",cmd,0,0,0);*/
730578Srgrimes	for (i=0; i<nretrys; i++) {
731578Srgrimes		outb(port+mcd_command, cmd);
7321197Srgrimes		if ((k=mcd_getstat(unit,0)) != -1) {
733578Srgrimes			break;
7341197Srgrimes		}
735578Srgrimes	}
736578Srgrimes	if (i == nretrys) {
737578Srgrimes		printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
738578Srgrimes		return -1;
739578Srgrimes	}
740578Srgrimes/*MCD_TRACE("mcd_send: status = 0x%x\n",k,0,0,0);*/
741578Srgrimes	return 0;
742578Srgrimes}
743578Srgrimes
7441197Srgrimesstatic int
7451197Srgrimesbcd2bin(bcd_t b)
746578Srgrimes{
747578Srgrimes	return (b >> 4) * 10 + (b & 15);
748578Srgrimes}
749578Srgrimes
7501197Srgrimesstatic bcd_t
7511197Srgrimesbin2bcd(int b)
752578Srgrimes{
753578Srgrimes	return ((b / 10) << 4) | (b % 10);
754578Srgrimes}
755578Srgrimes
7561197Srgrimesstatic void
7571197Srgrimeshsg2msf(int hsg, bcd_t *msf)
758578Srgrimes{
759578Srgrimes	hsg += 150;
760578Srgrimes	M_msf(msf) = bin2bcd(hsg / 4500);
761578Srgrimes	hsg %= 4500;
762578Srgrimes	S_msf(msf) = bin2bcd(hsg / 75);
763578Srgrimes	F_msf(msf) = bin2bcd(hsg % 75);
764578Srgrimes}
765578Srgrimes
7661197Srgrimesstatic int
7671197Srgrimesmsf2hsg(bcd_t *msf)
768578Srgrimes{
769578Srgrimes	return (bcd2bin(M_msf(msf)) * 60 +
770578Srgrimes		bcd2bin(S_msf(msf))) * 75 +
771578Srgrimes		bcd2bin(F_msf(msf)) - 150;
772578Srgrimes}
773578Srgrimes
7741197Srgrimesstatic int
7751197Srgrimesmcd_volinfo(int unit)
776578Srgrimes{
777578Srgrimes	struct mcd_data *cd = mcd_data + unit;
778578Srgrimes	int i;
779578Srgrimes
780578Srgrimes/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
781578Srgrimes
782578Srgrimes	/* Get the status, in case the disc has been changed */
783578Srgrimes	if (mcd_getstat(unit, 1) < 0) return EIO;
784578Srgrimes
785578Srgrimes	/* Just return if we already have it */
786578Srgrimes	if (cd->flags & MCDVOLINFO) return 0;
787578Srgrimes
788578Srgrimes	/* send volume info command */
789578Srgrimes	if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
790578Srgrimes		return -1;
791578Srgrimes
792578Srgrimes	/* get data */
793578Srgrimes	if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
794578Srgrimes		printf("mcd%d: mcd_volinfo: error read data\n",unit);
795578Srgrimes		return -1;
796578Srgrimes	}
797578Srgrimes
798578Srgrimes	if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) {
799578Srgrimes		cd->flags |= MCDVOLINFO;	/* volinfo is OK */
800578Srgrimes		return 0;
801578Srgrimes	}
802578Srgrimes
803578Srgrimes	return -1;
804578Srgrimes}
805578Srgrimes
806798Swollmanvoid
807798Swollmanmcdintr(unit)
808798Swollman	int unit;
809578Srgrimes{
810578Srgrimes	int	port = mcd_data[unit].iobase;
811578Srgrimes	u_int	i;
812578Srgrimes
813578Srgrimes	MCD_TRACE("stray interrupt xfer=0x%x\n",inb(port+mcd_xfer),0,0,0);
814578Srgrimes
815578Srgrimes	/* just read out status and ignore the rest */
816578Srgrimes	if ((inb(port+mcd_xfer)&0xFF) != 0xFF) {
817578Srgrimes		i = inb(port+mcd_status);
818578Srgrimes	}
819578Srgrimes}
820578Srgrimes
821578Srgrimes/* state machine to process read requests
822578Srgrimes * initialize with MCD_S_BEGIN: calculate sizes, and read status
823578Srgrimes * MCD_S_WAITSTAT: wait for status reply, set mode
824578Srgrimes * MCD_S_WAITMODE: waits for status reply from set mode, set read command
825578Srgrimes * MCD_S_WAITREAD: wait for read ready, read data
826578Srgrimes */
827578Srgrimesstatic struct mcd_mbx *mbxsave;
828578Srgrimes
8291197Srgrimesstatic void
8301197Srgrimesmcd_doread(int state, struct mcd_mbx *mbxin)
831578Srgrimes{
832578Srgrimes	struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
833578Srgrimes	int	unit = mbx->unit;
834578Srgrimes	int	port = mbx->port;
835578Srgrimes	struct	buf *bp = mbx->bp;
836578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
837578Srgrimes
838578Srgrimes	int	rm,i,k;
839578Srgrimes	struct mcd_read2 rbuf;
840578Srgrimes	int	blknum;
841578Srgrimes	caddr_t	addr;
842578Srgrimes
843578Srgrimesloop:
844578Srgrimes	switch (state) {
845578Srgrimes	case MCD_S_BEGIN:
846578Srgrimes		mbx = mbxsave = mbxin;
847578Srgrimes
848578Srgrimes	case MCD_S_BEGIN1:
849578Srgrimes		/* get status */
850578Srgrimes		outb(port+mcd_command, MCD_CMDGETSTAT);
851578Srgrimes		mbx->count = RDELAY_WAITSTAT;
8521197Srgrimes		timeout((timeout_func_t)mcd_doread,
8531197Srgrimes			(caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
854578Srgrimes		return;
855578Srgrimes	case MCD_S_WAITSTAT:
8561000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT);
857578Srgrimes		if (mbx->count-- >= 0) {
858578Srgrimes			if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
8591197Srgrimes				timeout((timeout_func_t)mcd_doread,
8601197Srgrimes				    (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
861578Srgrimes				return;
862578Srgrimes			}
863578Srgrimes			mcd_setflags(unit,cd);
8641197Srgrimes			MCD_TRACE("got WAITSTAT delay=%d\n",
8651197Srgrimes				RDELAY_WAITSTAT-mbx->count,0,0,0);
866578Srgrimes			/* reject, if audio active */
867578Srgrimes			if (cd->status & MCDAUDIOBSY) {
868578Srgrimes				printf("mcd%d: audio is active\n",unit);
869578Srgrimes				goto readerr;
870578Srgrimes			}
871578Srgrimes
872578Srgrimes			/* to check for raw/cooked mode */
873578Srgrimes			if (cd->flags & MCDREADRAW) {
874578Srgrimes				rm = MCD_MD_RAW;
875578Srgrimes				mbx->sz = MCDRBLK;
876578Srgrimes			} else {
877578Srgrimes				rm = MCD_MD_COOKED;
878578Srgrimes				mbx->sz = cd->blksize;
879578Srgrimes			}
880578Srgrimes
881578Srgrimes			mbx->count = RDELAY_WAITMODE;
882578Srgrimes
883578Srgrimes			mcd_put(port+mcd_command, MCD_CMDSETMODE);
884578Srgrimes			mcd_put(port+mcd_command, rm);
8851197Srgrimes			timeout((timeout_func_t)mcd_doread,
8861197Srgrimes				(caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */
887578Srgrimes			return;
888578Srgrimes		} else {
889578Srgrimes#ifdef MCD_TO_WARNING_ON
890578Srgrimes			printf("mcd%d: timeout getstatus\n",unit);
891578Srgrimes#endif
892578Srgrimes			goto readerr;
893578Srgrimes		}
894578Srgrimes
895578Srgrimes	case MCD_S_WAITMODE:
8961000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE);
897578Srgrimes		if (mbx->count-- < 0) {
898578Srgrimes#ifdef MCD_TO_WARNING_ON
899578Srgrimes			printf("mcd%d: timeout set mode\n",unit);
900578Srgrimes#endif
901578Srgrimes			goto readerr;
902578Srgrimes		}
903578Srgrimes		if (inb(port+mcd_xfer) & MCD_ST_BUSY) {
904798Swollman			timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100);
905578Srgrimes			return;
906578Srgrimes		}
907578Srgrimes		mcd_setflags(unit,cd);
9081197Srgrimes		MCD_TRACE("got WAITMODE delay=%d\n",
9091197Srgrimes			RDELAY_WAITMODE-mbx->count,0,0,0);
910578Srgrimes		/* for first block */
911578Srgrimes		mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
912578Srgrimes		mbx->skip = 0;
913578Srgrimes
914578Srgrimesnextblock:
915578Srgrimes		blknum 	= (bp->b_blkno / (mbx->sz/DEV_BSIZE))
916578Srgrimes			+ mbx->p_offset + mbx->skip/mbx->sz;
917578Srgrimes
9181197Srgrimes		MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",
9191197Srgrimes			blknum,bp,0,0);
920578Srgrimes
921578Srgrimes		/* build parameter block */
922578Srgrimes		hsg2msf(blknum,rbuf.start_msf);
923578Srgrimes
924578Srgrimes		/* send the read command */
925578Srgrimes		mcd_put(port+mcd_command,MCD_CMDREAD2);
926578Srgrimes		mcd_put(port+mcd_command,rbuf.start_msf[0]);
927578Srgrimes		mcd_put(port+mcd_command,rbuf.start_msf[1]);
928578Srgrimes		mcd_put(port+mcd_command,rbuf.start_msf[2]);
929578Srgrimes		mcd_put(port+mcd_command,0);
930578Srgrimes		mcd_put(port+mcd_command,0);
931578Srgrimes		mcd_put(port+mcd_command,1);
932578Srgrimes		mbx->count = RDELAY_WAITREAD;
9331197Srgrimes		timeout((timeout_func_t)mcd_doread,
9341197Srgrimes			(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
935578Srgrimes		return;
936578Srgrimes	case MCD_S_WAITREAD:
9371000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD);
938578Srgrimes		if (mbx->count-- > 0) {
939578Srgrimes			k = inb(port+mcd_xfer);
940578Srgrimes			if ((k & 2)==0) {
9411197Srgrimes				MCD_TRACE("got data delay=%d\n",
9421197Srgrimes					RDELAY_WAITREAD-mbx->count,0,0,0);
943578Srgrimes				/* data is ready */
944578Srgrimes				addr	= bp->b_un.b_addr + mbx->skip;
945578Srgrimes				outb(port+mcd_ctl2,0x04);	/* XXX */
946578Srgrimes				for (i=0; i<mbx->sz; i++)
947578Srgrimes					*addr++	= inb(port+mcd_rdata);
948578Srgrimes				outb(port+mcd_ctl2,0x0c);	/* XXX */
949578Srgrimes
950578Srgrimes				if (--mbx->nblk > 0) {
951578Srgrimes					mbx->skip += mbx->sz;
952578Srgrimes					goto nextblock;
953578Srgrimes				}
954578Srgrimes
955578Srgrimes				/* return buffer */
956578Srgrimes				bp->b_resid = 0;
957578Srgrimes				biodone(bp);
958578Srgrimes
959578Srgrimes				cd->flags &= ~MCDMBXBSY;
960578Srgrimes				mcd_start(mbx->unit);
961578Srgrimes				return;
962578Srgrimes			}
963578Srgrimes			if ((k & 4)==0)
964578Srgrimes				mcd_getstat(unit,0);
9651197Srgrimes			timeout((timeout_func_t)mcd_doread,
9661197Srgrimes				(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
967578Srgrimes			return;
968578Srgrimes		} else {
969578Srgrimes#ifdef MCD_TO_WARNING_ON
970578Srgrimes			printf("mcd%d: timeout read data\n",unit);
971578Srgrimes#endif
972578Srgrimes			goto readerr;
973578Srgrimes		}
974578Srgrimes	}
975578Srgrimes
976578Srgrimesreaderr:
977578Srgrimes	if (mbx->retry-- > 0) {
978578Srgrimes#ifdef MCD_TO_WARNING_ON
979578Srgrimes		printf("mcd%d: retrying\n",unit);
980578Srgrimes#endif
981578Srgrimes		state = MCD_S_BEGIN1;
982578Srgrimes		goto loop;
983578Srgrimes	}
984578Srgrimes
985578Srgrimes	/* invalidate the buffer */
986578Srgrimes	bp->b_flags |= B_ERROR;
987578Srgrimes	bp->b_resid = bp->b_bcount;
988578Srgrimes	biodone(bp);
989578Srgrimes	mcd_start(mbx->unit);
990578Srgrimes	return;
991578Srgrimes
992578Srgrimes#ifdef NOTDEF
993578Srgrimes	printf("mcd%d: unit timeout, resetting\n",mbx->unit);
994578Srgrimes	outb(mbx->port+mcd_reset,MCD_CMDRESET);
995578Srgrimes	DELAY(300000);
996578Srgrimes	(void)mcd_getstat(mbx->unit,1);
997578Srgrimes	(void)mcd_getstat(mbx->unit,1);
998578Srgrimes	/*cd->status &= ~MCDDSKCHNG; */
999578Srgrimes	cd->debug = 1; /* preventive set debug mode */
1000578Srgrimes
1001578Srgrimes#endif
1002578Srgrimes
1003578Srgrimes}
1004578Srgrimes
1005578Srgrimes#ifndef MCDMINI
10061197Srgrimesstatic int
10071197Srgrimesmcd_setmode(int unit, int mode)
1008578Srgrimes{
1009578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1010578Srgrimes	int port = cd->iobase;
1011578Srgrimes	int retry;
1012578Srgrimes
1013578Srgrimes	printf("mcd%d: setting mode to %d\n", unit, mode);
1014578Srgrimes	for(retry=0; retry<MCD_RETRYS; retry++)
1015578Srgrimes	{
1016578Srgrimes		outb(port+mcd_command, MCD_CMDSETMODE);
1017578Srgrimes		outb(port+mcd_command, mode);
1018578Srgrimes		if (mcd_getstat(unit, 0) != -1) return 0;
1019578Srgrimes	}
1020578Srgrimes
1021578Srgrimes	return -1;
1022578Srgrimes}
1023578Srgrimes
10241197Srgrimesstatic int
10251197Srgrimesmcd_toc_header(int unit, struct ioc_toc_header *th)
1026578Srgrimes{
1027578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1028578Srgrimes
10291197Srgrimes	if (mcd_volinfo(unit) < 0) {
1030578Srgrimes		return ENXIO;
10311197Srgrimes	}
1032578Srgrimes
1033578Srgrimes	th->len = msf2hsg(cd->volinfo.vol_msf);
1034578Srgrimes	th->starting_track = bcd2bin(cd->volinfo.trk_low);
1035578Srgrimes	th->ending_track = bcd2bin(cd->volinfo.trk_high);
1036578Srgrimes
1037578Srgrimes	return 0;
1038578Srgrimes}
1039578Srgrimes
10401197Srgrimesstatic int
10411197Srgrimesmcd_read_toc(int unit)
1042578Srgrimes{
1043578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1044578Srgrimes	struct ioc_toc_header th;
1045578Srgrimes	struct mcd_qchninfo q;
1046578Srgrimes	int rc, trk, idx, retry;
1047578Srgrimes
1048578Srgrimes	/* Only read TOC if needed */
10491197Srgrimes	if (cd->flags & MCDTOC) {
10501197Srgrimes		return 0;
10511197Srgrimes	}
1052578Srgrimes
1053578Srgrimes	printf("mcd%d: reading toc header\n", unit);
10541197Srgrimes	if (mcd_toc_header(unit, &th) != 0) {
1055578Srgrimes		return ENXIO;
10561197Srgrimes	}
1057578Srgrimes
1058578Srgrimes	printf("mcd%d: stopping play\n", unit);
10591197Srgrimes	if ((rc=mcd_stop(unit)) != 0) {
1060578Srgrimes		return rc;
10611197Srgrimes	}
1062578Srgrimes
1063578Srgrimes	/* try setting the mode twice */
10641197Srgrimes	if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
1065578Srgrimes		return EIO;
10661197Srgrimes	}
10671197Srgrimes	if (mcd_setmode(unit, MCD_MD_TOC) != 0) {
1068578Srgrimes		return EIO;
10691197Srgrimes	}
1070578Srgrimes
1071578Srgrimes	printf("mcd%d: get_toc reading qchannel info\n",unit);
1072578Srgrimes	for(trk=th.starting_track; trk<=th.ending_track; trk++)
1073578Srgrimes		cd->toc[trk].idx_no = 0;
1074578Srgrimes	trk = th.ending_track - th.starting_track + 1;
1075578Srgrimes	for(retry=0; retry<300 && trk>0; retry++)
1076578Srgrimes	{
1077578Srgrimes		if (mcd_getqchan(unit, &q) < 0) break;
1078578Srgrimes		idx = bcd2bin(q.idx_no);
10791197Srgrimes		if (idx>0 && idx < MCD_MAXTOCS && q.trk_no==0) {
10801197Srgrimes			if (cd->toc[idx].idx_no == 0) {
1081578Srgrimes				cd->toc[idx] = q;
1082578Srgrimes				trk--;
1083578Srgrimes			}
10841197Srgrimes		}
1085578Srgrimes	}
1086578Srgrimes
10871197Srgrimes	if (mcd_setmode(unit, MCD_MD_COOKED) != 0) {
1088578Srgrimes		return EIO;
10891197Srgrimes	}
1090578Srgrimes
10911197Srgrimes	if (trk != 0) {
10921197Srgrimes		return ENXIO;
10931197Srgrimes	}
1094578Srgrimes
1095578Srgrimes	/* add a fake last+1 */
1096578Srgrimes	idx = th.ending_track + 1;
1097578Srgrimes	cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
1098578Srgrimes	cd->toc[idx].trk_no = 0;
1099578Srgrimes	cd->toc[idx].idx_no = 0xAA;
1100578Srgrimes	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
1101578Srgrimes	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
1102578Srgrimes	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
1103578Srgrimes
1104578Srgrimes	cd->flags |= MCDTOC;
1105578Srgrimes
1106578Srgrimes	return 0;
1107578Srgrimes}
1108578Srgrimes
11091197Srgrimesstatic int
11101197Srgrimesmcd_toc_entry(int unit, struct ioc_read_toc_entry *te)
1111578Srgrimes{
1112578Srgrimes	struct mcd_data *cd = mcd_data + unit;
11131197Srgrimes	struct ret_toc {
1114578Srgrimes		struct ioc_toc_header th;
1115578Srgrimes		struct cd_toc_entry rt;
1116578Srgrimes	} ret_toc;
1117578Srgrimes	struct ioc_toc_header th;
1118578Srgrimes	int rc, i;
1119578Srgrimes
1120578Srgrimes	/* Make sure we have a valid toc */
11211197Srgrimes	if ((rc=mcd_read_toc(unit)) != 0) {
1122578Srgrimes		return rc;
11231197Srgrimes	}
1124578Srgrimes
1125578Srgrimes	/* find the toc to copy*/
1126578Srgrimes	i = te->starting_track;
11271197Srgrimes	if (i == MCD_LASTPLUS1) {
1128578Srgrimes		i = bcd2bin(cd->volinfo.trk_high) + 1;
11291197Srgrimes	}
1130578Srgrimes
1131578Srgrimes	/* verify starting track */
1132578Srgrimes	if (i < bcd2bin(cd->volinfo.trk_low) ||
11331197Srgrimes		i > bcd2bin(cd->volinfo.trk_high)+1) {
1134578Srgrimes		return EINVAL;
11351197Srgrimes	}
1136578Srgrimes
1137578Srgrimes	/* do we have room */
1138578Srgrimes	if (te->data_len < sizeof(struct ioc_toc_header) +
11391197Srgrimes		sizeof(struct cd_toc_entry)) {
11401197Srgrimes		return EINVAL;
11411197Srgrimes	}
1142578Srgrimes
1143578Srgrimes	/* Copy the toc header */
11441197Srgrimes	if (mcd_toc_header(unit, &th) < 0) {
11451197Srgrimes		return EIO;
11461197Srgrimes	}
1147578Srgrimes	ret_toc.th = th;
1148578Srgrimes
1149578Srgrimes	/* copy the toc data */
1150578Srgrimes	ret_toc.rt.control = cd->toc[i].ctrl_adr;
1151578Srgrimes	ret_toc.rt.addr_type = te->address_format;
1152578Srgrimes	ret_toc.rt.track = i;
11531197Srgrimes	if (te->address_format == CD_MSF_FORMAT) {
11541097Srgrimes		ret_toc.rt.addr.addr[1] = cd->toc[i].hd_pos_msf[0];
11551097Srgrimes		ret_toc.rt.addr.addr[2] = cd->toc[i].hd_pos_msf[1];
11561097Srgrimes		ret_toc.rt.addr.addr[3] = cd->toc[i].hd_pos_msf[2];
1157578Srgrimes	}
1158578Srgrimes
1159578Srgrimes	/* copy the data back */
1160578Srgrimes	copyout(&ret_toc, te->data, sizeof(struct cd_toc_entry)
1161578Srgrimes		+ sizeof(struct ioc_toc_header));
1162578Srgrimes
1163578Srgrimes	return 0;
1164578Srgrimes}
1165578Srgrimes
11661197Srgrimesstatic int
11671197Srgrimesmcd_stop(int unit)
1168578Srgrimes{
1169578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1170578Srgrimes
11711197Srgrimes	if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) {
1172578Srgrimes		return ENXIO;
11731197Srgrimes	}
1174578Srgrimes	cd->audio_status = CD_AS_PLAY_COMPLETED;
1175578Srgrimes	return 0;
1176578Srgrimes}
1177578Srgrimes
11781197Srgrimesstatic int
11791197Srgrimesmcd_getqchan(int unit, struct mcd_qchninfo *q)
1180578Srgrimes{
1181578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1182578Srgrimes
11831197Srgrimes	if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) {
1184578Srgrimes		return -1;
11851197Srgrimes	}
11861197Srgrimes	if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) {
1187578Srgrimes		return -1;
11881197Srgrimes	}
11891197Srgrimes	if (cd->debug) {
11901197Srgrimes		printf("mcd%d: qchannel ctl=%d, t=%d, i=%d, ttm=%d:%d.%d dtm=%d:%d.%d\n",
1191578Srgrimes		unit,
1192578Srgrimes		q->ctrl_adr, q->trk_no, q->idx_no,
1193578Srgrimes		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
1194578Srgrimes		q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
11951197Srgrimes	}
1196578Srgrimes	return 0;
1197578Srgrimes}
1198578Srgrimes
11991197Srgrimesstatic int
12001197Srgrimesmcd_subchan(int unit, struct ioc_read_subchannel *sc)
1201578Srgrimes{
1202578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1203578Srgrimes	struct mcd_qchninfo q;
1204578Srgrimes	struct cd_sub_channel_info data;
1205578Srgrimes
1206578Srgrimes	printf("mcd%d: subchan af=%d, df=%d\n", unit,
1207578Srgrimes		sc->address_format,
1208578Srgrimes		sc->data_format);
12091197Srgrimes	if (sc->address_format != CD_MSF_FORMAT) {
12101197Srgrimes		return EIO;
12111197Srgrimes	}
12121197Srgrimes	if (sc->data_format != CD_CURRENT_POSITION) {
12131197Srgrimes		return EIO;
12141197Srgrimes	}
12151197Srgrimes	if (mcd_getqchan(unit, &q) < 0) {
12161197Srgrimes		return EIO;
12171197Srgrimes	}
1218578Srgrimes
1219578Srgrimes	data.header.audio_status = cd->audio_status;
1220578Srgrimes	data.what.position.data_format = CD_MSF_FORMAT;
1221578Srgrimes	data.what.position.track_number = bcd2bin(q.trk_no);
1222578Srgrimes
12231197Srgrimes	if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0) {
1224578Srgrimes		return EFAULT;
12251197Srgrimes	}
1226578Srgrimes	return 0;
1227578Srgrimes}
1228578Srgrimes
12291197Srgrimesstatic int
12301197Srgrimesmcd_playtracks(int unit, struct ioc_play_track *pt)
1231578Srgrimes{
1232578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1233578Srgrimes	struct mcd_read2 pb;
1234578Srgrimes	int a = pt->start_track;
1235578Srgrimes	int z = pt->end_track;
1236578Srgrimes	int rc;
1237578Srgrimes
12381197Srgrimes	if ((rc = mcd_read_toc(unit)) != 0) {
12391197Srgrimes		return rc;
12401197Srgrimes	}
1241578Srgrimes	printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
1242578Srgrimes		a, pt->start_index, z, pt->end_index);
1243578Srgrimes
1244578Srgrimes	if (a < cd->volinfo.trk_low || a > cd->volinfo.trk_high || a > z ||
12451197Srgrimes	    z < cd->volinfo.trk_low || z > cd->volinfo.trk_high) {
1246578Srgrimes		return EINVAL;
12471197Srgrimes	}
1248578Srgrimes
1249578Srgrimes	pb.start_msf[0] = cd->toc[a].hd_pos_msf[0];
1250578Srgrimes	pb.start_msf[1] = cd->toc[a].hd_pos_msf[1];
1251578Srgrimes	pb.start_msf[2] = cd->toc[a].hd_pos_msf[2];
1252578Srgrimes	pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0];
1253578Srgrimes	pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1];
1254578Srgrimes	pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2];
1255578Srgrimes
1256578Srgrimes	return mcd_play(unit, &pb);
1257578Srgrimes}
1258578Srgrimes
12591197Srgrimesstatic int
12601197Srgrimesmcd_play(int unit, struct mcd_read2 *pb)
1261578Srgrimes{
1262578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1263578Srgrimes	int port = cd->iobase;
1264578Srgrimes	int retry, st;
1265578Srgrimes
1266578Srgrimes	cd->lastpb = *pb;
12671197Srgrimes	for(retry=0; retry<MCD_RETRYS; retry++) {
1268578Srgrimes		outb(port+mcd_command, MCD_CMDREAD2);
1269578Srgrimes		outb(port+mcd_command, pb->start_msf[0]);
1270578Srgrimes		outb(port+mcd_command, pb->start_msf[1]);
1271578Srgrimes		outb(port+mcd_command, pb->start_msf[2]);
1272578Srgrimes		outb(port+mcd_command, pb->end_msf[0]);
1273578Srgrimes		outb(port+mcd_command, pb->end_msf[1]);
1274578Srgrimes		outb(port+mcd_command, pb->end_msf[2]);
12751197Srgrimes		if ((st=mcd_getstat(unit, 0)) != -1) {
12761197Srgrimes			break;
12771197Srgrimes		}
1278578Srgrimes	}
1279578Srgrimes
12801197Srgrimes	if (cd->debug) {
12811197Srgrimes		printf("mcd%d: mcd_play retry=%d, status=%d\n", unit, retry, st);
12821197Srgrimes	}
12831197Srgrimes	if (st == -1) {
12841197Srgrimes		return ENXIO;
12851197Srgrimes	}
1286578Srgrimes	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
1287578Srgrimes	return 0;
1288578Srgrimes}
1289578Srgrimes
12901197Srgrimesstatic int
12911197Srgrimesmcd_pause(int unit)
1292578Srgrimes{
1293578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1294578Srgrimes	struct mcd_qchninfo q;
1295578Srgrimes	int rc;
1296578Srgrimes
1297578Srgrimes	/* Verify current status */
12981197Srgrimes	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) {
1299578Srgrimes		printf("mcd%d: pause attempted when not playing\n", unit);
1300578Srgrimes		return EINVAL;
1301578Srgrimes	}
1302578Srgrimes
1303578Srgrimes	/* Get the current position */
13041197Srgrimes	if (mcd_getqchan(unit, &q) < 0) {
13051197Srgrimes		return EIO;
13061197Srgrimes	}
1307578Srgrimes
1308578Srgrimes	/* Copy it into lastpb */
1309578Srgrimes	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
1310578Srgrimes	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
1311578Srgrimes	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
1312578Srgrimes
1313578Srgrimes	/* Stop playing */
13141197Srgrimes	if ((rc=mcd_stop(unit)) != 0) {
13151197Srgrimes		return rc;
13161197Srgrimes	}
1317578Srgrimes
1318578Srgrimes	/* Set the proper status and exit */
1319578Srgrimes	cd->audio_status = CD_AS_PLAY_PAUSED;
1320578Srgrimes	return 0;
1321578Srgrimes}
1322578Srgrimes
13231197Srgrimesstatic int
13241197Srgrimesmcd_resume(int unit)
1325578Srgrimes{
1326578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1327578Srgrimes
13281197Srgrimes	if (cd->audio_status != CD_AS_PLAY_PAUSED) {
13291197Srgrimes		return EINVAL;
13301197Srgrimes	}
1331578Srgrimes	return mcd_play(unit, &cd->lastpb);
1332578Srgrimes}
1333578Srgrimes#endif /*!MCDMINI*/
1334578Srgrimes
1335578Srgrimes#endif /* NMCD > 0 */
1336