mcd.c revision 24203
1/*
2 * Copyright 1993 by Holger Veit (data part)
3 * Copyright 1993 by Brian Moore (audio part)
4 * Changes Copyright 1993 by Gary Clark II
5 * Changes Copyright (C) 1994-1995 by Andrey A. Chernov, Moscow, Russia
6 *
7 * Rewrote probe routine to work on newer Mitsumi drives.
8 * Additional changes (C) 1994 by Jordan K. Hubbard
9 *
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 *    must display the following acknowledgement:
22 *	This software was developed by Holger Veit and Brian Moore
23 *	for use with "386BSD" and similar operating systems.
24 *    "Similar operating systems" includes mainly non-profit oriented
25 *    systems for research and education, including but not restricted to
26 *    "NetBSD", "FreeBSD", "Mach" (by CMU).
27 * 4. Neither the name of the developer(s) nor the name "386BSD"
28 *    may be used to endorse or promote products derived from this
29 *    software without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
32 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
34 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE DEVELOPER(S) BE
35 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
36 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
37 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
38 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
39 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 *
43 *	$Id: mcd.c,v 1.86 1997/03/23 03:34:51 bde Exp $
44 */
45static const char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";
46
47#include "mcd.h"
48#if NMCD > 0
49#include <sys/types.h>
50#include <sys/param.h>
51#include <sys/systm.h>
52#include <sys/conf.h>
53#include <sys/fcntl.h>
54#include <sys/buf.h>
55#include <sys/proc.h>
56#include <sys/stat.h>
57#include <sys/uio.h>
58#include <sys/cdio.h>
59#include <sys/errno.h>
60#include <sys/dkbad.h>
61#include <sys/disklabel.h>
62#include <sys/kernel.h>
63#ifdef DEVFS
64#include <sys/devfsext.h>
65#endif /*DEVFS*/
66
67#include <machine/spl.h>
68#include <machine/clock.h>
69
70#include <i386/i386/cons.h>
71
72#include <i386/isa/isa_device.h>
73#include <i386/isa/mcdreg.h>
74
75#define	MCD_TRACE(format, args...)						\
76{									\
77	if (mcd_data[unit].debug) {					\
78		printf("mcd%d: status=0x%02x: ",			\
79			unit, mcd_data[unit].status);			\
80		printf(format, ## args);				\
81	}								\
82}
83
84#define mcd_part(dev)	((minor(dev)) & 7)
85#define mcd_unit(dev)	(((minor(dev)) & 0x38) >> 3)
86#define mcd_phys(dev)	(((minor(dev)) & 0x40) >> 6)
87#define RAW_PART        2
88
89/* flags */
90#define MCDVALID        0x0001  /* parameters loaded */
91#define MCDINIT         0x0002  /* device is init'd */
92#define MCDNEWMODEL     0x0004  /* device is new model */
93#define MCDLABEL        0x0008  /* label is read */
94#define MCDPROBING      0x0010  /* probing */
95#define MCDREADRAW      0x0020  /* read raw mode (2352 bytes) */
96#define MCDVOLINFO      0x0040  /* already read volinfo */
97#define MCDTOC          0x0080  /* already read toc */
98#define MCDMBXBSY       0x0100  /* local mbx is busy */
99
100/* status */
101#define	MCDAUDIOBSY	MCD_ST_AUDIOBSY		/* playing audio */
102#define MCDDSKCHNG	MCD_ST_DSKCHNG		/* sensed change of disk */
103#define MCDDSKIN	MCD_ST_DSKIN		/* sensed disk in drive */
104#define MCDDOOROPEN	MCD_ST_DOOROPEN		/* sensed door open */
105
106/* These are apparently the different states a mitsumi can get up to */
107#define MCDCDABSENT	0x0030
108#define MCDCDPRESENT	0x0020
109#define MCDSCLOSED	0x0080
110#define MCDSOPEN	0x00a0
111
112#define MCD_MD_UNKNOWN  (-1)
113
114/* toc */
115#define MCD_MAXTOCS	104	/* from the Linux driver */
116#define MCD_LASTPLUS1	170	/* special toc entry */
117
118#define	MCD_TYPE_UNKNOWN	0
119#define	MCD_TYPE_LU002S		1
120#define	MCD_TYPE_LU005S		2
121#define MCD_TYPE_LU006S         3
122#define MCD_TYPE_FX001          4
123#define MCD_TYPE_FX001D         5
124
125struct mcd_mbx {
126	short		unit;
127	short		port;
128	short		retry;
129	short		nblk;
130	int		sz;
131	u_long		skip;
132	struct buf	*bp;
133	int		p_offset;
134	short		count;
135	short           mode;
136};
137
138static struct mcd_data {
139	short	type;
140	char	*name;
141	short	config;
142	short	flags;
143	u_char	read_command;
144	short	status;
145	int	blksize;
146	u_long	disksize;
147	int	iobase;
148	struct disklabel dlabel;
149	int	partflags[MAXPARTITIONS];
150	int	openflags;
151	struct mcd_volinfo volinfo;
152	struct mcd_qchninfo toc[MCD_MAXTOCS];
153	short	audio_status;
154	short   curr_mode;
155	struct mcd_read2 lastpb;
156	short	debug;
157	struct buf_queue_head head;		/* head of buf queue */
158	struct mcd_mbx mbx;
159#ifdef	DEVFS
160	void *ra_devfs_token;		/* store the devfs handle here */
161	void *rc_devfs_token;		/* store the devfs handle here */
162	void *a_devfs_token;		/* store the devfs handle here */
163	void *c_devfs_token;		/* store the devfs handle here */
164#endif
165} mcd_data[NMCD];
166
167/* reader state machine */
168#define MCD_S_BEGIN	0
169#define MCD_S_BEGIN1	1
170#define MCD_S_WAITSTAT	2
171#define MCD_S_WAITMODE	3
172#define MCD_S_WAITREAD	4
173
174/* prototypes */
175static	void	mcd_start(int unit);
176static	int	mcd_getdisklabel(int unit);
177#ifdef NOTYET
178static	void	mcd_configure(struct mcd_data *cd);
179#endif
180static	int	mcd_get(int unit, char *buf, int nmax);
181static  int     mcd_setflags(int unit,struct mcd_data *cd);
182static	int	mcd_getstat(int unit,int sflg);
183static	int	mcd_send(int unit, int cmd,int nretrys);
184static	void	hsg2msf(int hsg, bcd_t *msf);
185static  int     msf2hsg(bcd_t *msf, int relative);
186static	int	mcd_volinfo(int unit);
187static	int	mcd_waitrdy(int port,int dly);
188static 	void	mcd_doread(int state, struct mcd_mbx *mbxin);
189static  void    mcd_soft_reset(int unit);
190static  int     mcd_hard_reset(int unit);
191static	int 	mcd_setmode(int unit, int mode);
192static	int	mcd_getqchan(int unit, struct mcd_qchninfo *q);
193static	int	mcd_subchan(int unit, struct ioc_read_subchannel *sc);
194static	int	mcd_toc_header(int unit, struct ioc_toc_header *th);
195static	int	mcd_read_toc(int unit);
196static  int     mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te);
197static	int	mcd_stop(int unit);
198static  int     mcd_eject(int unit);
199static  int     mcd_inject(int unit);
200static	int	mcd_playtracks(int unit, struct ioc_play_track *pt);
201static	int	mcd_play(int unit, struct mcd_read2 *pb);
202static  int     mcd_playmsf(int unit, struct ioc_play_msf *pt);
203static  int     mcd_playblocks(int unit, struct ioc_play_blocks *);
204static	int	mcd_pause(int unit);
205static	int	mcd_resume(int unit);
206static  int     mcd_lock_door(int unit, int lock);
207static  int     mcd_close_tray(int unit);
208
209static	int	mcd_probe(struct isa_device *dev);
210static	int	mcd_attach(struct isa_device *dev);
211struct	isa_driver	mcddriver = { mcd_probe, mcd_attach, "mcd" };
212
213static	d_open_t	mcdopen;
214static	d_close_t	mcdclose;
215static	d_ioctl_t	mcdioctl;
216static	d_psize_t	mcdsize;
217static	d_strategy_t	mcdstrategy;
218
219#define CDEV_MAJOR 29
220#define BDEV_MAJOR 7
221static struct cdevsw mcd_cdevsw;
222static struct bdevsw mcd_bdevsw =
223	{ mcdopen,	mcdclose,	mcdstrategy,	mcdioctl,	/*7*/
224	  nodump,	mcdsize,	0,	"mcd",	&mcd_cdevsw,	-1 };
225
226#define mcd_put(port,byte)	outb(port,byte)
227
228#define MCD_RETRYS	5
229#define MCD_RDRETRYS	8
230
231#define CLOSE_TRAY_SECS 8
232#define DISK_SENSE_SECS 3
233#define WAIT_FRAC 4
234
235/* several delays */
236#define RDELAY_WAITSTAT 300
237#define RDELAY_WAITMODE 300
238#define RDELAY_WAITREAD	800
239
240#define MIN_DELAY       15
241#define DELAY_GETREPLY  5000000
242
243int mcd_attach(struct isa_device *dev)
244{
245	int	unit = dev->id_unit;
246	struct mcd_data *cd = mcd_data + unit;
247
248	cd->iobase = dev->id_iobase;
249	cd->flags |= MCDINIT;
250	mcd_soft_reset(unit);
251	TAILQ_INIT(&cd->head);
252
253#ifdef NOTYET
254	/* wire controller for interrupts and dma */
255	mcd_configure(cd);
256#endif
257	/* name filled in probe */
258#ifdef DEVFS
259	cd->ra_devfs_token =
260		devfs_add_devswf(&mcd_cdevsw, dkmakeminor(unit, 0, 0),
261				 DV_CHR, UID_ROOT, GID_OPERATOR, 0640,
262				 "rmcd%da", unit);
263	cd->rc_devfs_token =
264		devfs_add_devswf(&mcd_cdevsw, dkmakeminor(unit, 0, RAW_PART),
265				 DV_CHR, UID_ROOT, GID_OPERATOR, 0640,
266				 "rmcd%dc", unit);
267	cd->a_devfs_token =
268		devfs_add_devswf(&mcd_bdevsw, dkmakeminor(unit, 0, 0),
269				 DV_BLK, UID_ROOT, GID_OPERATOR, 0640,
270				 "mcd%da", unit);
271	cd->c_devfs_token =
272		devfs_add_devswf(&mcd_bdevsw, dkmakeminor(unit, 0, RAW_PART),
273				 DV_BLK, UID_ROOT, GID_OPERATOR, 0640,
274				 "mcd%dc", unit);
275#endif
276	return 1;
277}
278
279int mcdopen(dev_t dev, int flags, int fmt, struct proc *p)
280{
281	int unit,part,phys,r,retry;
282	struct mcd_data *cd;
283
284	unit = mcd_unit(dev);
285	if (unit >= NMCD)
286		return ENXIO;
287
288	cd = mcd_data + unit;
289	part = mcd_part(dev);
290	phys = mcd_phys(dev);
291
292	/* not initialized*/
293	if (!(cd->flags & MCDINIT))
294		return ENXIO;
295
296	/* invalidated in the meantime? mark all open part's invalid */
297	if (!(cd->flags & MCDVALID) && cd->openflags)
298		return ENXIO;
299
300	if (mcd_getstat(unit,1) == -1)
301		return EIO;
302
303	if (    (cd->status & (MCDDSKCHNG|MCDDOOROPEN))
304	    || !(cd->status & MCDDSKIN))
305		for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) {
306			(void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdsn1", hz/WAIT_FRAC);
307			if ((r = mcd_getstat(unit,1)) == -1)
308				return EIO;
309			if (r != -2)
310				break;
311		}
312
313	if ((   (cd->status & (MCDDOOROPEN|MCDDSKCHNG))
314	     || !(cd->status & MCDDSKIN)
315	    )
316	    && major(dev) == CDEV_MAJOR && part == RAW_PART
317	   ) {
318		cd->openflags |= (1<<part);
319		if (phys)
320			cd->partflags[part] |= MCDREADRAW;
321		return 0;
322	}
323	if (cd->status & MCDDOOROPEN) {
324		printf("mcd%d: door is open\n", unit);
325		return ENXIO;
326	}
327	if (!(cd->status & MCDDSKIN)) {
328		printf("mcd%d: no CD inside\n", unit);
329		return ENXIO;
330	}
331	if (cd->status & MCDDSKCHNG) {
332		printf("mcd%d: CD not sensed\n", unit);
333		return ENXIO;
334	}
335
336	if (mcdsize(dev) < 0) {
337		if (major(dev) == CDEV_MAJOR && part == RAW_PART) {
338			cd->openflags |= (1<<part);
339			if (phys)
340				cd->partflags[part] |= MCDREADRAW;
341			return 0;
342		}
343		printf("mcd%d: failed to get disk size\n",unit);
344		return ENXIO;
345	} else
346		cd->flags |= MCDVALID;
347
348	/* XXX get a default disklabel */
349	mcd_getdisklabel(unit);
350
351MCD_TRACE("open: partition=%d, disksize = %ld, blksize=%d\n",
352	part, cd->disksize, cd->blksize);
353
354	if (part == RAW_PART ||
355		(part < cd->dlabel.d_npartitions &&
356		cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) {
357		cd->openflags |= (1<<part);
358		if (part == RAW_PART && phys)
359			cd->partflags[part] |= MCDREADRAW;
360		(void) mcd_lock_door(unit, MCD_LK_LOCK);
361		if (!(cd->flags & MCDVALID))
362			return ENXIO;
363		return 0;
364	}
365
366	return ENXIO;
367}
368
369int mcdclose(dev_t dev, int flags, int fmt, struct proc *p)
370{
371	int unit,part;
372	struct mcd_data *cd;
373
374	unit = mcd_unit(dev);
375	if (unit >= NMCD)
376		return ENXIO;
377
378	cd = mcd_data + unit;
379	part = mcd_part(dev);
380
381	if (!(cd->flags & MCDINIT) || !(cd->openflags & (1<<part)))
382		return ENXIO;
383
384	MCD_TRACE("close: partition=%d\n", part);
385
386	(void) mcd_lock_door(unit, MCD_LK_UNLOCK);
387	cd->openflags &= ~(1<<part);
388	cd->partflags[part] &= ~MCDREADRAW;
389
390	return 0;
391}
392
393void
394mcdstrategy(struct buf *bp)
395{
396	struct mcd_data *cd;
397	int s;
398
399	int unit = mcd_unit(bp->b_dev);
400
401	cd = mcd_data + unit;
402
403	/* test validity */
404/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n",
405	bp,unit,bp->b_blkno,bp->b_bcount);*/
406	if (unit >= NMCD || bp->b_blkno < 0) {
407		printf("mcdstrategy: unit = %d, blkno = %ld, bcount = %ld\n",
408			unit, bp->b_blkno, bp->b_bcount);
409		printf("mcd: mcdstratregy failure");
410		bp->b_error = EINVAL;
411		bp->b_flags |= B_ERROR;
412		goto bad;
413	}
414
415	/* if device invalidated (e.g. media change, door open), error */
416	if (!(cd->flags & MCDVALID)) {
417MCD_TRACE("strategy: drive not valid\n");
418		bp->b_error = EIO;
419		goto bad;
420	}
421
422	/* read only */
423	if (!(bp->b_flags & B_READ)) {
424		bp->b_error = EROFS;
425		goto bad;
426	}
427
428	/* no data to read */
429	if (bp->b_bcount == 0)
430		goto done;
431
432	/* for non raw access, check partition limits */
433	if (mcd_part(bp->b_dev) != RAW_PART) {
434		if (!(cd->flags & MCDLABEL)) {
435			bp->b_error = EIO;
436			goto bad;
437		}
438		/* adjust transfer if necessary */
439		if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) {
440			goto done;
441		}
442	} else {
443		bp->b_pblkno = bp->b_blkno;
444		bp->b_resid = 0;
445	}
446
447	/* queue it */
448	s = splbio();
449	tqdisksort(&cd->head, bp);
450	splx(s);
451
452	/* now check whether we can perform processing */
453	mcd_start(unit);
454	return;
455
456bad:
457	bp->b_flags |= B_ERROR;
458done:
459	bp->b_resid = bp->b_bcount;
460	biodone(bp);
461	return;
462}
463
464static void mcd_start(int unit)
465{
466	struct mcd_data *cd = mcd_data + unit;
467	struct partition *p;
468	struct buf *bp;
469	register s = splbio();
470
471	if (cd->flags & MCDMBXBSY) {
472		splx(s);
473		return;
474	}
475
476	bp = TAILQ_FIRST(&cd->head);
477	if (bp != 0) {
478		/* block found to process, dequeue */
479		/*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/
480		TAILQ_REMOVE(&cd->head, bp, b_act);
481		splx(s);
482	} else {
483		/* nothing to do */
484		splx(s);
485		return;
486	}
487
488	/* changed media? */
489	if (!(cd->flags	& MCDVALID)) {
490		MCD_TRACE("mcd_start: drive not valid\n");
491		return;
492	}
493
494	p = cd->dlabel.d_partitions + mcd_part(bp->b_dev);
495
496	cd->flags |= MCDMBXBSY;
497	if (cd->partflags[mcd_part(bp->b_dev)] & MCDREADRAW)
498		cd->flags |= MCDREADRAW;
499	cd->mbx.unit = unit;
500	cd->mbx.port = cd->iobase;
501	cd->mbx.retry = MCD_RETRYS;
502	cd->mbx.bp = bp;
503	cd->mbx.p_offset = p->p_offset;
504
505	/* calling the read routine */
506	mcd_doread(MCD_S_BEGIN,&(cd->mbx));
507	/* triggers mcd_start, when successful finished */
508	return;
509}
510
511int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags, struct proc *p)
512{
513	struct mcd_data *cd;
514	int unit,part,retry,r;
515
516	unit = mcd_unit(dev);
517	part = mcd_part(dev);
518	cd = mcd_data + unit;
519
520	if (mcd_getstat(unit, 1) == -1) /* detect disk change too */
521		return EIO;
522MCD_TRACE("ioctl called 0x%x\n", cmd);
523
524	switch (cmd) {
525	case CDIOCSETPATCH:
526	case CDIOCGETVOL:
527	case CDIOCSETVOL:
528	case CDIOCSETMONO:
529	case CDIOCSETSTERIO:
530	case CDIOCSETMUTE:
531	case CDIOCSETLEFT:
532	case CDIOCSETRIGHT:
533		return EINVAL;
534	case CDIOCEJECT:
535		return mcd_eject(unit);
536	case CDIOCSETDEBUG:
537		cd->debug = 1;
538		return 0;
539	case CDIOCCLRDEBUG:
540		cd->debug = 0;
541		return 0;
542	case CDIOCRESET:
543		return mcd_hard_reset(unit);
544	case CDIOCALLOW:
545		return mcd_lock_door(unit, MCD_LK_UNLOCK);
546	case CDIOCPREVENT:
547		return mcd_lock_door(unit, MCD_LK_LOCK);
548	case CDIOCCLOSE:
549		return mcd_inject(unit);
550	}
551
552	if (!(cd->flags & MCDVALID)) {
553		if (   major(dev) != CDEV_MAJOR
554		    || part != RAW_PART
555		    || !(cd->openflags & (1<<RAW_PART))
556		   )
557			return ENXIO;
558		if (    (cd->status & (MCDDSKCHNG|MCDDOOROPEN))
559		    || !(cd->status & MCDDSKIN))
560			for (retry = 0; retry < DISK_SENSE_SECS * WAIT_FRAC; retry++) {
561				(void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdsn2", hz/WAIT_FRAC);
562				if ((r = mcd_getstat(unit,1)) == -1)
563					return EIO;
564				if (r != -2)
565					break;
566			}
567		if (   (cd->status & (MCDDOOROPEN|MCDDSKCHNG))
568		    || !(cd->status & MCDDSKIN)
569		    || mcdsize(dev) < 0
570		   )
571			return ENXIO;
572		cd->flags |= MCDVALID;
573		mcd_getdisklabel(unit);
574		if (mcd_phys(dev))
575			cd->partflags[part] |= MCDREADRAW;
576		(void) mcd_lock_door(unit, MCD_LK_LOCK);
577		if (!(cd->flags & MCDVALID))
578			return ENXIO;
579	}
580
581	switch (cmd) {
582	case DIOCSBAD:
583		return EINVAL;
584	case DIOCGDINFO:
585		*(struct disklabel *) addr = cd->dlabel;
586		return 0;
587	case DIOCGPART:
588		((struct partinfo *) addr)->disklab = &cd->dlabel;
589		((struct partinfo *) addr)->part =
590		    &cd->dlabel.d_partitions[mcd_part(dev)];
591		return 0;
592
593		/*
594		 * a bit silly, but someone might want to test something on a
595		 * section of cdrom.
596		 */
597	case DIOCWDINFO:
598	case DIOCSDINFO:
599		if ((flags & FWRITE) == 0)
600			return EBADF;
601		else {
602			return setdisklabel(&cd->dlabel,
603			    (struct disklabel *) addr,
604			    0);
605		}
606	case DIOCWLABEL:
607		return EBADF;
608	case CDIOCPLAYTRACKS:
609		return mcd_playtracks(unit, (struct ioc_play_track *) addr);
610	case CDIOCPLAYBLOCKS:
611		return mcd_playblocks(unit, (struct ioc_play_blocks *) addr);
612	case CDIOCPLAYMSF:
613		return mcd_playmsf(unit, (struct ioc_play_msf *) addr);
614	case CDIOCREADSUBCHANNEL:
615		return mcd_subchan(unit, (struct ioc_read_subchannel *) addr);
616	case CDIOREADTOCHEADER:
617		return mcd_toc_header(unit, (struct ioc_toc_header *) addr);
618	case CDIOREADTOCENTRYS:
619		return mcd_toc_entrys(unit, (struct ioc_read_toc_entry *) addr);
620	case CDIOCRESUME:
621		return mcd_resume(unit);
622	case CDIOCPAUSE:
623		return mcd_pause(unit);
624	case CDIOCSTART:
625		if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
626			return EIO;
627		return 0;
628	case CDIOCSTOP:
629		return mcd_stop(unit);
630	default:
631		return ENOTTY;
632	}
633	/*NOTREACHED*/
634}
635
636/* this could have been taken from scsi/cd.c, but it is not clear
637 * whether the scsi cd driver is linked in
638 */
639static int mcd_getdisklabel(int unit)
640{
641	struct mcd_data *cd = mcd_data + unit;
642
643	if (cd->flags & MCDLABEL)
644		return -1;
645
646	bzero(&cd->dlabel,sizeof(struct disklabel));
647	/* filled with spaces first */
648	strncpy(cd->dlabel.d_typename,"               ",
649		sizeof(cd->dlabel.d_typename));
650	strncpy(cd->dlabel.d_typename, cd->name,
651		min(strlen(cd->name), sizeof(cd->dlabel.d_typename) - 1));
652	strncpy(cd->dlabel.d_packname,"unknown        ",
653		sizeof(cd->dlabel.d_packname));
654	cd->dlabel.d_secsize 	= cd->blksize;
655	cd->dlabel.d_nsectors	= 100;
656	cd->dlabel.d_ntracks	= 1;
657	cd->dlabel.d_ncylinders	= (cd->disksize/100)+1;
658	cd->dlabel.d_secpercyl	= 100;
659	cd->dlabel.d_secperunit	= cd->disksize;
660	cd->dlabel.d_rpm	= 300;
661	cd->dlabel.d_interleave	= 1;
662	cd->dlabel.d_flags	= D_REMOVABLE;
663	cd->dlabel.d_npartitions= 1;
664	cd->dlabel.d_partitions[0].p_offset = 0;
665	cd->dlabel.d_partitions[0].p_size = cd->disksize;
666	cd->dlabel.d_partitions[0].p_fstype = 9;
667	cd->dlabel.d_magic	= DISKMAGIC;
668	cd->dlabel.d_magic2	= DISKMAGIC;
669	cd->dlabel.d_checksum	= dkcksum(&cd->dlabel);
670
671	cd->flags |= MCDLABEL;
672	return 0;
673}
674
675int mcdsize(dev_t dev)
676{
677	int size;
678	int unit = mcd_unit(dev);
679	struct mcd_data *cd = mcd_data + unit;
680
681	if (mcd_volinfo(unit) == 0) {
682		cd->blksize = MCDBLK;
683		size = msf2hsg(cd->volinfo.vol_msf, 0);
684		cd->disksize = size * (MCDBLK/DEV_BSIZE);
685		return 0;
686	}
687	return -1;
688}
689
690/***************************************************************
691 * lower level of driver starts here
692 **************************************************************/
693
694#ifdef NOTDEF
695static char
696irqs[] = {
697	0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00,
698	0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00
699};
700
701static char
702drqs[] = {
703	0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07,
704};
705#endif
706
707#ifdef NOT_YET
708static void
709mcd_configure(struct mcd_data *cd)
710{
711	outb(cd->iobase+mcd_config,cd->config);
712}
713#endif
714
715/* Wait for non-busy - return 0 on timeout */
716static int
717twiddle_thumbs(int port, int unit, int count, char *whine)
718{
719	int i;
720
721	for (i = 0; i < count; i++) {
722		if (!(inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL))
723			return 1;
724		}
725	if (bootverbose)
726		printf("mcd%d: timeout %s\n", unit, whine);
727	return 0;
728}
729
730/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */
731
732int
733mcd_probe(struct isa_device *dev)
734{
735	int port = dev->id_iobase;
736	int unit = dev->id_unit;
737	int i, j;
738	unsigned char stbytes[3];
739
740	mcd_data[unit].flags = MCDPROBING;
741
742#ifdef NOTDEF
743	/* get irq/drq configuration word */
744	mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/
745#else
746	mcd_data[unit].config = 0;
747#endif
748
749	/* send a reset */
750	outb(port+MCD_FLAGS, M_RESET);
751
752	/*
753	 * delay awhile by getting any pending garbage (old data) and
754	 * throwing it away.
755	 */
756	for (i = 1000000; i != 0; i--)
757		inb(port+MCD_FLAGS);
758
759	/* Get status */
760	outb(port+MCD_DATA, MCD_CMDGETSTAT);
761	if (!twiddle_thumbs(port, unit, 1000000, "getting status"))
762		return 0;	/* Timeout */
763	/* Get version information */
764	outb(port+MCD_DATA, MCD_CMDCONTINFO);
765	for (j = 0; j < 3; j++) {
766		if (!twiddle_thumbs(port, unit, 3000, "getting version info"))
767			return 0;
768		stbytes[j] = (inb(port+MCD_DATA) & 0xFF);
769	}
770	if (stbytes[1] == stbytes[2])
771		return 0;
772	if (stbytes[2] >= 4 || stbytes[1] != 'M') {
773		outb(port+MCD_CTRL, M_PICKLE);
774		mcd_data[unit].flags |= MCDNEWMODEL;
775	}
776	mcd_data[unit].read_command = MCD_CMDSINGLESPEEDREAD;
777	switch (stbytes[1]) {
778	case 'M':
779		if (stbytes[2] <= 2) {
780			mcd_data[unit].type = MCD_TYPE_LU002S;
781			mcd_data[unit].name = "Mitsumi LU002S";
782		} else if (stbytes[2] <= 5) {
783			mcd_data[unit].type = MCD_TYPE_LU005S;
784			mcd_data[unit].name = "Mitsumi LU005S";
785		} else {
786			mcd_data[unit].type = MCD_TYPE_LU006S;
787			mcd_data[unit].name = "Mitsumi LU006S";
788		}
789		break;
790	case 'F':
791		mcd_data[unit].type = MCD_TYPE_FX001;
792		mcd_data[unit].name = "Mitsumi FX001";
793		break;
794	case 'D':
795		mcd_data[unit].type = MCD_TYPE_FX001D;
796		mcd_data[unit].name = "Mitsumi FX001D";
797		mcd_data[unit].read_command = MCD_CMDDOUBLESPEEDREAD;
798		break;
799	default:
800		mcd_data[unit].type = MCD_TYPE_UNKNOWN;
801		mcd_data[unit].name = "Mitsumi ???";
802		break;
803	}
804	printf("mcd%d: type %s, version info: %c %x\n", unit, mcd_data[unit].name,
805		stbytes[1], stbytes[2]);
806
807	return 4;
808}
809
810
811static int
812mcd_waitrdy(int port,int dly)
813{
814	int i;
815
816	/* wait until flag port senses status ready */
817	for (i=0; i<dly; i+=MIN_DELAY) {
818		if (!(inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL))
819			return 0;
820		DELAY(MIN_DELAY);
821	}
822	return -1;
823}
824
825static int
826mcd_getreply(int unit,int dly)
827{
828	struct	mcd_data *cd = mcd_data + unit;
829	int	port = cd->iobase;
830
831	/* wait data to become ready */
832	if (mcd_waitrdy(port,dly)<0) {
833		printf("mcd%d: timeout getreply\n",unit);
834		return -1;
835	}
836
837	/* get the data */
838	return inb(port+mcd_status) & 0xFF;
839}
840
841static int
842mcd_getstat(int unit,int sflg)
843{
844	int	i;
845	struct	mcd_data *cd = mcd_data + unit;
846	int	port = cd->iobase;
847
848	/* get the status */
849	if (sflg)
850		outb(port+mcd_command, MCD_CMDGETSTAT);
851	i = mcd_getreply(unit,DELAY_GETREPLY);
852	if (i<0 || (i & MCD_ST_CMDCHECK)) {
853		cd->curr_mode = MCD_MD_UNKNOWN;
854		return -1;
855	}
856
857	cd->status = i;
858
859	if (mcd_setflags(unit,cd) < 0)
860		return -2;
861	return cd->status;
862}
863
864static int
865mcd_setflags(int unit, struct mcd_data *cd)
866{
867	/* check flags */
868	if (    (cd->status & (MCDDSKCHNG|MCDDOOROPEN))
869	    || !(cd->status & MCDDSKIN)) {
870		MCD_TRACE("setflags: sensed DSKCHNG or DOOROPEN or !DSKIN\n");
871		mcd_soft_reset(unit);
872		return -1;
873	}
874
875	if (cd->status & MCDAUDIOBSY)
876		cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
877	else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
878		cd->audio_status = CD_AS_PLAY_COMPLETED;
879	return 0;
880}
881
882static int
883mcd_get(int unit, char *buf, int nmax)
884{
885	int i,k;
886
887	for (i=0; i<nmax; i++) {
888		/* wait for data */
889		if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) {
890			printf("mcd%d: timeout mcd_get\n",unit);
891			return -1;
892		}
893		buf[i] = k;
894	}
895	return i;
896}
897
898static int
899mcd_send(int unit, int cmd,int nretrys)
900{
901	int i,k=0;
902	int port = mcd_data[unit].iobase;
903
904/*MCD_TRACE("mcd_send: command = 0x%02x\n",cmd,0,0,0);*/
905	for (i=0; i<nretrys; i++) {
906		outb(port+mcd_command, cmd);
907		if ((k=mcd_getstat(unit,0)) != -1)
908			break;
909	}
910	if (k == -2) {
911		printf("mcd%d: media changed\n",unit);
912		return -1;
913	}
914	if (i == nretrys) {
915		printf("mcd%d: mcd_send retry cnt exceeded\n",unit);
916		return -1;
917	}
918/*MCD_TRACE("mcd_send: done\n",0,0,0,0);*/
919	return 0;
920}
921
922static void
923hsg2msf(int hsg, bcd_t *msf)
924{
925	hsg += 150;
926	F_msf(msf) = bin2bcd(hsg % 75);
927	hsg /= 75;
928	S_msf(msf) = bin2bcd(hsg % 60);
929	hsg /= 60;
930	M_msf(msf) = bin2bcd(hsg);
931}
932
933static int
934msf2hsg(bcd_t *msf, int relative)
935{
936	return (bcd2bin(M_msf(msf)) * 60 + bcd2bin(S_msf(msf))) * 75 +
937		bcd2bin(F_msf(msf)) - (!relative) * 150;
938}
939
940static int
941mcd_volinfo(int unit)
942{
943	struct mcd_data *cd = mcd_data + unit;
944
945	/* Just return if we already have it */
946	if (cd->flags & MCDVOLINFO) return 0;
947
948/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/
949
950	/* send volume info command */
951	if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0)
952		return EIO;
953
954	/* get data */
955	if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) {
956		printf("mcd%d: mcd_volinfo: error read data\n",unit);
957		return EIO;
958	}
959
960	if (cd->volinfo.trk_low > 0 &&
961	    cd->volinfo.trk_high >= cd->volinfo.trk_low
962	   ) {
963		cd->flags |= MCDVOLINFO;	/* volinfo is OK */
964		return 0;
965	}
966
967	return EINVAL;
968}
969
970void
971mcdintr(unit)
972	int unit;
973{
974	MCD_TRACE("stray interrupt\n");
975}
976
977/* state machine to process read requests
978 * initialize with MCD_S_BEGIN: calculate sizes, and read status
979 * MCD_S_WAITSTAT: wait for status reply, set mode
980 * MCD_S_WAITMODE: waits for status reply from set mode, set read command
981 * MCD_S_WAITREAD: wait for read ready, read data
982 */
983static struct mcd_mbx *mbxsave;
984
985static void
986mcd_doread(int state, struct mcd_mbx *mbxin)
987{
988	struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin;
989	int	unit = mbx->unit;
990	int	port = mbx->port;
991	int     com_port = mbx->port + mcd_command;
992	int     data_port = mbx->port + mcd_rdata;
993	struct	buf *bp = mbx->bp;
994	struct	mcd_data *cd = mcd_data + unit;
995
996	int	rm,i,k;
997	struct mcd_read2 rbuf;
998	int	blknum;
999	caddr_t	addr;
1000
1001loop:
1002	switch (state) {
1003	case MCD_S_BEGIN:
1004		mbx = mbxsave = mbxin;
1005
1006	case MCD_S_BEGIN1:
1007retry_status:
1008		/* get status */
1009		outb(com_port, MCD_CMDGETSTAT);
1010		mbx->count = RDELAY_WAITSTAT;
1011		timeout((timeout_func_t)mcd_doread,
1012			(caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
1013		return;
1014	case MCD_S_WAITSTAT:
1015		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT);
1016		if (mbx->count-- >= 0) {
1017			if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) {
1018				timeout((timeout_func_t)mcd_doread,
1019				    (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */
1020				return;
1021			}
1022			cd->status = inb(port+mcd_status) & 0xFF;
1023			if (cd->status & MCD_ST_CMDCHECK)
1024				goto retry_status;
1025			if (mcd_setflags(unit,cd) < 0)
1026				goto changed;
1027			MCD_TRACE("got WAITSTAT delay=%d\n",
1028				RDELAY_WAITSTAT-mbx->count);
1029			/* reject, if audio active */
1030			if (cd->status & MCDAUDIOBSY) {
1031				printf("mcd%d: audio is active\n",unit);
1032				goto readerr;
1033			}
1034
1035retry_mode:
1036			/* to check for raw/cooked mode */
1037			if (cd->flags & MCDREADRAW) {
1038				rm = MCD_MD_RAW;
1039				mbx->sz = MCDRBLK;
1040			} else {
1041				rm = MCD_MD_COOKED;
1042				mbx->sz = cd->blksize;
1043			}
1044
1045			if (rm == cd->curr_mode)
1046				goto modedone;
1047
1048			mbx->count = RDELAY_WAITMODE;
1049
1050			cd->curr_mode = MCD_MD_UNKNOWN;
1051			mbx->mode = rm;
1052			mcd_put(com_port, MCD_CMDSETMODE);
1053			mcd_put(com_port, rm);
1054
1055			timeout((timeout_func_t)mcd_doread,
1056				(caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */
1057			return;
1058		} else {
1059			printf("mcd%d: timeout getstatus\n",unit);
1060			goto readerr;
1061		}
1062
1063	case MCD_S_WAITMODE:
1064		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE);
1065		if (mbx->count-- < 0) {
1066			printf("mcd%d: timeout set mode\n",unit);
1067			goto readerr;
1068		}
1069		if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL) {
1070			timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100);
1071			return;
1072		}
1073		cd->status = inb(port+mcd_status) & 0xFF;
1074		if (cd->status & MCD_ST_CMDCHECK) {
1075			cd->curr_mode = MCD_MD_UNKNOWN;
1076			goto retry_mode;
1077		}
1078		if (mcd_setflags(unit,cd) < 0)
1079			goto changed;
1080		cd->curr_mode = mbx->mode;
1081		MCD_TRACE("got WAITMODE delay=%d\n",
1082			RDELAY_WAITMODE-mbx->count);
1083modedone:
1084		/* for first block */
1085		mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz;
1086		mbx->skip = 0;
1087
1088nextblock:
1089		blknum 	= (bp->b_blkno / (mbx->sz/DEV_BSIZE))
1090			+ mbx->p_offset + mbx->skip/mbx->sz;
1091
1092		MCD_TRACE("mcd_doread: read blknum=%d for bp=%p\n",
1093			blknum, bp);
1094
1095		/* build parameter block */
1096		hsg2msf(blknum,rbuf.start_msf);
1097retry_read:
1098		/* send the read command */
1099		disable_intr();
1100		mcd_put(com_port,cd->read_command);
1101		mcd_put(com_port,rbuf.start_msf[0]);
1102		mcd_put(com_port,rbuf.start_msf[1]);
1103		mcd_put(com_port,rbuf.start_msf[2]);
1104		mcd_put(com_port,0);
1105		mcd_put(com_port,0);
1106		mcd_put(com_port,1);
1107		enable_intr();
1108
1109		/* Spin briefly (<= 2ms) to avoid missing next block */
1110		for (i = 0; i < 20; i++) {
1111			k = inb(port+MCD_FLAGS);
1112			if (!(k & MFL_DATA_NOT_AVAIL))
1113				goto got_it;
1114			DELAY(100);
1115		}
1116
1117		mbx->count = RDELAY_WAITREAD;
1118		timeout((timeout_func_t)mcd_doread,
1119			(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
1120		return;
1121	case MCD_S_WAITREAD:
1122		untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD);
1123		if (mbx->count-- > 0) {
1124			k = inb(port+MCD_FLAGS);
1125			if (!(k & MFL_DATA_NOT_AVAIL)) { /* XXX */
1126				MCD_TRACE("got data delay=%d\n",
1127					RDELAY_WAITREAD-mbx->count);
1128			got_it:
1129				/* data is ready */
1130				addr	= bp->b_un.b_addr + mbx->skip;
1131
1132				outb(port+mcd_ctl2,0x04);	/* XXX */
1133				for (i=0; i<mbx->sz; i++)
1134					*addr++ = inb(data_port);
1135				outb(port+mcd_ctl2,0x0c);	/* XXX */
1136
1137				k = inb(port+MCD_FLAGS);
1138				/* If we still have some junk, read it too */
1139				if (!(k & MFL_DATA_NOT_AVAIL)) {
1140					outb(port+mcd_ctl2,0x04);       /* XXX */
1141					(void)inb(data_port);
1142					(void)inb(data_port);
1143					outb(port+mcd_ctl2,0x0c);       /* XXX */
1144				}
1145
1146				if (--mbx->nblk > 0) {
1147					mbx->skip += mbx->sz;
1148					goto nextblock;
1149				}
1150
1151				/* return buffer */
1152				bp->b_resid = 0;
1153				biodone(bp);
1154
1155				cd->flags &= ~(MCDMBXBSY|MCDREADRAW);
1156				mcd_start(mbx->unit);
1157				return;
1158			}
1159			if (!(k & MFL_STATUS_NOT_AVAIL)) {
1160				cd->status = inb(port+mcd_status) & 0xFF;
1161				if (cd->status & MCD_ST_CMDCHECK)
1162					goto retry_read;
1163				if (mcd_setflags(unit,cd) < 0)
1164					goto changed;
1165			}
1166			timeout((timeout_func_t)mcd_doread,
1167				(caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */
1168			return;
1169		} else {
1170			printf("mcd%d: timeout read data\n",unit);
1171			goto readerr;
1172		}
1173	}
1174
1175readerr:
1176	if (mbx->retry-- > 0) {
1177		printf("mcd%d: retrying\n",unit);
1178		state = MCD_S_BEGIN1;
1179		goto loop;
1180	}
1181harderr:
1182	/* invalidate the buffer */
1183	bp->b_flags |= B_ERROR;
1184	bp->b_resid = bp->b_bcount;
1185	biodone(bp);
1186
1187	cd->flags &= ~(MCDMBXBSY|MCDREADRAW);
1188	mcd_start(mbx->unit);
1189	return;
1190
1191changed:
1192	printf("mcd%d: media changed\n", unit);
1193	goto harderr;
1194
1195#ifdef NOTDEF
1196	printf("mcd%d: unit timeout, resetting\n",mbx->unit);
1197	outb(mbx->port+mcd_reset,MCD_CMDRESET);
1198	DELAY(300000);
1199	(void)mcd_getstat(mbx->unit,1);
1200	(void)mcd_getstat(mbx->unit,1);
1201	/*cd->status &= ~MCDDSKCHNG; */
1202	cd->debug = 1; /* preventive set debug mode */
1203
1204#endif
1205
1206}
1207
1208static int
1209mcd_lock_door(int unit, int lock)
1210{
1211	struct mcd_data *cd = mcd_data + unit;
1212	int port = cd->iobase;
1213
1214	outb(port+mcd_command, MCD_CMDLOCKDRV);
1215	outb(port+mcd_command, lock);
1216	if (mcd_getstat(unit,0) == -1)
1217		return EIO;
1218	return 0;
1219}
1220
1221static int
1222mcd_close_tray(int unit)
1223{
1224	struct mcd_data *cd = mcd_data + unit;
1225	int port = cd->iobase;
1226	int retry, r;
1227
1228	if (mcd_getstat(unit,1) == -1)
1229		return EIO;
1230	if (cd->status & MCDDOOROPEN) {
1231		outb(port+mcd_command, MCD_CMDCLOSETRAY);
1232		for (retry = 0; retry < CLOSE_TRAY_SECS * WAIT_FRAC; retry++) {
1233			if (inb(port+MCD_FLAGS) & MFL_STATUS_NOT_AVAIL)
1234				(void) tsleep((caddr_t)cd, PSOCK | PCATCH, "mcdcls", hz/WAIT_FRAC);
1235			else {
1236				if ((r = mcd_getstat(unit,0)) == -1)
1237					return EIO;
1238				return 0;
1239			}
1240		}
1241		return ENXIO;
1242	}
1243	return 0;
1244}
1245
1246static int
1247mcd_eject(int unit)
1248{
1249	struct mcd_data *cd = mcd_data + unit;
1250	int port = cd->iobase, r;
1251
1252	if (mcd_getstat(unit,1) == -1)    /* detect disk change too */
1253		return EIO;
1254	if (cd->status & MCDDOOROPEN)
1255		return 0;
1256	if ((r = mcd_stop(unit)) == EIO)
1257		return r;
1258	outb(port+mcd_command, MCD_CMDEJECTDISK);
1259	if (mcd_getstat(unit,0) == -1)
1260		return EIO;
1261	return 0;
1262}
1263
1264static int
1265mcd_inject(int unit)
1266{
1267	struct mcd_data *cd = mcd_data + unit;
1268
1269	if (mcd_getstat(unit,1) == -1)    /* detect disk change too */
1270		return EIO;
1271	if (cd->status & MCDDOOROPEN)
1272		return mcd_close_tray(unit);
1273	return 0;
1274}
1275
1276static int
1277mcd_hard_reset(int unit)
1278{
1279	struct mcd_data *cd = mcd_data + unit;
1280	int port = cd->iobase;
1281
1282	outb(port+mcd_reset,MCD_CMDRESET);
1283	cd->curr_mode = MCD_MD_UNKNOWN;
1284	cd->audio_status = CD_AS_AUDIO_INVALID;
1285	return 0;
1286}
1287
1288static void
1289mcd_soft_reset(int unit)
1290{
1291	struct mcd_data *cd = mcd_data + unit;
1292	int i;
1293
1294	cd->flags &= (MCDINIT|MCDPROBING|MCDNEWMODEL);
1295	cd->curr_mode = MCD_MD_UNKNOWN;
1296	for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0;
1297	cd->audio_status = CD_AS_AUDIO_INVALID;
1298}
1299
1300static int
1301mcd_setmode(int unit, int mode)
1302{
1303	struct mcd_data *cd = mcd_data + unit;
1304	int port = cd->iobase;
1305	int retry, st;
1306
1307	if (cd->curr_mode == mode)
1308		return 0;
1309	if (cd->debug)
1310		printf("mcd%d: setting mode to %d\n", unit, mode);
1311	for(retry=0; retry<MCD_RETRYS; retry++)
1312	{
1313		cd->curr_mode = MCD_MD_UNKNOWN;
1314		outb(port+mcd_command, MCD_CMDSETMODE);
1315		outb(port+mcd_command, mode);
1316		if ((st = mcd_getstat(unit, 0)) >= 0) {
1317			cd->curr_mode = mode;
1318			return 0;
1319		}
1320		if (st == -2) {
1321			printf("mcd%d: media changed\n", unit);
1322			break;
1323		}
1324	}
1325
1326	return -1;
1327}
1328
1329static int
1330mcd_toc_header(int unit, struct ioc_toc_header *th)
1331{
1332	struct mcd_data *cd = mcd_data + unit;
1333	int r;
1334
1335	if ((r = mcd_volinfo(unit)) != 0)
1336		return r;
1337
1338	th->starting_track = bcd2bin(cd->volinfo.trk_low);
1339	th->ending_track = bcd2bin(cd->volinfo.trk_high);
1340	th->len = 2 * sizeof(u_char) /* start & end tracks */ +
1341		  (th->ending_track + 1 - th->starting_track + 1) *
1342		  sizeof(struct cd_toc_entry);
1343
1344	return 0;
1345}
1346
1347static int
1348mcd_read_toc(int unit)
1349{
1350	struct mcd_data *cd = mcd_data + unit;
1351	struct ioc_toc_header th;
1352	struct mcd_qchninfo q;
1353	int rc, trk, idx, retry;
1354
1355	/* Only read TOC if needed */
1356	if (cd->flags & MCDTOC)
1357		return 0;
1358
1359	if (cd->debug)
1360		printf("mcd%d: reading toc header\n", unit);
1361
1362	if ((rc = mcd_toc_header(unit, &th)) != 0)
1363		return rc;
1364
1365	if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0)
1366		return EIO;
1367
1368	if (mcd_setmode(unit, MCD_MD_TOC) != 0)
1369		return EIO;
1370
1371	if (cd->debug)
1372		printf("mcd%d: get_toc reading qchannel info\n",unit);
1373
1374	for(trk=th.starting_track; trk<=th.ending_track; trk++)
1375		cd->toc[trk].idx_no = 0;
1376	trk = th.ending_track - th.starting_track + 1;
1377	for(retry=0; retry<600 && trk>0; retry++)
1378	{
1379		if (mcd_getqchan(unit, &q) < 0) break;
1380		idx = bcd2bin(q.idx_no);
1381		if (idx>=th.starting_track && idx<=th.ending_track && q.trk_no==0) {
1382			if (cd->toc[idx].idx_no == 0) {
1383				cd->toc[idx] = q;
1384				trk--;
1385			}
1386		}
1387	}
1388
1389	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1390		return EIO;
1391
1392	if (trk != 0)
1393		return ENXIO;
1394
1395	/* add a fake last+1 */
1396	idx = th.ending_track + 1;
1397	cd->toc[idx].control = cd->toc[idx-1].control;
1398	cd->toc[idx].addr_type = cd->toc[idx-1].addr_type;
1399	cd->toc[idx].trk_no = 0;
1400	cd->toc[idx].idx_no = MCD_LASTPLUS1;
1401	cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0];
1402	cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1];
1403	cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2];
1404
1405	if (cd->debug)
1406	{ int i;
1407	for (i = th.starting_track; i <= idx; i++)
1408		printf("mcd%d: trk %d idx %d pos %d %d %d\n",
1409			unit, i,
1410			cd->toc[i].idx_no > 0x99 ? cd->toc[i].idx_no :
1411			bcd2bin(cd->toc[i].idx_no),
1412			bcd2bin(cd->toc[i].hd_pos_msf[0]),
1413			bcd2bin(cd->toc[i].hd_pos_msf[1]),
1414			bcd2bin(cd->toc[i].hd_pos_msf[2]));
1415	}
1416
1417	cd->flags |= MCDTOC;
1418
1419	return 0;
1420}
1421
1422static int
1423mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te)
1424{
1425	struct mcd_data *cd = mcd_data + unit;
1426	struct cd_toc_entry entries[MCD_MAXTOCS];
1427	struct ioc_toc_header th;
1428	int rc, n, trk, len;
1429
1430	if (   te->data_len < sizeof(entries[0])
1431	    || (te->data_len % sizeof(entries[0])) != 0
1432	    || te->address_format != CD_MSF_FORMAT
1433	    && te->address_format != CD_LBA_FORMAT
1434	   )
1435		return EINVAL;
1436
1437	/* Copy the toc header */
1438	if ((rc = mcd_toc_header(unit, &th)) != 0)
1439		return rc;
1440
1441	/* verify starting track */
1442	trk = te->starting_track;
1443	if (trk == 0)
1444		trk = th.starting_track;
1445	else if (trk == MCD_LASTPLUS1)
1446		trk = th.ending_track + 1;
1447	else if (trk < th.starting_track || trk > th.ending_track + 1)
1448		return EINVAL;
1449
1450	len = ((th.ending_track + 1 - trk) + 1) *
1451		sizeof(entries[0]);
1452	if (te->data_len < len)
1453		len = te->data_len;
1454	if (len > sizeof(entries))
1455		return EINVAL;
1456
1457	/* Make sure we have a valid toc */
1458	if ((rc=mcd_read_toc(unit)) != 0)
1459		return rc;
1460
1461	/* Copy the TOC data. */
1462	for (n = 0; len > 0 && trk <= th.ending_track + 1; trk++) {
1463		if (cd->toc[trk].idx_no == 0)
1464			continue;
1465		entries[n].control = cd->toc[trk].control;
1466		entries[n].addr_type = cd->toc[trk].addr_type;
1467		entries[n].track =
1468			cd->toc[trk].idx_no > 0x99 ? cd->toc[trk].idx_no :
1469			bcd2bin(cd->toc[trk].idx_no);
1470		switch (te->address_format) {
1471		case CD_MSF_FORMAT:
1472			entries[n].addr.msf.unused = 0;
1473			entries[n].addr.msf.minute = bcd2bin(cd->toc[trk].hd_pos_msf[0]);
1474			entries[n].addr.msf.second = bcd2bin(cd->toc[trk].hd_pos_msf[1]);
1475			entries[n].addr.msf.frame = bcd2bin(cd->toc[trk].hd_pos_msf[2]);
1476			break;
1477		case CD_LBA_FORMAT:
1478			entries[n].addr.lba = htonl(msf2hsg(cd->toc[trk].hd_pos_msf, 0));
1479			break;
1480		}
1481		len -= sizeof(struct cd_toc_entry);
1482		n++;
1483	}
1484
1485	/* copy the data back */
1486	return copyout(entries, te->data, n * sizeof(struct cd_toc_entry));
1487}
1488
1489static int
1490mcd_stop(int unit)
1491{
1492	struct mcd_data *cd = mcd_data + unit;
1493
1494	/* Verify current status */
1495	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS &&
1496	    cd->audio_status != CD_AS_PLAY_PAUSED &&
1497	    cd->audio_status != CD_AS_PLAY_COMPLETED) {
1498		if (cd->debug)
1499			printf("mcd%d: stop attempted when not playing, audio status %d\n",
1500				unit, cd->audio_status);
1501		return EINVAL;
1502	}
1503	if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS)
1504		if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0)
1505			return EIO;
1506	cd->audio_status = CD_AS_PLAY_COMPLETED;
1507	return 0;
1508}
1509
1510static int
1511mcd_getqchan(int unit, struct mcd_qchninfo *q)
1512{
1513	struct mcd_data *cd = mcd_data + unit;
1514
1515	if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0)
1516		return -1;
1517	if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0)
1518		return -1;
1519	if (cd->debug) {
1520		printf("mcd%d: getqchan control=0x%x addr_type=0x%x trk=%d ind=%d ttm=%d:%d.%d dtm=%d:%d.%d\n",
1521		unit,
1522		q->control, q->addr_type, bcd2bin(q->trk_no),
1523		bcd2bin(q->idx_no),
1524		bcd2bin(q->trk_size_msf[0]), bcd2bin(q->trk_size_msf[1]),
1525		bcd2bin(q->trk_size_msf[2]),
1526		bcd2bin(q->hd_pos_msf[0]), bcd2bin(q->hd_pos_msf[1]),
1527		bcd2bin(q->hd_pos_msf[2]));
1528	}
1529	return 0;
1530}
1531
1532static int
1533mcd_subchan(int unit, struct ioc_read_subchannel *sc)
1534{
1535	struct mcd_data *cd = mcd_data + unit;
1536	struct mcd_qchninfo q;
1537	struct cd_sub_channel_info data;
1538	int lba;
1539
1540	if (cd->debug)
1541		printf("mcd%d: subchan af=%d, df=%d\n", unit,
1542			sc->address_format,
1543			sc->data_format);
1544
1545	if (sc->address_format != CD_MSF_FORMAT &&
1546	    sc->address_format != CD_LBA_FORMAT)
1547		return EINVAL;
1548
1549	if (sc->data_format != CD_CURRENT_POSITION &&
1550	    sc->data_format != CD_MEDIA_CATALOG)
1551		return EINVAL;
1552
1553	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1554		return EIO;
1555
1556	if (mcd_getqchan(unit, &q) < 0)
1557		return EIO;
1558
1559	data.header.audio_status = cd->audio_status;
1560	data.what.position.data_format = sc->data_format;
1561
1562	switch (sc->data_format) {
1563	case CD_MEDIA_CATALOG:
1564		data.what.media_catalog.mc_valid = 1;
1565		data.what.media_catalog.mc_number[0] = '\0';
1566		break;
1567
1568	case CD_CURRENT_POSITION:
1569		data.what.position.control = q.control;
1570		data.what.position.addr_type = q.addr_type;
1571		data.what.position.track_number = bcd2bin(q.trk_no);
1572		data.what.position.index_number = bcd2bin(q.idx_no);
1573		switch (sc->address_format) {
1574		case CD_MSF_FORMAT:
1575			data.what.position.reladdr.msf.unused = 0;
1576			data.what.position.reladdr.msf.minute = bcd2bin(q.trk_size_msf[0]);
1577			data.what.position.reladdr.msf.second = bcd2bin(q.trk_size_msf[1]);
1578			data.what.position.reladdr.msf.frame = bcd2bin(q.trk_size_msf[2]);
1579			data.what.position.absaddr.msf.unused = 0;
1580			data.what.position.absaddr.msf.minute = bcd2bin(q.hd_pos_msf[0]);
1581			data.what.position.absaddr.msf.second = bcd2bin(q.hd_pos_msf[1]);
1582			data.what.position.absaddr.msf.frame = bcd2bin(q.hd_pos_msf[2]);
1583			break;
1584		case CD_LBA_FORMAT:
1585			lba = msf2hsg(q.trk_size_msf, 1);
1586			/*
1587			 * Pre-gap has index number of 0, and decreasing MSF
1588			 * address.  Must be converted to negative LBA, per
1589			 * SCSI spec.
1590			 */
1591			if (data.what.position.index_number == 0)
1592				lba = -lba;
1593			data.what.position.reladdr.lba = htonl(lba);
1594			data.what.position.absaddr.lba = htonl(msf2hsg(q.hd_pos_msf, 0));
1595			break;
1596		}
1597		break;
1598	}
1599
1600	return copyout(&data, sc->data, min(sizeof(struct cd_sub_channel_info), sc->data_len));
1601}
1602
1603static int
1604mcd_playmsf(int unit, struct ioc_play_msf *p)
1605{
1606	struct mcd_data *cd = mcd_data + unit;
1607	struct mcd_read2 pb;
1608
1609	if (cd->debug)
1610		printf("mcd%d: playmsf: from %d:%d.%d to %d:%d.%d\n",
1611		    unit,
1612		    p->start_m, p->start_s, p->start_f,
1613		    p->end_m, p->end_s, p->end_f);
1614
1615	if ((p->start_m * 60 * 75 + p->start_s * 75 + p->start_f) >=
1616	    (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) ||
1617	    (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f) >
1618	    M_msf(cd->volinfo.vol_msf) * 60 * 75 +
1619	    S_msf(cd->volinfo.vol_msf) * 75 +
1620	    F_msf(cd->volinfo.vol_msf))
1621		return EINVAL;
1622
1623	pb.start_msf[0] = bin2bcd(p->start_m);
1624	pb.start_msf[1] = bin2bcd(p->start_s);
1625	pb.start_msf[2] = bin2bcd(p->start_f);
1626	pb.end_msf[0] = bin2bcd(p->end_m);
1627	pb.end_msf[1] = bin2bcd(p->end_s);
1628	pb.end_msf[2] = bin2bcd(p->end_f);
1629
1630	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1631		return EIO;
1632
1633	return mcd_play(unit, &pb);
1634}
1635
1636static int
1637mcd_playtracks(int unit, struct ioc_play_track *pt)
1638{
1639	struct mcd_data *cd = mcd_data + unit;
1640	struct mcd_read2 pb;
1641	int a = pt->start_track;
1642	int z = pt->end_track;
1643	int rc, i;
1644
1645	if ((rc = mcd_read_toc(unit)) != 0)
1646		return rc;
1647
1648	if (cd->debug)
1649		printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit,
1650			a, pt->start_index, z, pt->end_index);
1651
1652	if (   a < bcd2bin(cd->volinfo.trk_low)
1653	    || a > bcd2bin(cd->volinfo.trk_high)
1654	    || a > z
1655	    || z < bcd2bin(cd->volinfo.trk_low)
1656	    || z > bcd2bin(cd->volinfo.trk_high))
1657		return EINVAL;
1658
1659	for (i = 0; i < 3; i++) {
1660		pb.start_msf[i] = cd->toc[a].hd_pos_msf[i];
1661		pb.end_msf[i] = cd->toc[z+1].hd_pos_msf[i];
1662	}
1663
1664	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1665		return EIO;
1666
1667	return mcd_play(unit, &pb);
1668}
1669
1670static int
1671mcd_playblocks(int unit, struct ioc_play_blocks *p)
1672{
1673	struct mcd_data *cd = mcd_data + unit;
1674	struct mcd_read2 pb;
1675
1676	if (cd->debug)
1677		printf("mcd%d: playblocks: blkno %d length %d\n",
1678		    unit, p->blk, p->len);
1679
1680	if (p->blk > cd->disksize || p->len > cd->disksize ||
1681	    p->blk < 0 || p->len < 0 ||
1682	    (p->blk + p->len) > cd->disksize)
1683		return EINVAL;
1684
1685	hsg2msf(p->blk, pb.start_msf);
1686	hsg2msf(p->blk + p->len, pb.end_msf);
1687
1688	if (mcd_setmode(unit, MCD_MD_COOKED) != 0)
1689		return EIO;
1690
1691	return mcd_play(unit, &pb);
1692}
1693
1694static int
1695mcd_play(int unit, struct mcd_read2 *pb)
1696{
1697	struct mcd_data *cd = mcd_data + unit;
1698	int com_port = cd->iobase + mcd_command;
1699	int retry, st = -1, status;
1700
1701	cd->lastpb = *pb;
1702	for(retry=0; retry<MCD_RETRYS; retry++) {
1703
1704		disable_intr();
1705		outb(com_port, MCD_CMDSINGLESPEEDREAD);
1706		outb(com_port, pb->start_msf[0]);
1707		outb(com_port, pb->start_msf[1]);
1708		outb(com_port, pb->start_msf[2]);
1709		outb(com_port, pb->end_msf[0]);
1710		outb(com_port, pb->end_msf[1]);
1711		outb(com_port, pb->end_msf[2]);
1712		enable_intr();
1713
1714		status=mcd_getstat(unit, 0);
1715		if (status == -1)
1716			continue;
1717		else if (status != -2)
1718			st = 0;
1719		break;
1720	}
1721
1722	if (status == -2) {
1723		printf("mcd%d: media changed\n", unit);
1724		return ENXIO;
1725	}
1726	if (cd->debug)
1727		printf("mcd%d: mcd_play retry=%d, status=0x%02x\n", unit, retry, status);
1728	if (st < 0)
1729		return ENXIO;
1730	cd->audio_status = CD_AS_PLAY_IN_PROGRESS;
1731	return 0;
1732}
1733
1734static int
1735mcd_pause(int unit)
1736{
1737	struct mcd_data *cd = mcd_data + unit;
1738	struct mcd_qchninfo q;
1739	int rc;
1740
1741	/* Verify current status */
1742	if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS &&
1743	    cd->audio_status != CD_AS_PLAY_PAUSED) {
1744		if (cd->debug)
1745			printf("mcd%d: pause attempted when not playing, audio status %d\n",
1746			       unit, cd->audio_status);
1747		return EINVAL;
1748	}
1749
1750	/* Get the current position */
1751	if (mcd_getqchan(unit, &q) < 0)
1752		return EIO;
1753
1754	/* Copy it into lastpb */
1755	cd->lastpb.start_msf[0] = q.hd_pos_msf[0];
1756	cd->lastpb.start_msf[1] = q.hd_pos_msf[1];
1757	cd->lastpb.start_msf[2] = q.hd_pos_msf[2];
1758
1759	/* Stop playing */
1760	if ((rc=mcd_stop(unit)) != 0)
1761		return rc;
1762
1763	/* Set the proper status and exit */
1764	cd->audio_status = CD_AS_PLAY_PAUSED;
1765	return 0;
1766}
1767
1768static int
1769mcd_resume(int unit)
1770{
1771	struct mcd_data *cd = mcd_data + unit;
1772
1773	if (cd->audio_status != CD_AS_PLAY_PAUSED)
1774		return EINVAL;
1775	return mcd_play(unit, &cd->lastpb);
1776}
1777
1778
1779static mcd_devsw_installed = 0;
1780
1781static void 	mcd_drvinit(void *unused)
1782{
1783
1784	if( ! mcd_devsw_installed ) {
1785		bdevsw_add_generic(BDEV_MAJOR,CDEV_MAJOR, &mcd_bdevsw);
1786		mcd_devsw_installed = 1;
1787    	}
1788}
1789
1790SYSINIT(mcddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,mcd_drvinit,NULL)
1791
1792
1793#endif /* NMCD > 0 */
1794