mcd.c revision 6665
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
56604Sache * Changes Copyright (C) 1994-1995 by Andrey A. Chernov, Moscow, Russia
61197Srgrimes *
71197Srgrimes * Rewrote probe routine to work on newer Mitsumi drives.
81197Srgrimes * Additional changes (C) 1994 by Jordan K. Hubbard
91197Srgrimes *
10578Srgrimes * All rights reserved.
11578Srgrimes *
12578Srgrimes * Redistribution and use in source and binary forms, with or without
13578Srgrimes * modification, are permitted provided that the following conditions
14578Srgrimes * are met:
15578Srgrimes * 1. Redistributions of source code must retain the above copyright
16578Srgrimes *    notice, this list of conditions and the following disclaimer.
17578Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
18578Srgrimes *    notice, this list of conditions and the following disclaimer in the
19578Srgrimes *    documentation and/or other materials provided with the distribution.
20578Srgrimes * 3. All advertising materials mentioning features or use of this software
21578Srgrimes *    must display the following acknowledgement:
22578Srgrimes *	This software was developed by Holger Veit and Brian Moore
231197Srgrimes *	for use with "386BSD" and similar operating systems.
24578Srgrimes *    "Similar operating systems" includes mainly non-profit oriented
25578Srgrimes *    systems for research and education, including but not restricted to
26578Srgrimes *    "NetBSD", "FreeBSD", "Mach" (by CMU).
27578Srgrimes * 4. Neither the name of the developer(s) nor the name "386BSD"
28578Srgrimes *    may be used to endorse or promote products derived from this
29578Srgrimes *    software without specific prior written permission.
30578Srgrimes *
31578Srgrimes * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
32578Srgrimes * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33578Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34578Srgrimes * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER(S) BE
35578Srgrimes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
36578Srgrimes * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
37578Srgrimes * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
38578Srgrimes * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
39578Srgrimes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40578Srgrimes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41578Srgrimes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42578Srgrimes *
436665Sache *	$Id: mcd.c,v 1.38 1995/02/22 02:12:10 ache Exp $
44578Srgrimes */
45578Srgrimesstatic char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
46578Srgrimes
47578Srgrimes#include "mcd.h"
48578Srgrimes#if NMCD > 0
492056Swollman#include <sys/types.h>
502056Swollman#include <sys/param.h>
512056Swollman#include <sys/systm.h>
522056Swollman#include <sys/conf.h>
532056Swollman#include <sys/file.h>
542056Swollman#include <sys/buf.h>
552056Swollman#include <sys/stat.h>
562056Swollman#include <sys/uio.h>
572056Swollman#include <sys/ioctl.h>
582056Swollman#include <sys/cdio.h>
592056Swollman#include <sys/errno.h>
602056Swollman#include <sys/dkbad.h>
612056Swollman#include <sys/disklabel.h>
623816Swollman#include <sys/devconf.h>
633816Swollman
642056Swollman#include <i386/isa/isa.h>
652056Swollman#include <i386/isa/isa_device.h>
662056Swollman#include <i386/isa/mcdreg.h>
67578Srgrimes
682477Sache#define MCD_TRACE(fmt,a,b,c,d)  {if (mcd_data[unit].debug) {printf("mcd%d: status=0x%02x: ",unit,mcd_data[unit].status); printf(fmt,a,b,c,d);}}
69578Srgrimes
70578Srgrimes#define mcd_part(dev)	((minor(dev)) & 7)
71578Srgrimes#define mcd_unit(dev)	(((minor(dev)) & 0x38) >> 3)
72578Srgrimes#define mcd_phys(dev)	(((minor(dev)) & 0x40) >> 6)
732395Sache#define RAW_PART        2
74578Srgrimes
75578Srgrimes/* flags */
76578Srgrimes#define MCDOPEN		0x0001	/* device opened */
77578Srgrimes#define MCDVALID	0x0002	/* parameters loaded */
78578Srgrimes#define MCDINIT		0x0004	/* device is init'd */
792477Sache#define MCDNEWMODEL     0x0008  /* device is new model */
80578Srgrimes#define MCDLABEL	0x0010	/* label is read */
81578Srgrimes#define	MCDPROBING	0x0020	/* probing */
82578Srgrimes#define	MCDREADRAW	0x0040	/* read raw mode (2352 bytes) */
83578Srgrimes#define	MCDVOLINFO	0x0080	/* already read volinfo */
84578Srgrimes#define	MCDTOC		0x0100	/* already read toc */
85578Srgrimes#define	MCDMBXBSY	0x0200	/* local mbx is busy */
86578Srgrimes
87578Srgrimes/* status */
88578Srgrimes#define	MCDAUDIOBSY	MCD_ST_AUDIOBSY		/* playing audio */
89578Srgrimes#define MCDDSKCHNG	MCD_ST_DSKCHNG		/* sensed change of disk */
90578Srgrimes#define MCDDSKIN	MCD_ST_DSKIN		/* sensed disk in drive */
91578Srgrimes#define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
92578Srgrimes
931241Sjkh/* These are apparently the different states a mitsumi can get up to */
941241Sjkh#define MCDCDABSENT	0x0030
951241Sjkh#define MCDCDPRESENT	0x0020
961241Sjkh#define MCDSCLOSED	0x0080
971241Sjkh#define MCDSOPEN	0x00a0
981237Sjkh
992477Sache#define MCD_MD_UNKNOWN  (-1)
1002477Sache
101578Srgrimes/* toc */
102578Srgrimes#define MCD_MAXTOCS	104	/* from the Linux driver */
103578Srgrimes#define MCD_LASTPLUS1	170	/* special toc entry */
104578Srgrimes
1054480Sache#define	MCD_TYPE_UNKNOWN	0
1064480Sache#define	MCD_TYPE_LU002S		1
1074480Sache#define	MCD_TYPE_LU005S		2
1084480Sache#define	MCD_TYPE_FX001		3
1094480Sache#define	MCD_TYPE_FX001D		4
1104480Sache
111578Srgrimesstruct mcd_mbx {
112578Srgrimes	short		unit;
113578Srgrimes	short		port;
114578Srgrimes	short		retry;
115578Srgrimes	short		nblk;
116578Srgrimes	int		sz;
117578Srgrimes	u_long		skip;
118578Srgrimes	struct buf	*bp;
119578Srgrimes	int		p_offset;
120578Srgrimes	short		count;
1212477Sache	short           mode;
122578Srgrimes};
123578Srgrimes
124578Srgrimesstruct mcd_data {
1254480Sache	short	type;
1264480Sache	char	*name;
127578Srgrimes	short	config;
128578Srgrimes	short	flags;
1296028Sache	u_char	read_command;
130578Srgrimes	short	status;
131578Srgrimes	int	blksize;
132578Srgrimes	u_long	disksize;
133578Srgrimes	int	iobase;
134578Srgrimes	struct disklabel dlabel;
135578Srgrimes	int	partflags[MAXPARTITIONS];
136578Srgrimes	int	openflags;
137578Srgrimes	struct mcd_volinfo volinfo;
138578Srgrimes	struct mcd_qchninfo toc[MCD_MAXTOCS];
139578Srgrimes	short	audio_status;
1402477Sache	short   curr_mode;
141578Srgrimes	struct mcd_read2 lastpb;
142578Srgrimes	short	debug;
143578Srgrimes	struct buf head;		/* head of buf queue */
144578Srgrimes	struct mcd_mbx mbx;
145578Srgrimes} mcd_data[NMCD];
146578Srgrimes
147578Srgrimes/* reader state machine */
148578Srgrimes#define MCD_S_BEGIN	0
149578Srgrimes#define MCD_S_BEGIN1	1
150578Srgrimes#define MCD_S_WAITSTAT	2
151578Srgrimes#define MCD_S_WAITMODE	3
152578Srgrimes#define MCD_S_WAITREAD	4
153578Srgrimes
154578Srgrimes/* prototypes */
155578Srgrimesint	mcdopen(dev_t dev);
156578Srgrimesint	mcdclose(dev_t dev);
157798Swollmanvoid	mcdstrategy(struct buf *bp);
158578Srgrimesint	mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags);
159578Srgrimesint	mcdsize(dev_t dev);
160578Srgrimesstatic	void	mcd_done(struct mcd_mbx *mbx);
161578Srgrimesstatic	void	mcd_start(int unit);
162578Srgrimesstatic	int	mcd_getdisklabel(int unit);
163578Srgrimesstatic	void	mcd_configure(struct mcd_data *cd);
164578Srgrimesstatic	int	mcd_get(int unit, char *buf, int nmax);
1652477Sachestatic  int     mcd_setflags(int unit,struct mcd_data *cd);
166578Srgrimesstatic	int	mcd_getstat(int unit,int sflg);
167578Srgrimesstatic	int	mcd_send(int unit, int cmd,int nretrys);
168578Srgrimesstatic	int	bcd2bin(bcd_t b);
169578Srgrimesstatic	bcd_t	bin2bcd(int b);
170578Srgrimesstatic	void	hsg2msf(int hsg, bcd_t *msf);
171578Srgrimesstatic	int	msf2hsg(bcd_t *msf);
172578Srgrimesstatic	int	mcd_volinfo(int unit);
173578Srgrimesstatic	int	mcd_waitrdy(int port,int dly);
174978Sjkhstatic 	void	mcd_doread(int state, struct mcd_mbx *mbxin);
1752477Sachestatic  void    mcd_soft_reset(int unit);
1762477Sachestatic  int     mcd_hard_reset(int unit);
177578Srgrimesstatic	int 	mcd_setmode(int unit, int mode);
178578Srgrimesstatic	int	mcd_getqchan(int unit, struct mcd_qchninfo *q);
179578Srgrimesstatic	int	mcd_subchan(int unit, struct ioc_read_subchannel *sc);
180578Srgrimesstatic	int	mcd_toc_header(int unit, struct ioc_toc_header *th);
181578Srgrimesstatic	int	mcd_read_toc(int unit);
1822477Sachestatic  int     mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te);
183578Srgrimesstatic	int	mcd_stop(int unit);
1842477Sachestatic  int     mcd_eject(int unit);
185578Srgrimesstatic	int	mcd_playtracks(int unit, struct ioc_play_track *pt);
186578Srgrimesstatic	int	mcd_play(int unit, struct mcd_read2 *pb);
1872477Sachestatic  int     mcd_playmsf(int unit, struct ioc_play_msf *pt);
188578Srgrimesstatic	int	mcd_pause(int unit);
189578Srgrimesstatic	int	mcd_resume(int unit);
1906604Sachestatic  int     mcd_lock_door(int unit, int lock);
1916604Sachestatic  int     mcd_close_tray(int unit);
192578Srgrimes
193578Srgrimesextern	int	hz;
194578Srgrimesextern	int	mcd_probe(struct isa_device *dev);
195578Srgrimesextern	int	mcd_attach(struct isa_device *dev);
196578Srgrimesstruct	isa_driver	mcddriver = { mcd_probe, mcd_attach, "mcd" };
197578Srgrimes
198578Srgrimes#define mcd_put(port,byte)	outb(port,byte)
199578Srgrimes
200578Srgrimes#define MCD_RETRYS	5
201578Srgrimes#define MCD_RDRETRYS	8
202578Srgrimes
2036604Sache#define CLOSE_TRAY_SECS 8
2046604Sache#define DISK_SENSE_SECS 3
2056604Sache#define WAIT_FRAC 4
2066604Sache
207578Srgrimes/* several delays */
2082477Sache#define RDELAY_WAITSTAT 300
2092477Sache#define RDELAY_WAITMODE 300
210578Srgrimes#define RDELAY_WAITREAD	800
211578Srgrimes
2122477Sache#define MIN_DELAY       15
2136665Sache#define DELAY_GETREPLY  1500000
214578Srgrimes
2153816Swollmanstatic struct kern_devconf kdc_mcd[NMCD] = { {
2163816Swollman	0, 0, 0,		/* filled in by dev_attach */
2173816Swollman	"mcd", 0, { MDDT_ISA, 0, "bio" },
2183816Swollman	isa_generic_externalize, 0, 0, ISA_EXTERNALLEN,
2193816Swollman	&kdc_isa0,		/* parent */
2203816Swollman	0,			/* parentdata */
2213816Swollman	DC_IDLE,		/* status */
2224480Sache	"Mitsumi CD-ROM controller"     /* properly filled later */
2233816Swollman} };
2243816Swollman
2253816Swollmanstatic inline void
2263816Swollmanmcd_registerdev(struct isa_device *id)
2273816Swollman{
2283816Swollman	if(id->id_unit)
2293816Swollman		kdc_mcd[id->id_unit] = kdc_mcd[0];
2303816Swollman	kdc_mcd[id->id_unit].kdc_unit = id->id_unit;
2313816Swollman	kdc_mcd[id->id_unit].kdc_isa = id;
2323816Swollman	dev_attach(&kdc_mcd[id->id_unit]);
2333816Swollman}
2343816Swollman
235578Srgrimesint mcd_attach(struct isa_device *dev)
236578Srgrimes{
237578Srgrimes	struct mcd_data *cd = mcd_data + dev->id_unit;
238578Srgrimes	int i;
239578Srgrimes
240578Srgrimes	cd->iobase = dev->id_iobase;
241578Srgrimes	cd->flags |= MCDINIT;
2422477Sache	mcd_soft_reset(dev->id_unit);
243578Srgrimes
244578Srgrimes#ifdef NOTYET
245578Srgrimes	/* wire controller for interrupts and dma */
246578Srgrimes	mcd_configure(cd);
247578Srgrimes#endif
2483816Swollman	mcd_registerdev(dev);
2494480Sache	/* name filled in probe */
2504480Sache	kdc_mcd[dev->id_unit].kdc_description = mcd_data[dev->id_unit].name;
251578Srgrimes
252578Srgrimes	return 1;
253578Srgrimes}
254578Srgrimes
255578Srgrimesint mcdopen(dev_t dev)
256578Srgrimes{
2576604Sache	int unit,part,phys,r,retry;
258578Srgrimes	struct mcd_data *cd;
259578Srgrimes
260578Srgrimes	unit = mcd_unit(dev);
261578Srgrimes	if (unit >= NMCD)
262578Srgrimes		return ENXIO;
263578Srgrimes
264578Srgrimes	cd = mcd_data + unit;
265578Srgrimes	part = mcd_part(dev);
266578Srgrimes	phys = mcd_phys(dev);
267578Srgrimes
268578Srgrimes	/* not initialized*/
269578Srgrimes	if (!(cd->flags & MCDINIT))
270578Srgrimes		return ENXIO;
271578Srgrimes
272578Srgrimes	/* invalidated in the meantime? mark all open part's invalid */
273578Srgrimes	if (!(cd->flags & MCDVALID) && cd->openflags)
274578Srgrimes		return ENXIO;
275578Srgrimes
2766604Sache	if (mcd_close_tray(unit) == EIO)  /* detect disk change too */
2776604Sache		return EIO;
278578Srgrimes
2796604Sache	if (    (cd->status & (MCDDSKCHNG|MCDDOOROPEN))
2806604Sache	    || !(cd->status & MCDDSKIN))
2816604Sache		for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) {
2826604Sache			(void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdsns", hz/WAIT_FRAC);
2836604Sache			if ((r = mcd_getstat(unit,1)) == -1)
2846604Sache				return EIO;
2856604Sache			if (r != -2)
2866604Sache				break;
2876604Sache		}
2886604Sache
2892477Sache	if (cd->status & MCDDOOROPEN) {
2902477Sache		printf("mcd%d: door is open\n");
2912477Sache		return ENXIO;
2922477Sache	}
2932477Sache	if (!(cd->status & MCDDSKIN)) {
2942477Sache		printf("mcd%d: no CD inside\n");
2952477Sache		return ENXIO;
2962477Sache	}
2976604Sache	if (cd->status & MCDDSKCHNG) {
2986604Sache		printf("mcd%d: CD not sensed\n");
2996604Sache		return ENXIO;
3006604Sache	}
301578Srgrimes
302578Srgrimes	if (mcdsize(dev) < 0) {
303578Srgrimes		printf("mcd%d: failed to get disk size\n",unit);
304578Srgrimes		return ENXIO;
305578Srgrimes	} else
306578Srgrimes		cd->flags |= MCDVALID;
307578Srgrimes
3082477Sache	/* XXX get a default disklabel */
3092477Sache	mcd_getdisklabel(unit);
3102477Sache
311578SrgrimesMCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n",
312578Srgrimes	part,cd->disksize,cd->blksize,0);
313578Srgrimes
314578Srgrimes	if (part == RAW_PART ||
315578Srgrimes		(part < cd->dlabel.d_npartitions &&
316578Srgrimes		cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
317578Srgrimes		cd->partflags[part] |= MCDOPEN;
318578Srgrimes		cd->openflags |= (1<<part);
319578Srgrimes		if (part == RAW_PART && phys != 0)
320578Srgrimes			cd->partflags[part] |= MCDREADRAW;
3213816Swollman		kdc_mcd[unit].kdc_state = DC_BUSY;
3226604Sache		(void) mcd_lock_door(unit, MCD_LK_LOCK);
3236604Sache		if (!(cd->flags & MCDVALID))
3246604Sache			return ENXIO;
325578Srgrimes		return 0;
326578Srgrimes	}
327578Srgrimes
328578Srgrimes	return ENXIO;
329578Srgrimes}
330578Srgrimes
331578Srgrimesint mcdclose(dev_t dev)
332578Srgrimes{
333578Srgrimes	int unit,part,phys;
334578Srgrimes	struct mcd_data *cd;
335578Srgrimes
336578Srgrimes	unit = mcd_unit(dev);
337578Srgrimes	if (unit >= NMCD)
338578Srgrimes		return ENXIO;
339578Srgrimes
340578Srgrimes	cd = mcd_data + unit;
341578Srgrimes	part = mcd_part(dev);
342578Srgrimes	phys = mcd_phys(dev);
343578Srgrimes
344578Srgrimes	if (!(cd->flags & MCDINIT))
345578Srgrimes		return ENXIO;
346578Srgrimes
3473816Swollman	kdc_mcd[unit].kdc_state = DC_IDLE;
3486604Sache	(void) mcd_lock_door(unit, MCD_LK_UNLOCK);
3496604Sache
3506604Sache	if (!(cd->flags & MCDVALID))
3512477Sache		return 0;
352578Srgrimes
353578Srgrimes	/* close channel */
354578Srgrimes	cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW);
355578Srgrimes	cd->openflags &= ~(1<<part);
356578Srgrimes	MCD_TRACE("close: partition=%d\n",part,0,0,0);
357578Srgrimes
358578Srgrimes	return 0;
359578Srgrimes}
360578Srgrimes
361798Swollmanvoid
362798Swollmanmcdstrategy(struct buf *bp)
363578Srgrimes{
364578Srgrimes	struct mcd_data *cd;
365578Srgrimes	struct buf *qp;
366578Srgrimes	int s;
367578Srgrimes
368578Srgrimes	int unit = mcd_unit(bp->b_dev);
369578Srgrimes
370578Srgrimes	cd = mcd_data + unit;
371578Srgrimes
372578Srgrimes	/* test validity */
373578Srgrimes/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
374578Srgrimes	bp,unit,bp->b_blkno,bp->b_bcount);*/
375578Srgrimes	if (unit >= NMCD || bp->b_blkno < 0) {
376578Srgrimes		printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n",
377578Srgrimes			unit, bp->b_blkno, bp->b_bcount);
378578Srgrimes		pg("mcd: mcdstratregy failure");
379578Srgrimes		bp->b_error = EINVAL;
380578Srgrimes		bp->b_flags |= B_ERROR;
381578Srgrimes		goto bad;
382578Srgrimes	}
383578Srgrimes
384578Srgrimes	/* if device invalidated (e.g. media change, door open), error */
385578Srgrimes	if (!(cd->flags & MCDVALID)) {
386578SrgrimesMCD_TRACE("strategy: drive not valid\n",0,0,0,0);
387578Srgrimes		bp->b_error = EIO;
388578Srgrimes		goto bad;
389578Srgrimes	}
390578Srgrimes
391578Srgrimes	/* read only */
392578Srgrimes	if (!(bp->b_flags & B_READ)) {
393578Srgrimes		bp->b_error = EROFS;
394578Srgrimes		goto bad;
395578Srgrimes	}
396578Srgrimes
397578Srgrimes	/* no data to read */
398578Srgrimes	if (bp->b_bcount == 0)
399578Srgrimes		goto done;
400578Srgrimes
401578Srgrimes	/* for non raw access, check partition limits */
402578Srgrimes	if (mcd_part(bp->b_dev) != RAW_PART) {
403578Srgrimes		if (!(cd->flags & MCDLABEL)) {
404578Srgrimes			bp->b_error = EIO;
405578Srgrimes			goto bad;
406578Srgrimes		}
407578Srgrimes		/* adjust transfer if necessary */
408578Srgrimes		if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
409578Srgrimes			goto done;
410578Srgrimes		}
4111379Sdg	} else {
4121379Sdg		bp->b_pblkno = bp->b_blkno;
4131437Sgclarkii		bp->b_resid = 0;
414578Srgrimes	}
415578Srgrimes
416578Srgrimes	/* queue it */
417578Srgrimes	qp = &cd->head;
418578Srgrimes	s = splbio();
419578Srgrimes	disksort(qp,bp);
420578Srgrimes	splx(s);
421578Srgrimes
422578Srgrimes	/* now check whether we can perform processing */
423578Srgrimes	mcd_start(unit);
424578Srgrimes	return;
425578Srgrimes
426578Srgrimesbad:
427578Srgrimes	bp->b_flags |= B_ERROR;
428578Srgrimesdone:
429578Srgrimes	bp->b_resid = bp->b_bcount;
430578Srgrimes	biodone(bp);
431578Srgrimes	return;
432578Srgrimes}
433578Srgrimes
434578Srgrimesstatic void mcd_start(int unit)
435578Srgrimes{
436578Srgrimes	struct mcd_data *cd = mcd_data + unit;
437578Srgrimes	struct buf *bp, *qp = &cd->head;
438578Srgrimes	struct partition *p;
439578Srgrimes	int part;
440578Srgrimes	register s = splbio();
441578Srgrimes
4422477Sache	if (cd->flags & MCDMBXBSY) {
4432477Sache		splx(s);
444578Srgrimes		return;
4452477Sache	}
446578Srgrimes
447578Srgrimes	if ((bp = qp->b_actf) != 0) {
448578Srgrimes		/* block found to process, dequeue */
449578Srgrimes		/*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
4502526Sse	    qp->b_actf = bp->b_actf; /* changed from: bp->av_forw <se> */
451578Srgrimes		splx(s);
452578Srgrimes	} else {
453578Srgrimes		/* nothing to do */
454578Srgrimes		splx(s);
455578Srgrimes		return;
456578Srgrimes	}
457578Srgrimes
458578Srgrimes	/* changed media? */
459578Srgrimes	if (!(cd->flags	& MCDVALID)) {
460578Srgrimes		MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0);
461578Srgrimes		return;
462578Srgrimes	}
463578Srgrimes
464578Srgrimes	p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
465578Srgrimes
466578Srgrimes	cd->flags |= MCDMBXBSY;
4672477Sache	if (cd->partflags[mcd_part(bp->b_dev)] & MCDREADRAW)
4682477Sache		cd->flags |= MCDREADRAW;
469578Srgrimes	cd->mbx.unit = unit;
470578Srgrimes	cd->mbx.port = cd->iobase;
471578Srgrimes	cd->mbx.retry = MCD_RETRYS;
472578Srgrimes	cd->mbx.bp = bp;
473578Srgrimes	cd->mbx.p_offset = p->p_offset;
474578Srgrimes
475578Srgrimes	/* calling the read routine */
476978Sjkh	mcd_doread(MCD_S_BEGIN,&(cd->mbx));
477578Srgrimes	/* triggers mcd_start, when successful finished */
478578Srgrimes	return;
479578Srgrimes}
480578Srgrimes
481578Srgrimesint mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags)
482578Srgrimes{
483578Srgrimes	struct mcd_data *cd;
484578Srgrimes	int unit,part;
485578Srgrimes
486578Srgrimes	unit = mcd_unit(dev);
487578Srgrimes	part = mcd_part(dev);
488578Srgrimes	cd = mcd_data + unit;
489578Srgrimes
4906604Sache	if (mcd_getstat(unit, 1) == -1) /* detect disk change too */
4912477Sache		return EIO;
492578SrgrimesMCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0);
493578Srgrimes
494578Srgrimes	switch (cmd) {
495578Srgrimes	case DIOCSBAD:
4966604Sache		if (!(cd->flags & MCDVALID))
4976604Sache			return ENXIO;
498578Srgrimes		return EINVAL;
499578Srgrimes	case DIOCGDINFO:
5006604Sache		if (!(cd->flags & MCDVALID))
5016604Sache			return ENXIO;
5022477Sache		*(struct disklabel *) addr = cd->dlabel;
5032477Sache		return 0;
504578Srgrimes	case DIOCGPART:
5056604Sache		if (!(cd->flags & MCDVALID))
5066604Sache			return ENXIO;
5072477Sache		((struct partinfo *) addr)->disklab = &cd->dlabel;
5082477Sache		((struct partinfo *) addr)->part =
5092477Sache		    &cd->dlabel.d_partitions[mcd_part(dev)];
5102477Sache		return 0;
5112477Sache
5122477Sache		/*
5132477Sache		 * a bit silly, but someone might want to test something on a
5142477Sache		 * section of cdrom.
5152477Sache		 */
516578Srgrimes	case DIOCWDINFO:
517578Srgrimes	case DIOCSDINFO:
5186604Sache		if (!(cd->flags & MCDVALID))
5196604Sache			return ENXIO;
5202477Sache		if ((flags & FWRITE) == 0)
5212477Sache			return EBADF;
5222477Sache		else {
5232477Sache			return setdisklabel(&cd->dlabel,
5242477Sache			    (struct disklabel *) addr,
5252477Sache			    0);
5262477Sache		}
527578Srgrimes	case DIOCWLABEL:
5286604Sache		if (!(cd->flags & MCDVALID))
5296604Sache			return ENXIO;
5302477Sache		return EBADF;
531578Srgrimes	case CDIOCPLAYTRACKS:
5326604Sache		if (!(cd->flags & MCDVALID))
5336604Sache			return ENXIO;
534578Srgrimes		return mcd_playtracks(unit, (struct ioc_play_track *) addr);
535578Srgrimes	case CDIOCPLAYBLOCKS:
5366604Sache		if (!(cd->flags & MCDVALID))
5376604Sache			return ENXIO;
5382477Sache		return EINVAL;
5392477Sache	case CDIOCPLAYMSF:
5406604Sache		if (!(cd->flags & MCDVALID))
5416604Sache			return ENXIO;
5422477Sache		return mcd_playmsf(unit, (struct ioc_play_msf *) addr);
543578Srgrimes	case CDIOCREADSUBCHANNEL:
5446604Sache		if (!(cd->flags & MCDVALID))
5456604Sache			return ENXIO;
546578Srgrimes		return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
547578Srgrimes	case CDIOREADTOCHEADER:
5486604Sache		if (!(cd->flags & MCDVALID))
5496604Sache			return ENXIO;
550578Srgrimes		return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
551578Srgrimes	case CDIOREADTOCENTRYS:
5526604Sache		if (!(cd->flags & MCDVALID))
5536604Sache			return ENXIO;
5542477Sache		return mcd_toc_entrys(unit, (struct ioc_read_toc_entry *) addr);
555578Srgrimes	case CDIOCSETPATCH:
556578Srgrimes	case CDIOCGETVOL:
557578Srgrimes	case CDIOCSETVOL:
558578Srgrimes	case CDIOCSETMONO:
559578Srgrimes	case CDIOCSETSTERIO:
560578Srgrimes	case CDIOCSETMUTE:
561578Srgrimes	case CDIOCSETLEFT:
562578Srgrimes	case CDIOCSETRIGHT:
563578Srgrimes		return EINVAL;
564578Srgrimes	case CDIOCRESUME:
5656604Sache		if (!(cd->flags & MCDVALID))
5666604Sache			return ENXIO;
567578Srgrimes		return mcd_resume(unit);
568578Srgrimes	case CDIOCPAUSE:
5696604Sache		if (!(cd->flags & MCDVALID))
5706604Sache			return ENXIO;
571578Srgrimes		return mcd_pause(unit);
572578Srgrimes	case CDIOCSTART:
5736604Sache		if (!(cd->flags & MCDVALID))
5746604Sache			return ENXIO;
575578Srgrimes		return EINVAL;
576578Srgrimes	case CDIOCSTOP:
5776604Sache		if (!(cd->flags & MCDVALID))
5786604Sache			return ENXIO;
579578Srgrimes		return mcd_stop(unit);
580578Srgrimes	case CDIOCEJECT:
5812477Sache		return mcd_eject(unit);
582578Srgrimes	case CDIOCSETDEBUG:
583578Srgrimes		cd->debug = 1;
584578Srgrimes		return 0;
585578Srgrimes	case CDIOCCLRDEBUG:
586578Srgrimes		cd->debug = 0;
587578Srgrimes		return 0;
588578Srgrimes	case CDIOCRESET:
5892477Sache		return mcd_hard_reset(unit);
5904398Sache	case CDIOCALLOW:
5916604Sache		return mcd_lock_door(unit, MCD_LK_UNLOCK);
592578Srgrimes	default:
593578Srgrimes		return ENOTTY;
594578Srgrimes	}
595578Srgrimes	/*NOTREACHED*/
596578Srgrimes}
597578Srgrimes
598578Srgrimes/* this could have been taken from scsi/cd.c, but it is not clear
599578Srgrimes * whether the scsi cd driver is linked in
600578Srgrimes */
601578Srgrimesstatic int mcd_getdisklabel(int unit)
602578Srgrimes{
603578Srgrimes	struct mcd_data *cd = mcd_data + unit;
604578Srgrimes
605578Srgrimes	if (cd->flags & MCDLABEL)
606578Srgrimes		return -1;
607578Srgrimes
608578Srgrimes	bzero(&cd->dlabel,sizeof(struct disklabel));
6094480Sache	/* filled with spaces first */
6104480Sache	strncpy(cd->dlabel.d_typename,"               ",
6114480Sache		sizeof(cd->dlabel.d_typename));
6124480Sache	strncpy(cd->dlabel.d_typename, cd->name,
6134480Sache		min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1));
6144480Sache	strncpy(cd->dlabel.d_packname,"unknown        ",
6154480Sache		sizeof(cd->dlabel.d_packname));
616578Srgrimes	cd->dlabel.d_secsize 	= cd->blksize;
617578Srgrimes	cd->dlabel.d_nsectors	= 100;
618578Srgrimes	cd->dlabel.d_ntracks	= 1;
619578Srgrimes	cd->dlabel.d_ncylinders	= (cd->disksize/100)+1;
620578Srgrimes	cd->dlabel.d_secpercyl	= 100;
621578Srgrimes	cd->dlabel.d_secperunit	= cd->disksize;
622578Srgrimes	cd->dlabel.d_rpm	= 300;
623578Srgrimes	cd->dlabel.d_interleave	= 1;
624578Srgrimes	cd->dlabel.d_flags	= D_REMOVABLE;
625578Srgrimes	cd->dlabel.d_npartitions= 1;
626578Srgrimes	cd->dlabel.d_partitions[0].p_offset = 0;
627578Srgrimes	cd->dlabel.d_partitions[0].p_size = cd->disksize;
628578Srgrimes	cd->dlabel.d_partitions[0].p_fstype = 9;
629578Srgrimes	cd->dlabel.d_magic	= DISKMAGIC;
630578Srgrimes	cd->dlabel.d_magic2	= DISKMAGIC;
631578Srgrimes	cd->dlabel.d_checksum	= dkcksum(&cd->dlabel);
632578Srgrimes
633578Srgrimes	cd->flags |= MCDLABEL;
634578Srgrimes	return 0;
635578Srgrimes}
636578Srgrimes
637578Srgrimesint mcdsize(dev_t dev)
638578Srgrimes{
639578Srgrimes	int size;
640578Srgrimes	int unit = mcd_unit(dev);
641578Srgrimes	struct mcd_data *cd = mcd_data + unit;
642578Srgrimes
6432477Sache	if (mcd_volinfo(unit) == 0) {
644578Srgrimes		cd->blksize = MCDBLK;
645578Srgrimes		size = msf2hsg(cd->volinfo.vol_msf);
646578Srgrimes		cd->disksize = size * (MCDBLK/DEV_BSIZE);
647578Srgrimes		return 0;
648578Srgrimes	}
649578Srgrimes	return -1;
650578Srgrimes}
651578Srgrimes
652578Srgrimes/***************************************************************
653578Srgrimes * lower level of driver starts here
654578Srgrimes **************************************************************/
655578Srgrimes
656578Srgrimes#ifdef NOTDEF
6571197Srgrimesstatic char
6581197Srgrimesirqs[] = {
659578Srgrimes	0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
660578Srgrimes	0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
661578Srgrimes};
662578Srgrimes
6631197Srgrimesstatic char
6641197Srgrimesdrqs[] = {
665578Srgrimes	0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
666578Srgrimes};
667578Srgrimes#endif
668578Srgrimes
6691197Srgrimesstatic void
6701197Srgrimesmcd_configure(struct mcd_data *cd)
671578Srgrimes{
672578Srgrimes	outb(cd->iobase+mcd_config,cd->config);
673578Srgrimes}
674578Srgrimes
6751197Srgrimes/* Wait for non-busy - return 0 on timeout */
6761197Srgrimesstatic int
6771197Srgrimestwiddle_thumbs(int port, int unit, int count, char *whine)
6781197Srgrimes{
6791197Srgrimes	int i;
6801197Srgrimes
6811197Srgrimes	for (i = 0; i < count; i++) {
6825226Sache		if (!(inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL))
6831197Srgrimes			return 1;
6841197Srgrimes		}
6851197Srgrimes	printf("mcd%d: timeout %s\n", unit, whine);
6861197Srgrimes	return 0;
6871197Srgrimes}
6881197Srgrimes
689978Sjkh/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
690578Srgrimes
6911197Srgrimesint
6921197Srgrimesmcd_probe(struct isa_device *dev)
693578Srgrimes{
694578Srgrimes	int port = dev->id_iobase;
695578Srgrimes	int unit = dev->id_unit;
6961197Srgrimes	int i, j;
6971197Srgrimes	int status;
6981197Srgrimes	unsigned char stbytes[3];
699578Srgrimes
700578Srgrimes	mcd_data[unit].flags = MCDPROBING;
701578Srgrimes
702578Srgrimes#ifdef NOTDEF
703578Srgrimes	/* get irq/drq configuration word */
704578Srgrimes	mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
705578Srgrimes#else
706578Srgrimes	mcd_data[unit].config = 0;
707578Srgrimes#endif
708578Srgrimes
709578Srgrimes	/* send a reset */
7101197Srgrimes	outb(port+MCD_FLAGS, M_RESET);
711578Srgrimes
7121197Srgrimes	/*
7131197Srgrimes	 * delay awhile by getting any pending garbage (old data) and
7141197Srgrimes	 * throwing it away.
7151197Srgrimes	 */
7162477Sache	for (i = 1000000; i != 0; i--)
7171197Srgrimes		inb(port+MCD_FLAGS);
718578Srgrimes
7191197Srgrimes	/* Get status */
7201197Srgrimes	outb(port+MCD_DATA, MCD_CMDGETSTAT);
7212477Sache	if (!twiddle_thumbs(port, unit, 1000000, "getting status"))
7221197Srgrimes		return 0;	/* Timeout */
7231197Srgrimes	/* Get version information */
7241197Srgrimes	outb(port+MCD_DATA, MCD_CMDCONTINFO);
7251197Srgrimes	for (j = 0; j < 3; j++) {
7262477Sache		if (!twiddle_thumbs(port, unit, 3000, "getting version info"))
7271197Srgrimes			return 0;
7281197Srgrimes		stbytes[j] = (inb(port+MCD_DATA) & 0xFF);
7291197Srgrimes	}
7305178Sache	if (stbytes[1] == stbytes[2])
7315178Sache		return 0;
7325226Sache	if (stbytes[2] >= 4 || stbytes[1] != 'M') {
7331197Srgrimes		outb(port+MCD_CTRL, M_PICKLE);
7342477Sache		mcd_data[unit].flags |= MCDNEWMODEL;
7351197Srgrimes	}
7366028Sache	mcd_data[unit].read_command = MCD_CMDSINGLESPEEDREAD;
7374480Sache	switch (stbytes[1]) {
7384480Sache	case 'M':
7394480Sache		if (mcd_data[unit].flags & MCDNEWMODEL)	{
7404480Sache			mcd_data[unit].type = MCD_TYPE_LU005S;
7414480Sache			mcd_data[unit].name = "Mitsumi LU005S";
7424480Sache		} else {
7434480Sache			mcd_data[unit].type = MCD_TYPE_LU002S;
7444480Sache			mcd_data[unit].name = "Mitsumi LU002S";
7454480Sache		}
7464480Sache		break;
7474480Sache	case 'F':
7484480Sache		mcd_data[unit].type = MCD_TYPE_FX001;
7494480Sache		mcd_data[unit].name = "Mitsumi FX001";
7504480Sache		break;
7514480Sache	case 'D':
7524480Sache		mcd_data[unit].type = MCD_TYPE_FX001D;
7534480Sache		mcd_data[unit].name = "Mitsumi FX001D";
7546028Sache		mcd_data[unit].read_command = MCD_CMDDOUBLESPEEDREAD;
7554480Sache		break;
7564480Sache	default:
7574480Sache		mcd_data[unit].type = MCD_TYPE_UNKNOWN;
7584480Sache		mcd_data[unit].name = "Mitsumi ???";
7594480Sache		break;
7604480Sache	}
7615226Sache	printf("mcd%d: type %s, version info: %c %x\n", unit, mcd_data[unit].name,
7625226Sache		stbytes[1], stbytes[2]);
7635226Sache
7641197Srgrimes	return 4;
765578Srgrimes}
766578Srgrimes
767978Sjkh
7681197Srgrimesstatic int
7691197Srgrimesmcd_waitrdy(int port,int dly)
770578Srgrimes{
771578Srgrimes	int i;
772578Srgrimes
7735226Sache	/* wait until flag port senses status ready */
7742477Sache	for (i=0; i<dly; i+=MIN_DELAY) {
7755226Sache		if (!(inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL))
776578Srgrimes			return 0;
7772477Sache		DELAY(MIN_DELAY);
778578Srgrimes	}
779578Srgrimes	return -1;
780578Srgrimes}
781578Srgrimes
7821197Srgrimesstatic int
7831197Srgrimesmcd_getreply(int unit,int dly)
784578Srgrimes{
785578Srgrimes	int	i;
786578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
787578Srgrimes	int	port = cd->iobase;
788578Srgrimes
789578Srgrimes	/* wait data to become ready */
790578Srgrimes	if (mcd_waitrdy(port,dly)<0) {
791578Srgrimes		printf("mcd%d: timeout getreply\n",unit);
792578Srgrimes		return -1;
793578Srgrimes	}
794578Srgrimes
795578Srgrimes	/* get the data */
796578Srgrimes	return inb(port+mcd_status) & 0xFF;
797578Srgrimes}
798578Srgrimes
7991197Srgrimesstatic int
8001197Srgrimesmcd_getstat(int unit,int sflg)
801578Srgrimes{
802578Srgrimes	int	i;
803578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
804578Srgrimes	int	port = cd->iobase;
805578Srgrimes
806578Srgrimes	/* get the status */
807578Srgrimes	if (sflg)
808578Srgrimes		outb(port+mcd_command, MCD_CMDGETSTAT);
809578Srgrimes	i = mcd_getreply(unit,DELAY_GETREPLY);
8106604Sache	if (i<0 || (i & MCD_ST_CMDCHECK)) {
8116604Sache		cd->curr_mode = MCD_MD_UNKNOWN;
8126604Sache		return -1;
8136604Sache	}
814578Srgrimes
815578Srgrimes	cd->status = i;
816578Srgrimes
8172477Sache	if (mcd_setflags(unit,cd) < 0)
8182477Sache		return -2;
819578Srgrimes	return cd->status;
820578Srgrimes}
821578Srgrimes
8222477Sachestatic int
8231197Srgrimesmcd_setflags(int unit, struct mcd_data *cd)
824578Srgrimes{
825578Srgrimes	/* check flags */
8262477Sache	if (    (cd->status & (MCDDSKCHNG|MCDDOOROPEN))
8272477Sache	    || !(cd->status & MCDDSKIN)) {
8282477Sache		MCD_TRACE("setflags: sensed DSKCHNG or DOOROPEN or !DSKIN\n",0,0,0,0);
8292477Sache		mcd_soft_reset(unit);
8302477Sache		return -1;
831578Srgrimes	}
832578Srgrimes
833578Srgrimes	if (cd->status & MCDAUDIOBSY)
834578Srgrimes		cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
835578Srgrimes	else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
836578Srgrimes		cd->audio_status = CD_AS_PLAY_COMPLETED;
8372477Sache	return 0;
838578Srgrimes}
839578Srgrimes
8401197Srgrimesstatic int
8411197Srgrimesmcd_get(int unit, char *buf, int nmax)
842578Srgrimes{
843578Srgrimes	int port = mcd_data[unit].iobase;
844578Srgrimes	int i,k;
845578Srgrimes
846578Srgrimes	for (i=0; i<nmax; i++) {
847578Srgrimes		/* wait for data */
848578Srgrimes		if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
849578Srgrimes			printf("mcd%d: timeout mcd_get\n",unit);
850578Srgrimes			return -1;
851578Srgrimes		}
852578Srgrimes		buf[i] = k;
853578Srgrimes	}
854578Srgrimes	return i;
855578Srgrimes}
856578Srgrimes
8571197Srgrimesstatic int
8581197Srgrimesmcd_send(int unit, int cmd,int nretrys)
859578Srgrimes{
8602477Sache	int i,k=0;
861578Srgrimes	int port = mcd_data[unit].iobase;
862578Srgrimes
8632477Sache/*MCD_TRACE("mcd_send: command = 0x%02x\n",cmd,0,0,0);*/
864578Srgrimes	for (i=0; i<nretrys; i++) {
865578Srgrimes		outb(port+mcd_command, cmd);
8662477Sache		if ((k=mcd_getstat(unit,0)) != -1)
867578Srgrimes			break;
868578Srgrimes	}
8692477Sache	if (k == -2) {
8702477Sache		printf("mcd%d: media changed\n",unit);
8712477Sache		return -1;
8722477Sache	}
873578Srgrimes	if (i == nretrys) {
874578Srgrimes		printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
875578Srgrimes		return -1;
876578Srgrimes	}
8772477Sache/*MCD_TRACE("mcd_send: done\n",0,0,0,0);*/
878578Srgrimes	return 0;
879578Srgrimes}
880578Srgrimes
8811197Srgrimesstatic int
8821197Srgrimesbcd2bin(bcd_t b)
883578Srgrimes{
884578Srgrimes	return (b >> 4) * 10 + (b & 15);
885578Srgrimes}
886578Srgrimes
8871197Srgrimesstatic bcd_t
8881197Srgrimesbin2bcd(int b)
889578Srgrimes{
890578Srgrimes	return ((b / 10) << 4) | (b % 10);
891578Srgrimes}
892578Srgrimes
8931197Srgrimesstatic void
8941197Srgrimeshsg2msf(int hsg, bcd_t *msf)
895578Srgrimes{
896578Srgrimes	hsg += 150;
897578Srgrimes	M_msf(msf) = bin2bcd(hsg / 4500);
898578Srgrimes	hsg %= 4500;
899578Srgrimes	S_msf(msf) = bin2bcd(hsg / 75);
900578Srgrimes	F_msf(msf) = bin2bcd(hsg % 75);
901578Srgrimes}
902578Srgrimes
9031197Srgrimesstatic int
9041197Srgrimesmsf2hsg(bcd_t *msf)
905578Srgrimes{
906578Srgrimes	return (bcd2bin(M_msf(msf)) * 60 +
907578Srgrimes		bcd2bin(S_msf(msf))) * 75 +
908578Srgrimes		bcd2bin(F_msf(msf)) - 150;
909578Srgrimes}
910578Srgrimes
9111197Srgrimesstatic int
9121197Srgrimesmcd_volinfo(int unit)
913578Srgrimes{
914578Srgrimes	struct mcd_data *cd = mcd_data + unit;
915578Srgrimes	int i;
916578Srgrimes
917578Srgrimes	/* Just return if we already have it */
918578Srgrimes	if (cd->flags & MCDVOLINFO) return 0;
919578Srgrimes
9202477Sache/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
9212477Sache
922578Srgrimes	/* send volume info command */
923578Srgrimes	if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
9242477Sache		return EIO;
925578Srgrimes
926578Srgrimes	/* get data */
927578Srgrimes	if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
928578Srgrimes		printf("mcd%d: mcd_volinfo: error read data\n",unit);
9292477Sache		return EIO;
930578Srgrimes	}
931578Srgrimes
9326604Sache	if (cd->volinfo.trk_low > 0 &&
9336604Sache	    cd->volinfo.trk_high >= cd->volinfo.trk_low
9346604Sache	   ) {
935578Srgrimes		cd->flags |= MCDVOLINFO;	/* volinfo is OK */
936578Srgrimes		return 0;
937578Srgrimes	}
938578Srgrimes
9392477Sache	return EINVAL;
940578Srgrimes}
941578Srgrimes
942798Swollmanvoid
943798Swollmanmcdintr(unit)
944798Swollman	int unit;
945578Srgrimes{
9462477Sache	MCD_TRACE("stray interrupt\n",0,0,0,0);
947578Srgrimes}
948578Srgrimes
949578Srgrimes/* state machine to process read requests
950578Srgrimes * initialize with MCD_S_BEGIN: calculate sizes, and read status
951578Srgrimes * MCD_S_WAITSTAT: wait for status reply, set mode
952578Srgrimes * MCD_S_WAITMODE: waits for status reply from set mode, set read command
953578Srgrimes * MCD_S_WAITREAD: wait for read ready, read data
954578Srgrimes */
955578Srgrimesstatic struct mcd_mbx *mbxsave;
956578Srgrimes
9571197Srgrimesstatic void
9581197Srgrimesmcd_doread(int state, struct mcd_mbx *mbxin)
959578Srgrimes{
960578Srgrimes	struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
961578Srgrimes	int	unit = mbx->unit;
962578Srgrimes	int	port = mbx->port;
9632477Sache	int     com_port = mbx->port + mcd_command;
9642477Sache	int     data_port = mbx->port + mcd_rdata;
965578Srgrimes	struct	buf *bp = mbx->bp;
966578Srgrimes	struct	mcd_data *cd = mcd_data + unit;
967578Srgrimes
968578Srgrimes	int	rm,i,k;
969578Srgrimes	struct mcd_read2 rbuf;
970578Srgrimes	int	blknum;
971578Srgrimes	caddr_t	addr;
972578Srgrimes
973578Srgrimesloop:
974578Srgrimes	switch (state) {
975578Srgrimes	case MCD_S_BEGIN:
976578Srgrimes		mbx = mbxsave = mbxin;
977578Srgrimes
978578Srgrimes	case MCD_S_BEGIN1:
9796604Sacheretry_status:
980578Srgrimes		/* get status */
9812477Sache		outb(com_port, MCD_CMDGETSTAT);
982578Srgrimes		mbx->count = RDELAY_WAITSTAT;
9831197Srgrimes		timeout((timeout_func_t)mcd_doread,
9841197Srgrimes			(caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
985578Srgrimes		return;
986578Srgrimes	case MCD_S_WAITSTAT:
9871000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT);
988578Srgrimes		if (mbx->count-- >= 0) {
9895226Sache			if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) {
9901197Srgrimes				timeout((timeout_func_t)mcd_doread,
9911197Srgrimes				    (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
992578Srgrimes				return;
993578Srgrimes			}
9942477Sache			cd->status = inb(port+mcd_status) & 0xFF;
9956028Sache			if (cd->status & MCD_ST_CMDCHECK)
9966028Sache				goto retry_status;
9972477Sache			if (mcd_setflags(unit,cd) < 0)
9982477Sache				goto changed;
9991197Srgrimes			MCD_TRACE("got WAITSTAT delay=%d\n",
10001197Srgrimes				RDELAY_WAITSTAT-mbx->count,0,0,0);
1001578Srgrimes			/* reject, if audio active */
1002578Srgrimes			if (cd->status & MCDAUDIOBSY) {
1003578Srgrimes				printf("mcd%d: audio is active\n",unit);
1004578Srgrimes				goto readerr;
1005578Srgrimes			}
10066604Sache
10076028Sacheretry_mode:
1008578Srgrimes			/* to check for raw/cooked mode */
1009578Srgrimes			if (cd->flags & MCDREADRAW) {
10104389Sache				rm = MCD_MD_RAW;
1011578Srgrimes				mbx->sz = MCDRBLK;
1012578Srgrimes			} else {
10134389Sache				rm = MCD_MD_COOKED;
1014578Srgrimes				mbx->sz = cd->blksize;
1015578Srgrimes			}
1016578Srgrimes
10172477Sache			if (rm == cd->curr_mode)
10182477Sache				goto modedone;
10192477Sache
1020578Srgrimes			mbx->count = RDELAY_WAITMODE;
10212477Sache
10222477Sache			cd->curr_mode = MCD_MD_UNKNOWN;
10232477Sache			mbx->mode = rm;
10242477Sache			mcd_put(com_port, MCD_CMDSETMODE);
10252477Sache			mcd_put(com_port, rm);
10262477Sache
10271197Srgrimes			timeout((timeout_func_t)mcd_doread,
10281197Srgrimes				(caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */
1029578Srgrimes			return;
1030578Srgrimes		} else {
1031578Srgrimes			printf("mcd%d: timeout getstatus\n",unit);
1032578Srgrimes			goto readerr;
1033578Srgrimes		}
1034578Srgrimes
1035578Srgrimes	case MCD_S_WAITMODE:
10361000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE);
1037578Srgrimes		if (mbx->count-- < 0) {
1038578Srgrimes			printf("mcd%d: timeout set mode\n",unit);
1039578Srgrimes			goto readerr;
1040578Srgrimes		}
10415226Sache		if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) {
1042798Swollman			timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100);
1043578Srgrimes			return;
1044578Srgrimes		}
10452477Sache		cd->status = inb(port+mcd_status) & 0xFF;
10466028Sache		if (cd->status & MCD_ST_CMDCHECK) {
10476604Sache			cd->curr_mode = MCD_MD_UNKNOWN;
10486028Sache			goto retry_mode;
10496028Sache		}
10502477Sache		if (mcd_setflags(unit,cd) < 0)
10512477Sache			goto changed;
10522477Sache		cd->curr_mode = mbx->mode;
10531197Srgrimes		MCD_TRACE("got WAITMODE delay=%d\n",
10541197Srgrimes			RDELAY_WAITMODE-mbx->count,0,0,0);
10552477Sachemodedone:
1056578Srgrimes		/* for first block */
1057578Srgrimes		mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
1058578Srgrimes		mbx->skip = 0;
1059578Srgrimes
1060578Srgrimesnextblock:
1061578Srgrimes		blknum 	= (bp->b_blkno / (mbx->sz/DEV_BSIZE))
1062578Srgrimes			+ mbx->p_offset + mbx->skip/mbx->sz;
1063578Srgrimes
10641197Srgrimes		MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n",
10651197Srgrimes			blknum,bp,0,0);
1066578Srgrimes
1067578Srgrimes		/* build parameter block */
1068578Srgrimes		hsg2msf(blknum,rbuf.start_msf);
10696028Sacheretry_read:
1070578Srgrimes		/* send the read command */
10712477Sache		disable_intr();
10726028Sache		mcd_put(com_port,cd->read_command);
10732477Sache		mcd_put(com_port,rbuf.start_msf[0]);
10742477Sache		mcd_put(com_port,rbuf.start_msf[1]);
10752477Sache		mcd_put(com_port,rbuf.start_msf[2]);
10762477Sache		mcd_put(com_port,0);
10772477Sache		mcd_put(com_port,0);
10782477Sache		mcd_put(com_port,1);
10792477Sache		enable_intr();
10802477Sache
10812762Sache		/* Spin briefly (<= 2ms) to avoid missing next block */
10822762Sache		for (i = 0; i < 20; i++) {
10835226Sache			k = inb(port+MCD_FLAGS);
10845226Sache			if (!(k & MFL_DATA_NOT_AVAIL))
10852762Sache				goto got_it;
10862762Sache			DELAY(100);
10872762Sache		}
10882762Sache
1089578Srgrimes		mbx->count = RDELAY_WAITREAD;
10901197Srgrimes		timeout((timeout_func_t)mcd_doread,
10911197Srgrimes			(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
1092578Srgrimes		return;
1093578Srgrimes	case MCD_S_WAITREAD:
10941000Sats		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD);
1095578Srgrimes		if (mbx->count-- > 0) {
10965226Sache			k = inb(port+MCD_FLAGS);
10975226Sache			if (!(k & MFL_DATA_NOT_AVAIL)) { /* XXX */
10981197Srgrimes				MCD_TRACE("got data delay=%d\n",
10991197Srgrimes					RDELAY_WAITREAD-mbx->count,0,0,0);
11002762Sache			got_it:
1101578Srgrimes				/* data is ready */
1102578Srgrimes				addr	= bp->b_un.b_addr + mbx->skip;
11032477Sache
1104578Srgrimes				outb(port+mcd_ctl2,0x04);	/* XXX */
1105578Srgrimes				for (i=0; i<mbx->sz; i++)
11062477Sache					*addr++ = inb(data_port);
1107578Srgrimes				outb(port+mcd_ctl2,0x0c);	/* XXX */
1108578Srgrimes
11095226Sache				k = inb(port+MCD_FLAGS);
11102477Sache				/* If we still have some junk, read it too */
11115226Sache				if (!(k & MFL_DATA_NOT_AVAIL)) {
11122477Sache					outb(port+mcd_ctl2,0x04);       /* XXX */
11132477Sache					(void)inb(data_port);
11142477Sache					(void)inb(data_port);
11152477Sache					outb(port+mcd_ctl2,0x0c);       /* XXX */
11162477Sache				}
11172477Sache
1118578Srgrimes				if (--mbx->nblk > 0) {
1119578Srgrimes					mbx->skip += mbx->sz;
1120578Srgrimes					goto nextblock;
1121578Srgrimes				}
1122578Srgrimes
1123578Srgrimes				/* return buffer */
1124578Srgrimes				bp->b_resid = 0;
1125578Srgrimes				biodone(bp);
1126578Srgrimes
11272477Sache				cd->flags &= ~(MCDMBXBSY|MCDREADRAW);
1128578Srgrimes				mcd_start(mbx->unit);
1129578Srgrimes				return;
1130578Srgrimes			}
11315226Sache			if (!(k & MFL_STATUS_NOT_AVAIL)) {
11322477Sache				cd->status = inb(port+mcd_status) & 0xFF;
11336028Sache				if (cd->status & MCD_ST_CMDCHECK)
11346028Sache					goto retry_read;
11352477Sache				if (mcd_setflags(unit,cd) < 0)
11362477Sache					goto changed;
11372477Sache			}
11381197Srgrimes			timeout((timeout_func_t)mcd_doread,
11391197Srgrimes				(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
1140578Srgrimes			return;
1141578Srgrimes		} else {
1142578Srgrimes			printf("mcd%d: timeout read data\n",unit);
1143578Srgrimes			goto readerr;
1144578Srgrimes		}
1145578Srgrimes	}
1146578Srgrimes
1147578Srgrimesreaderr:
1148578Srgrimes	if (mbx->retry-- > 0) {
1149578Srgrimes		printf("mcd%d: retrying\n",unit);
1150578Srgrimes		state = MCD_S_BEGIN1;
1151578Srgrimes		goto loop;
1152578Srgrimes	}
11532477Sacheharderr:
1154578Srgrimes	/* invalidate the buffer */
1155578Srgrimes	bp->b_flags |= B_ERROR;
1156578Srgrimes	bp->b_resid = bp->b_bcount;
1157578Srgrimes	biodone(bp);
11582477Sache
11592477Sache	cd->flags &= ~(MCDMBXBSY|MCDREADRAW);
1160578Srgrimes	mcd_start(mbx->unit);
1161578Srgrimes	return;
1162578Srgrimes
11632477Sachechanged:
11642477Sache	printf("mcd%d: media changed\n", unit);
11652477Sache	goto harderr;
11662477Sache
1167578Srgrimes#ifdef NOTDEF
1168578Srgrimes	printf("mcd%d: unit timeout, resetting\n",mbx->unit);
1169578Srgrimes	outb(mbx->port+mcd_reset,MCD_CMDRESET);
1170578Srgrimes	DELAY(300000);
1171578Srgrimes	(void)mcd_getstat(mbx->unit,1);
1172578Srgrimes	(void)mcd_getstat(mbx->unit,1);
1173578Srgrimes	/*cd->status &= ~MCDDSKCHNG; */
1174578Srgrimes	cd->debug = 1; /* preventive set debug mode */
1175578Srgrimes
1176578Srgrimes#endif
1177578Srgrimes
1178578Srgrimes}
1179578Srgrimes
11801197Srgrimesstatic int
11816604Sachemcd_lock_door(int unit, int lock)
11822477Sache{
11832477Sache	struct mcd_data *cd = mcd_data + unit;
11842477Sache	int port = cd->iobase;
11852477Sache
11866604Sache	outb(port+mcd_command, MCD_CMDLOCKDRV);
11876604Sache	outb(port+mcd_command, lock);
11886604Sache	if (mcd_getstat(unit,0) == -1)
11896604Sache		return EIO;
11906604Sache	return 0;
11916604Sache}
11926604Sache
11936604Sachestatic int
11946604Sachemcd_close_tray(int unit)
11956604Sache{
11966604Sache	struct mcd_data *cd = mcd_data + unit;
11976604Sache	int port = cd->iobase;
11986604Sache	int retry, r;
11996604Sache
12006604Sache	if (mcd_getstat(unit,1) == -1)
12016604Sache		return EIO;
12026604Sache	if (cd->status & MCDDOOROPEN) {
12036604Sache		outb(port+mcd_command, MCD_CMDCLOSETRAY);
12046604Sache		for (retry = 0; retry < CLOSE_TRAY_SECS * WAIT_FRAC; retry++) {
12056604Sache			if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL)
12066604Sache				(void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdcls", hz/WAIT_FRAC);
12076604Sache			else {
12086604Sache				if ((r = mcd_getstat(unit,0)) == -1)
12096604Sache					return EIO;
12106604Sache				return 0;
12116604Sache			}
12126604Sache		}
12136604Sache		return ENXIO;
12146604Sache	}
12156604Sache	return 0;
12166604Sache}
12176604Sache
12186604Sachestatic int
12196604Sachemcd_eject(int unit)
12206604Sache{
12216604Sache	struct mcd_data *cd = mcd_data + unit;
12226604Sache	int port = cd->iobase, r;
12236604Sache
12246604Sache	if (mcd_getstat(unit,1) == -1)    /* detect disk change too */
12256604Sache		return EIO;
12266604Sache	if (cd->status & MCDDOOROPEN)
12276604Sache		return mcd_close_tray(unit);
12286604Sache	if ((r = mcd_stop(unit)) == EIO)
12296604Sache		return r;
12302477Sache	outb(port+mcd_command, MCD_CMDEJECTDISK);
12316604Sache	if (mcd_getstat(unit,0) == -1)
12326604Sache		return EIO;
12332477Sache	return 0;
12342477Sache}
12352477Sache
12362477Sachestatic int
12372477Sachemcd_hard_reset(int unit)
12382477Sache{
12392477Sache	struct mcd_data *cd = mcd_data + unit;
12402477Sache	int port = cd->iobase;
12412477Sache
12422477Sache	outb(port+mcd_reset,MCD_CMDRESET);
12432477Sache	cd->curr_mode = MCD_MD_UNKNOWN;
12442477Sache	cd->audio_status = CD_AS_AUDIO_INVALID;
12452477Sache	return 0;
12462477Sache}
12472477Sache
12482477Sachestatic void
12492477Sachemcd_soft_reset(int unit)
12502477Sache{
12512477Sache	struct mcd_data *cd = mcd_data + unit;
12522477Sache	int i;
12532477Sache
12542477Sache	cd->openflags = 0;
12552477Sache	cd->flags &= (MCDINIT|MCDPROBING|MCDNEWMODEL);
12562477Sache	cd->curr_mode = MCD_MD_UNKNOWN;
12572477Sache	for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
12582477Sache	cd->audio_status = CD_AS_AUDIO_INVALID;
12592477Sache}
12602477Sache
12612477Sachestatic int
12621197Srgrimesmcd_setmode(int unit, int mode)
1263578Srgrimes{
1264578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1265578Srgrimes	int port = cd->iobase;
12662477Sache	int retry, st;
1267578Srgrimes
12682477Sache	if (cd->curr_mode == mode)
12692477Sache		return 0;
12702477Sache	if (cd->debug)
12712477Sache		printf("mcd%d: setting mode to %d\n", unit, mode);
1272578Srgrimes	for(retry=0; retry<MCD_RETRYS; retry++)
1273578Srgrimes	{
12742477Sache		cd->curr_mode = MCD_MD_UNKNOWN;
1275578Srgrimes		outb(port+mcd_command, MCD_CMDSETMODE);
1276578Srgrimes		outb(port+mcd_command, mode);
12772477Sache		if ((st = mcd_getstat(unit, 0)) >= 0) {
12782477Sache			cd->curr_mode = mode;
12792477Sache			return 0;
12802477Sache		}
12812477Sache		if (st == -2) {
12822477Sache			printf("mcd%d: media changed\n", unit);
12832477Sache			break;
12842477Sache		}
1285578Srgrimes	}
1286578Srgrimes
1287578Srgrimes	return -1;
1288578Srgrimes}
1289578Srgrimes
12901197Srgrimesstatic int
12911197Srgrimesmcd_toc_header(int unit, struct ioc_toc_header *th)
1292578Srgrimes{
1293578Srgrimes	struct mcd_data *cd = mcd_data + unit;
12942477Sache	int r;
1295578Srgrimes
12962477Sache	if ((r = mcd_volinfo(unit)) != 0)
12972477Sache		return r;
1298578Srgrimes
1299578Srgrimes	th->len = msf2hsg(cd->volinfo.vol_msf);
1300578Srgrimes	th->starting_track = bcd2bin(cd->volinfo.trk_low);
1301578Srgrimes	th->ending_track = bcd2bin(cd->volinfo.trk_high);
1302578Srgrimes
1303578Srgrimes	return 0;
1304578Srgrimes}
1305578Srgrimes
13061197Srgrimesstatic int
13071197Srgrimesmcd_read_toc(int unit)
1308578Srgrimes{
1309578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1310578Srgrimes	struct ioc_toc_header th;
1311578Srgrimes	struct mcd_qchninfo q;
1312578Srgrimes	int rc, trk, idx, retry;
1313578Srgrimes
1314578Srgrimes	/* Only read TOC if needed */
13152477Sache	if (cd->flags & MCDTOC)
13161197Srgrimes		return 0;
1317578Srgrimes
13182477Sache	if (cd->debug)
13192477Sache		printf("mcd%d: reading toc header\n", unit);
1320578Srgrimes
13212477Sache	if ((rc = mcd_toc_header(unit, &th)) != 0)
1322578Srgrimes		return rc;
1323578Srgrimes
13246665Sache	if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0)
13256665Sache		return EIO;
13266665Sache
13272477Sache	if (mcd_setmode(unit, MCD_MD_TOC) != 0)
1328578Srgrimes		return EIO;
1329578Srgrimes
13302477Sache	if (cd->debug)
13312477Sache		printf("mcd%d: get_toc reading qchannel info\n",unit);
13322477Sache
1333578Srgrimes	for(trk=th.starting_track; trk<=th.ending_track; trk++)
1334578Srgrimes		cd->toc[trk].idx_no = 0;
1335578Srgrimes	trk = th.ending_track - th.starting_track + 1;
13366612Sache	for(retry=0; retry<600 && trk>0; retry++)
1337578Srgrimes	{
1338578Srgrimes		if (mcd_getqchan(unit, &q) < 0) break;
1339578Srgrimes		idx = bcd2bin(q.idx_no);
13402477Sache		if (idx>=th.starting_track && idx<=th.ending_track && q.trk_no==0) {
13411197Srgrimes			if (cd->toc[idx].idx_no == 0) {
1342578Srgrimes				cd->toc[idx] = q;
1343578Srgrimes				trk--;
1344578Srgrimes			}
13451197Srgrimes		}
1346578Srgrimes	}
1347578Srgrimes
13482477Sache	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1349578Srgrimes		return EIO;
1350578Srgrimes
13512477Sache	if (trk != 0)
13521197Srgrimes		return ENXIO;
1353578Srgrimes
1354578Srgrimes	/* add a fake last+1 */
1355578Srgrimes	idx = th.ending_track + 1;
1356578Srgrimes	cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr;
1357578Srgrimes	cd->toc[idx].trk_no = 0;
1358578Srgrimes	cd->toc[idx].idx_no = 0xAA;
1359578Srgrimes	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
1360578Srgrimes	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
1361578Srgrimes	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
1362578Srgrimes
13632477Sache	if (cd->debug)
13642477Sache	{ int i;
13652477Sache	for (i = th.starting_track; i <= idx; i++)
13662477Sache		printf("mcd%d: trk %d idx %d pos %d %d %d\n",
13672477Sache			unit, i,
13682477Sache			cd->toc[i].idx_no,
13692477Sache			bcd2bin(cd->toc[i].hd_pos_msf[0]),
13702477Sache			bcd2bin(cd->toc[i].hd_pos_msf[1]),
13712477Sache			bcd2bin(cd->toc[i].hd_pos_msf[2]));
13722477Sache	}
13732477Sache
1374578Srgrimes	cd->flags |= MCDTOC;
1375578Srgrimes
1376578Srgrimes	return 0;
1377578Srgrimes}
1378578Srgrimes
13791197Srgrimesstatic int
13802477Sachemcd_toc_entrys(int unit, struct ioc_read_toc_entry *te)
1381578Srgrimes{
1382578Srgrimes	struct mcd_data *cd = mcd_data + unit;
13832477Sache	struct cd_toc_entry entries[MCD_MAXTOCS];
1384578Srgrimes	struct ioc_toc_header th;
13852477Sache	int rc, i, len = te->data_len;
1386578Srgrimes
1387578Srgrimes	/* Make sure we have a valid toc */
13882477Sache	if ((rc=mcd_read_toc(unit)) != 0)
1389578Srgrimes		return rc;
1390578Srgrimes
1391578Srgrimes	/* find the toc to copy*/
1392578Srgrimes	i = te->starting_track;
13932477Sache	if (i == MCD_LASTPLUS1)
1394578Srgrimes		i = bcd2bin(cd->volinfo.trk_high) + 1;
1395578Srgrimes
1396578Srgrimes	/* verify starting track */
1397578Srgrimes	if (i < bcd2bin(cd->volinfo.trk_low) ||
13981197Srgrimes		i > bcd2bin(cd->volinfo.trk_high)+1) {
1399578Srgrimes		return EINVAL;
14001197Srgrimes	}
1401578Srgrimes
1402578Srgrimes	/* do we have room */
14032477Sache	if (   len > sizeof(entries)
14042477Sache	    || len < sizeof(struct cd_toc_entry)
14052477Sache	    || (len % sizeof(struct cd_toc_entry)) != 0
14062477Sache	   )
14071197Srgrimes		return EINVAL;
1408578Srgrimes
1409578Srgrimes	/* Copy the toc header */
14102477Sache	if ((rc = mcd_toc_header(unit, &th)) != 0)
14112477Sache		return rc;
1412578Srgrimes
14132477Sache	do {
14142477Sache		/* copy the toc data */
14152477Sache		entries[i-1].control = cd->toc[i].ctrl_adr;
14162477Sache		entries[i-1].addr_type = te->address_format;
14172477Sache		entries[i-1].track = i;
14182477Sache		if (te->address_format == CD_MSF_FORMAT) {
14192477Sache			entries[i-1].addr.msf.unused = 0;
14202477Sache			entries[i-1].addr.msf.minute = bcd2bin(cd->toc[i].hd_pos_msf[0]);
14212477Sache			entries[i-1].addr.msf.second = bcd2bin(cd->toc[i].hd_pos_msf[1]);
14222477Sache			entries[i-1].addr.msf.frame = bcd2bin(cd->toc[i].hd_pos_msf[2]);
14232477Sache		}
14242477Sache		len -= sizeof(struct cd_toc_entry);
14252477Sache		i++;
1426578Srgrimes	}
14272477Sache	while (len > 0 && i <= th.ending_track + 2);
1428578Srgrimes
1429578Srgrimes	/* copy the data back */
14302477Sache	if (copyout(entries, te->data, (i - 1) * sizeof(struct cd_toc_entry)) != 0)
14312477Sache		return EFAULT;
1432578Srgrimes
1433578Srgrimes	return 0;
1434578Srgrimes}
1435578Srgrimes
14361197Srgrimesstatic int
14371197Srgrimesmcd_stop(int unit)
1438578Srgrimes{
1439578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1440578Srgrimes
14416604Sache	/* Verify current status */
14426604Sache	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS &&
14436604Sache	    cd->audio_status != CD_AS_PLAY_PAUSED) {
14446604Sache		if (cd->debug)
14456604Sache			printf("mcd%d: stop attempted when not playing\n", unit);
14466604Sache		return EINVAL;
14476604Sache	}
14486604Sache	if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
14496604Sache		if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0)
14506604Sache			return EIO;
1451578Srgrimes	cd->audio_status = CD_AS_PLAY_COMPLETED;
1452578Srgrimes	return 0;
1453578Srgrimes}
1454578Srgrimes
14551197Srgrimesstatic int
14561197Srgrimesmcd_getqchan(int unit, struct mcd_qchninfo *q)
1457578Srgrimes{
1458578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1459578Srgrimes
14602477Sache	if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0)
1461578Srgrimes		return -1;
14622477Sache	if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0)
1463578Srgrimes		return -1;
14641197Srgrimes	if (cd->debug) {
14656665Sache		printf("mcd%d: getqchan ctrl_adr=0x%x trk=%d ind=%d ttm=%d:%d.%d dtm=%d:%d.%d\n",
1466578Srgrimes		unit,
14672477Sache		q->ctrl_adr, bcd2bin(q->trk_no), bcd2bin(q->idx_no),
14684390Sache		bcd2bin(q->trk_size_msf[0]), bcd2bin(q->trk_size_msf[1]),
14694390Sache		bcd2bin(q->trk_size_msf[2]),
14702477Sache		bcd2bin(q->hd_pos_msf[0]), bcd2bin(q->hd_pos_msf[1]),
14712477Sache		bcd2bin(q->hd_pos_msf[2]));
14721197Srgrimes	}
1473578Srgrimes	return 0;
1474578Srgrimes}
1475578Srgrimes
14761197Srgrimesstatic int
14771197Srgrimesmcd_subchan(int unit, struct ioc_read_subchannel *sc)
1478578Srgrimes{
1479578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1480578Srgrimes	struct mcd_qchninfo q;
1481578Srgrimes	struct cd_sub_channel_info data;
1482578Srgrimes
14832477Sache	if (cd->debug)
14842477Sache		printf("mcd%d: subchan af=%d, df=%d\n", unit,
14852477Sache			sc->address_format,
14862477Sache			sc->data_format);
14872477Sache
14886665Sache	if (sc->address_format != CD_MSF_FORMAT &&
14896665Sache	    sc->address_format != CD_LBA_FORMAT)
14902477Sache		return EINVAL;
14912477Sache
14922477Sache	if (sc->data_format != CD_CURRENT_POSITION)
14932477Sache		return EINVAL;
14942477Sache
14952477Sache	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
14961197Srgrimes		return EIO;
14972477Sache
14982477Sache	if (mcd_getqchan(unit, &q) < 0)
14991197Srgrimes		return EIO;
1500578Srgrimes
1501578Srgrimes	data.header.audio_status = cd->audio_status;
15026665Sache	data.what.position.data_format = CD_CURRENT_POSITION;
15036665Sache	data.what.position.addr_type = q.ctrl_adr;
15046665Sache	data.what.position.control = q.ctrl_adr >> 4;
1505578Srgrimes	data.what.position.track_number = bcd2bin(q.trk_no);
15066665Sache	data.what.position.index_number = bcd2bin(q.idx_no);
15076665Sache	if (sc->address_format == CD_MSF_FORMAT) {
15086665Sache		data.what.position.reladdr.msf.unused = 0;
15096665Sache		data.what.position.reladdr.msf.minute = bcd2bin(q.trk_size_msf[0]);
15106665Sache		data.what.position.reladdr.msf.second = bcd2bin(q.trk_size_msf[1]);
15116665Sache		data.what.position.reladdr.msf.frame = bcd2bin(q.trk_size_msf[2]);
15126665Sache		data.what.position.absaddr.msf.unused = 0;
15136665Sache		data.what.position.absaddr.msf.minute = bcd2bin(q.hd_pos_msf[0]);
15146665Sache		data.what.position.absaddr.msf.second = bcd2bin(q.hd_pos_msf[1]);
15156665Sache		data.what.position.absaddr.msf.frame = bcd2bin(q.hd_pos_msf[2]);
15166665Sache	} else if (sc->address_format == CD_LBA_FORMAT) {
15176665Sache		data.what.position.reladdr.lba = msf2hsg(q.trk_size_msf);
15186665Sache		data.what.position.absaddr.lba = msf2hsg(q.hd_pos_msf);
15196665Sache	}
1520578Srgrimes
15214390Sache	if (copyout(&data, sc->data, min(sizeof(struct cd_sub_channel_info), sc->data_len))!=0)
1522578Srgrimes		return EFAULT;
1523578Srgrimes	return 0;
1524578Srgrimes}
1525578Srgrimes
15261197Srgrimesstatic int
15272477Sachemcd_playmsf(int unit, struct ioc_play_msf *pt)
15282477Sache{
15292477Sache	struct mcd_read2 pb;
15302477Sache
15312477Sache	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
15322477Sache		return EIO;
15332477Sache
15342477Sache	pb.start_msf[0] = bin2bcd(pt->start_m);
15352477Sache	pb.start_msf[1] = bin2bcd(pt->start_s);
15362477Sache	pb.start_msf[2] = bin2bcd(pt->start_f);
15372477Sache	pb.end_msf[0] = bin2bcd(pt->end_m);
15382477Sache	pb.end_msf[1] = bin2bcd(pt->end_s);
15392477Sache	pb.end_msf[2] = bin2bcd(pt->end_f);
15402477Sache
15412477Sache	return mcd_play(unit, &pb);
15422477Sache}
15432477Sache
15442477Sachestatic int
15451197Srgrimesmcd_playtracks(int unit, struct ioc_play_track *pt)
1546578Srgrimes{
1547578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1548578Srgrimes	struct mcd_read2 pb;
1549578Srgrimes	int a = pt->start_track;
1550578Srgrimes	int z = pt->end_track;
15514390Sache	int rc, i;
1552578Srgrimes
15532477Sache	if ((rc = mcd_read_toc(unit)) != 0)
15541197Srgrimes		return rc;
1555578Srgrimes
15562477Sache	if (cd->debug)
15572477Sache		printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
15582477Sache			a, pt->start_index, z, pt->end_index);
15592477Sache
15602477Sache	if (   a < bcd2bin(cd->volinfo.trk_low)
15612477Sache	    || a > bcd2bin(cd->volinfo.trk_high)
15622477Sache	    || a > z
15632477Sache	    || z < bcd2bin(cd->volinfo.trk_low)
15642477Sache	    || z > bcd2bin(cd->volinfo.trk_high))
1565578Srgrimes		return EINVAL;
1566578Srgrimes
15674390Sache	for (i = 0; i < 3; i++) {
15684390Sache		pb.start_msf[i] = cd->toc[a].hd_pos_msf[i];
15694390Sache		pb.end_msf[i] = cd->toc[z+1].hd_pos_msf[i];
15704390Sache	}
1571578Srgrimes
1572578Srgrimes	return mcd_play(unit, &pb);
1573578Srgrimes}
1574578Srgrimes
15751197Srgrimesstatic int
15761197Srgrimesmcd_play(int unit, struct mcd_read2 *pb)
1577578Srgrimes{
1578578Srgrimes	struct mcd_data *cd = mcd_data + unit;
15792477Sache	int com_port = cd->iobase + mcd_command;
15802477Sache	int retry, st = -1, status;
1581578Srgrimes
1582578Srgrimes	cd->lastpb = *pb;
15831197Srgrimes	for(retry=0; retry<MCD_RETRYS; retry++) {
15842477Sache
15852477Sache		disable_intr();
15866028Sache		outb(com_port, MCD_CMDSINGLESPEEDREAD);
15872477Sache		outb(com_port, pb->start_msf[0]);
15882477Sache		outb(com_port, pb->start_msf[1]);
15892477Sache		outb(com_port, pb->start_msf[2]);
15902477Sache		outb(com_port, pb->end_msf[0]);
15912477Sache		outb(com_port, pb->end_msf[1]);
15922477Sache		outb(com_port, pb->end_msf[2]);
15932477Sache		enable_intr();
15942477Sache
15952477Sache		status=mcd_getstat(unit, 0);
15962477Sache		if (status == -1)
15972477Sache			continue;
15982477Sache		else if (status != -2)
15992477Sache			st = 0;
16002477Sache		break;
1601578Srgrimes	}
1602578Srgrimes
16032477Sache	if (status == -2) {
16042477Sache		printf("mcd%d: media changed\n", unit);
16052477Sache		return ENXIO;
16061197Srgrimes	}
16072477Sache	if (cd->debug)
16082477Sache		printf("mcd%d: mcd_play retry=%d, status=0x%02x\n", unit, retry, status);
16092477Sache	if (st < 0)
16101197Srgrimes		return ENXIO;
1611578Srgrimes	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
1612578Srgrimes	return 0;
1613578Srgrimes}
1614578Srgrimes
16151197Srgrimesstatic int
16161197Srgrimesmcd_pause(int unit)
1617578Srgrimes{
1618578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1619578Srgrimes	struct mcd_qchninfo q;
1620578Srgrimes	int rc;
1621578Srgrimes
1622578Srgrimes	/* Verify current status */
16231197Srgrimes	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) {
16242477Sache		if (cd->debug)
16252477Sache			printf("mcd%d: pause attempted when not playing\n", unit);
1626578Srgrimes		return EINVAL;
1627578Srgrimes	}
1628578Srgrimes
1629578Srgrimes	/* Get the current position */
16302477Sache	if (mcd_getqchan(unit, &q) < 0)
16311197Srgrimes		return EIO;
1632578Srgrimes
1633578Srgrimes	/* Copy it into lastpb */
1634578Srgrimes	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
1635578Srgrimes	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
1636578Srgrimes	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
1637578Srgrimes
1638578Srgrimes	/* Stop playing */
16392477Sache	if ((rc=mcd_stop(unit)) != 0)
16401197Srgrimes		return rc;
1641578Srgrimes
1642578Srgrimes	/* Set the proper status and exit */
1643578Srgrimes	cd->audio_status = CD_AS_PLAY_PAUSED;
1644578Srgrimes	return 0;
1645578Srgrimes}
1646578Srgrimes
16471197Srgrimesstatic int
16481197Srgrimesmcd_resume(int unit)
1649578Srgrimes{
1650578Srgrimes	struct mcd_data *cd = mcd_data + unit;
1651578Srgrimes
16522477Sache	if (cd->audio_status != CD_AS_PLAY_PAUSED)
16531197Srgrimes		return EINVAL;
1654578Srgrimes	return mcd_play(unit, &cd->lastpb);
1655578Srgrimes}
1656578Srgrimes#endif /* NMCD > 0 */
1657