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