mcd.c revision 1.24 1 /* $NetBSD: mcd.c,v 1.24 1994/11/18 22:03:30 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1993, 1994 Charles Hannum.
5 * Copyright 1993 by Holger Veit (data part)
6 * Copyright 1993 by Brian Moore (audio part)
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This software was developed by Holger Veit and Brian Moore
20 * for use with "386BSD" and similar operating systems.
21 * "Similar operating systems" includes mainly non-profit oriented
22 * systems for research and education, including but not restricted to
23 * "NetBSD", "FreeBSD", "Mach" (by CMU).
24 * 4. Neither the name of the developer(s) nor the name "386BSD"
25 * may be used to endorse or promote products derived from this
26 * software without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
29 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE
32 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
33 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
34 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
35 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 */
40
41 /*static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";*/
42
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/proc.h>
48 #include <sys/conf.h>
49 #include <sys/file.h>
50 #include <sys/buf.h>
51 #include <sys/stat.h>
52 #include <sys/uio.h>
53 #include <sys/ioctl.h>
54 #include <sys/cdio.h>
55 #include <sys/errno.h>
56 #include <sys/dkbad.h>
57 #include <sys/disklabel.h>
58 #include <sys/device.h>
59 #include <sys/disk.h>
60
61 #include <machine/cpu.h>
62 #include <machine/pio.h>
63
64 #include <i386/isa/isavar.h>
65 #include <i386/isa/mcdreg.h>
66
67 #ifndef MCDDEBUG
68 #define MCD_TRACE(fmt,a,b,c,d)
69 #else
70 #define MCD_TRACE(fmt,a,b,c,d) {if (sc->debug) {printf("%s: st=%02x: ", sc->sc_dev.dv_xname, sc->status); printf(fmt,a,b,c,d);}}
71 #endif
72
73 #define MCDPART(dev) DISKPART(dev)
74 #define MCDUNIT(dev) DISKUNIT(dev)
75
76 /* status */
77 #define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */
78 #define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */
79 #define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */
80 #define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */
81
82 /* toc */
83 #define MCD_MAXTOCS 104 /* from the Linux driver */
84 #define MCD_LASTPLUS1 170 /* special toc entry */
85
86 struct mcd_mbx {
87 struct mcd_softc *softc;
88 short retry;
89 short nblk;
90 int sz;
91 u_long skip;
92 struct buf *bp;
93 int p_offset;
94 short count;
95 short state;
96 #define MCD_S_BEGIN 0
97 #define MCD_S_WAITSTAT 1
98 #define MCD_S_WAITMODE 2
99 #define MCD_S_WAITREAD 3
100 };
101
102 struct mcd_softc {
103 struct device sc_dev;
104 struct dkdevice sc_dk;
105 struct intrhand sc_ih;
106
107 int iobase;
108 short config;
109 short flags;
110 #define MCDOPEN 0x0001 /* device opened */
111 #define MCDVALID 0x0002 /* parameters loaded */
112 #define MCDLABEL 0x0004 /* label is read */
113 #define MCDVOLINFO 0x0008 /* already read volinfo */
114 #define MCDTOC 0x0010 /* already read toc */
115 #define MCDMBXBSY 0x0020 /* local mbx is busy */
116 short status;
117 int blksize;
118 u_long disksize;
119 struct mcd_volinfo volinfo;
120 struct mcd_qchninfo toc[MCD_MAXTOCS];
121 short audio_status;
122 struct mcd_read2 lastpb;
123 short debug;
124 struct buf buf_queue;
125 struct mcd_mbx mbx;
126 };
127
128 /* prototypes */
129 int mcdopen __P((dev_t, int, int, struct proc *));
130 int mcdclose __P((dev_t, int, int));
131 int mcd_start __P((struct mcd_softc *));
132 int mcdioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
133 int mcd_getdisklabel __P((struct mcd_softc *));
134 int mcdsize __P((dev_t));
135 void mcd_configure __P((struct mcd_softc *));
136 int mcd_waitrdy __P((int, int));
137 int mcd_getreply __P((struct mcd_softc *, int));
138 int mcd_getstat __P((struct mcd_softc *, int));
139 void mcd_setflags __P((struct mcd_softc *));
140 int mcd_get __P((struct mcd_softc *, char *, int));
141 int mcd_send __P((struct mcd_softc *, int, int));
142 int bcd2bin __P((bcd_t));
143 bcd_t bin2bcd __P((int));
144 void hsg2msf __P((int, bcd_t *));
145 int msf2hsg __P((bcd_t *));
146 int mcd_volinfo __P((struct mcd_softc *));
147 int mcdintr __P((struct mcd_softc *));
148 int mcd_setmode __P((struct mcd_softc *, int));
149 void mcd_doread __P((void *));
150 int mcd_toc_header __P((struct mcd_softc *, struct ioc_toc_header *));
151 int mcd_read_toc __P((struct mcd_softc *));
152 int mcd_toc_entry __P((struct mcd_softc *, struct ioc_read_toc_entry *));
153 int mcd_stop __P((struct mcd_softc *));
154 int mcd_getqchan __P((struct mcd_softc *, struct mcd_qchninfo *));
155 int mcd_subchan __P((struct mcd_softc *, struct ioc_read_subchannel *));
156 int mcd_playtracks __P((struct mcd_softc *, struct ioc_play_track *));
157 int mcd_play __P((struct mcd_softc *, struct mcd_read2 *));
158 int mcd_pause __P((struct mcd_softc *));
159 int mcd_resume __P((struct mcd_softc *));
160
161 int mcdprobe __P((struct device *, void *, void *));
162 void mcdattach __P((struct device *, struct device *, void *));
163 void mcdstrategy __P((struct buf *));
164
165 struct cfdriver mcdcd = {
166 NULL, "mcd", mcdprobe, mcdattach, DV_DISK, sizeof(struct mcd_softc)
167 };
168 struct dkdriver mcddkdriver = { mcdstrategy };
169
170 #define mcd_put(port,byte) outb(port,byte)
171
172 #define MCD_RETRIES 5
173 #define MCD_RDRETRIES 8
174
175 #define MCDBLK 2048 /* for cooked mode */
176 #define MCDRBLK 2352 /* for raw mode */
177
178 /* several delays */
179 #define RDELAY_WAITSTAT 300
180 #define RDELAY_WAITMODE 300
181 #define RDELAY_WAITREAD 800
182
183 #define DELAY_STATUS 10000l /* 10000 * 1us */
184 #define DELAY_GETREPLY 200000l /* 200000 * 2us */
185 #define DELAY_SEEKREAD 20000l /* 20000 * 1us */
186
187 void
188 mcdattach(parent, self, aux)
189 struct device *parent, *self;
190 void *aux;
191 {
192 struct mcd_softc *sc = (void *)self;
193 struct isa_attach_args *ia = aux;
194
195 #ifdef notyet
196 /* Wire controller for interrupts and DMA. */
197 mcd_configure(sc);
198 #endif
199
200 printf("\n");
201
202 sc->flags = 0;
203 sc->sc_dk.dk_driver = &mcddkdriver;
204
205 sc->sc_ih.ih_fun = mcdintr;
206 sc->sc_ih.ih_arg = sc;
207 sc->sc_ih.ih_level = IPL_BIO;
208 intr_establish(ia->ia_irq, &sc->sc_ih);
209 }
210
211 int
212 mcdopen(dev, flag, fmt, p)
213 dev_t dev;
214 int flag, fmt;
215 struct proc *p;
216 {
217 int unit, part;
218 struct mcd_softc *sc;
219
220 unit = MCDUNIT(dev);
221 if (unit >= mcdcd.cd_ndevs)
222 return ENXIO;
223 sc = mcdcd.cd_devs[unit];
224 if (!sc)
225 return ENXIO;
226
227 part = MCDPART(dev);
228
229 /* If it's been invalidated forget the label. */
230 if ((sc->flags & MCDVALID) == 0) {
231 sc->flags &= ~(MCDLABEL | MCDVOLINFO | MCDTOC);
232
233 /* If any partition still open, then don't allow fresh open. */
234 if (sc->sc_dk.dk_openmask != 0)
235 return ENXIO;
236 }
237
238 if (mcd_getstat(sc, 1) < 0)
239 return ENXIO;
240
241 if (mcdsize(dev) < 0) {
242 printf("%s: failed to get disk size\n", sc->sc_dev.dv_xname);
243 return ENXIO;
244 }
245
246 sc->flags |= MCDVALID;
247
248 /* XXX Get a default disklabel. */
249 mcd_getdisklabel(sc);
250
251 MCD_TRACE("open: partition=%d disksize=%d blksize=%d\n", part,
252 sc->disksize, sc->blksize, 0);
253
254 if (part != RAW_PART &&
255 (part >= sc->sc_dk.dk_label.d_npartitions ||
256 sc->sc_dk.dk_label.d_partitions[part].p_fstype == FS_UNUSED))
257 return ENXIO;
258
259 /* Insure only one open at a time. */
260 switch (fmt) {
261 case S_IFCHR:
262 sc->sc_dk.dk_copenmask |= (1 << part);
263 break;
264 case S_IFBLK:
265 sc->sc_dk.dk_bopenmask |= (1 << part);
266 break;
267 }
268 sc->sc_dk.dk_openmask = sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
269
270 return 0;
271 }
272
273 int
274 mcdclose(dev, flag, fmt)
275 dev_t dev;
276 int flag, fmt;
277 {
278 struct mcd_softc *sc = mcdcd.cd_devs[MCDUNIT(dev)];
279 int part = MCDPART(dev);
280
281 MCD_TRACE("close: partition=%d\n", part, 0, 0, 0);
282
283 /* Get status. */
284 mcd_getstat(sc, 1);
285
286 switch (fmt) {
287 case S_IFCHR:
288 sc->sc_dk.dk_copenmask &= ~(1 << part);
289 break;
290 case S_IFBLK:
291 sc->sc_dk.dk_bopenmask &= ~(1 << part);
292 break;
293 }
294 sc->sc_dk.dk_openmask = sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
295
296 return 0;
297 }
298
299 void
300 mcdstrategy(bp)
301 struct buf *bp;
302 {
303 struct mcd_softc *sc = mcdcd.cd_devs[MCDUNIT(bp->b_dev)];
304 struct buf *qp;
305 int s;
306
307 /* Test validity. */
308 MCD_TRACE("strategy: buf=0x%lx blkno=%ld bcount=%ld\n", bp,
309 bp->b_blkno, bp->b_bcount, 0);
310 if (bp->b_blkno < 0) {
311 printf("%s: strategy: blkno=%d bcount=%d\n",
312 sc->sc_dev.dv_xname, bp->b_blkno, bp->b_bcount);
313 bp->b_error = EINVAL;
314 goto bad;
315 }
316
317 /* If device invalidated (e.g. media change, door open), error. */
318 if (!(sc->flags & MCDVALID)) {
319 MCD_TRACE("strategy: drive not valid\n", 0, 0, 0, 0);
320 bp->b_error = EIO;
321 goto bad;
322 }
323
324 /* Check for read only. */
325 if (!(bp->b_flags & B_READ)) {
326 bp->b_error = EROFS;
327 goto bad;
328 }
329
330 /* No data to read. */
331 if (bp->b_bcount == 0)
332 goto done;
333
334 /* For non raw access, check partition limits. */
335 if (MCDPART(bp->b_dev) != RAW_PART) {
336 if (!(sc->flags & MCDLABEL)) {
337 bp->b_error = EIO;
338 goto bad;
339 }
340 /* Adjust transfer if necessary. */
341 if (bounds_check_with_label(bp, &sc->sc_dk.dk_label, 0) <= 0)
342 goto done;
343 }
344
345 /* Queue it. */
346 qp = &sc->buf_queue;
347 s = splbio();
348 disksort(qp, bp);
349 splx(s);
350
351 /* Now check whether we can perform processing. */
352 mcd_start(sc);
353 return;
354
355 bad:
356 bp->b_flags |= B_ERROR;
357 done:
358 bp->b_resid = bp->b_bcount;
359 biodone(bp);
360 }
361
362 int
363 mcd_start(sc)
364 struct mcd_softc *sc;
365 {
366 struct buf *bp, *qp = &sc->buf_queue;
367 int part;
368 int s;
369
370 loop:
371 s = splbio();
372
373 if (sc->flags & MCDMBXBSY) {
374 splx(s);
375 return;
376 }
377
378 if ((bp = qp->b_actf) == 0) {
379 /* Nothing to do; */
380 splx(s);
381 return;
382 }
383
384 /* Block found to process; dequeue. */
385 MCD_TRACE("start: found block bp=0x%x\n", bp, 0, 0, 0);
386 qp->b_actf = bp->b_actf;
387 splx(s);
388
389 /* Changed media? */
390 if (!(sc->flags & MCDVALID)) {
391 MCD_TRACE("start: drive not valid\n", 0, 0, 0, 0);
392 sc->flags &= ~(MCDLABEL | MCDVOLINFO | MCDTOC);
393 bp->b_error = EIO;
394 bp->b_flags |= B_ERROR;
395 biodone(bp);
396 goto loop;
397 }
398
399 sc->flags |= MCDMBXBSY;
400 sc->mbx.softc = sc;
401 sc->mbx.retry = MCD_RETRIES;
402 sc->mbx.bp = bp;
403 part = MCDPART(bp->b_dev);
404 if (part == RAW_PART)
405 sc->mbx.p_offset = 0;
406 else
407 sc->mbx.p_offset =
408 sc->sc_dk.dk_label.d_partitions[part].p_offset;
409
410 /* Calling the read routine. */
411 sc->mbx.state = MCD_S_BEGIN;
412 mcd_doread(&sc->mbx);
413 /* triggers mcd_start, when successful finished. */
414 }
415
416 int
417 mcdioctl(dev, cmd, addr, flags, p)
418 dev_t dev;
419 u_long cmd;
420 caddr_t addr;
421 int flags;
422 struct proc *p;
423 {
424 struct mcd_softc *sc = mcdcd.cd_devs[MCDUNIT(dev)];
425
426 if (!(sc->flags & MCDVALID))
427 return EIO;
428
429 MCD_TRACE("ioctl: cmd=0x%x\n", cmd, 0, 0, 0);
430
431 switch (cmd) {
432 case DIOCSBAD:
433 return EINVAL;
434 case CDIOCPLAYTRACKS:
435 return mcd_playtracks(sc, (struct ioc_play_track *)addr);
436 case CDIOCPLAYBLOCKS:
437 return mcd_play(sc, (struct mcd_read2 *)addr);
438 case CDIOCREADSUBCHANNEL:
439 return mcd_subchan(sc, (struct ioc_read_subchannel *)addr);
440 case CDIOREADTOCHEADER:
441 return mcd_toc_header(sc, (struct ioc_toc_header *)addr);
442 case CDIOREADTOCENTRYS:
443 return mcd_toc_entry(sc, (struct ioc_read_toc_entry *)addr);
444 case CDIOCSETPATCH:
445 case CDIOCGETVOL:
446 case CDIOCSETVOL:
447 case CDIOCSETMONO:
448 case CDIOCSETSTEREO:
449 case CDIOCSETMUTE:
450 case CDIOCSETLEFT:
451 case CDIOCSETRIGHT:
452 return EINVAL;
453 case CDIOCRESUME:
454 return mcd_resume(sc);
455 case CDIOCPAUSE:
456 return mcd_pause(sc);
457 case CDIOCSTART:
458 return EINVAL;
459 case CDIOCSTOP:
460 return mcd_stop(sc);
461 case CDIOCEJECT:
462 return EINVAL;
463 case CDIOCSETDEBUG:
464 sc->debug = 1;
465 return 0;
466 case CDIOCCLRDEBUG:
467 sc->debug = 0;
468 return 0;
469 case CDIOCRESET:
470 return EINVAL;
471 default:
472 #if 0
473 case DIOCGDINFO:
474 case DIOCGPART:
475 case DIOCWDINFO:
476 case DIOCSDINFO:
477 case DIOCWLABEL:
478 #endif
479 return ENOTTY;
480 }
481 #ifdef DIAGNOSTIC
482 panic("mcdioctl: impossible");
483 #endif
484 }
485
486 /*
487 * This could have been taken from scsi/cd.c, but it is not clear
488 * whether the scsi cd driver is linked in.
489 */
490 int
491 mcd_getdisklabel(sc)
492 struct mcd_softc *sc;
493 {
494
495 if (sc->flags & MCDLABEL)
496 return 0;
497
498 bzero(&sc->sc_dk.dk_label, sizeof(struct disklabel));
499 bzero(&sc->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
500 strncpy(sc->sc_dk.dk_label.d_typename, "Mitsumi CD ROM", 16);
501 strncpy(sc->sc_dk.dk_label.d_packname, "unknown", 16);
502 sc->sc_dk.dk_label.d_secsize = sc->blksize;
503 sc->sc_dk.dk_label.d_nsectors = 100;
504 sc->sc_dk.dk_label.d_ntracks = 1;
505 sc->sc_dk.dk_label.d_ncylinders = (sc->disksize /100) + 1;
506 sc->sc_dk.dk_label.d_secpercyl = 100;
507 sc->sc_dk.dk_label.d_secperunit = sc->disksize;
508 sc->sc_dk.dk_label.d_rpm = 300;
509 sc->sc_dk.dk_label.d_interleave = 1;
510 sc->sc_dk.dk_label.d_flags = D_REMOVABLE;
511 sc->sc_dk.dk_label.d_npartitions= RAW_PART + 1;
512 sc->sc_dk.dk_label.d_partitions[0].p_offset = 0;
513 sc->sc_dk.dk_label.d_partitions[0].p_size = sc->disksize;
514 sc->sc_dk.dk_label.d_partitions[0].p_fstype = 9;
515 sc->sc_dk.dk_label.d_partitions[RAW_PART].p_offset = 0;
516 sc->sc_dk.dk_label.d_partitions[RAW_PART].p_size = sc->disksize;
517 sc->sc_dk.dk_label.d_partitions[RAW_PART].p_fstype = 9;
518
519 sc->flags |= MCDLABEL;
520 return 0;
521 }
522
523 int
524 mcdsize(dev)
525 dev_t dev;
526 {
527 int size;
528 struct mcd_softc *sc = mcdcd.cd_devs[MCDUNIT(dev)];
529
530 if (mcd_volinfo(sc) >= 0) {
531 sc->blksize = MCDBLK;
532 size = msf2hsg(sc->volinfo.vol_msf);
533 sc->disksize = size * (MCDBLK / DEV_BSIZE);
534 return 0;
535 }
536 return -1;
537 }
538
539 int
540 mcddump()
541 {
542
543 /* Not implemented. */
544 return EINVAL;
545 }
546
547 /***************************************************************
548 * lower level of driver starts here
549 **************************************************************/
550
551 #ifdef notyet
552 static char irqs[] = {
553 0x00, 0x00, 0x10, 0x20, 0x00, 0x30, 0x00, 0x00,
554 0x00, 0x10, 0x40, 0x50, 0x00, 0x00, 0x00, 0x00
555 };
556
557 static char drqs[] = {
558 0x00, 0x01, 0x00, 0x03, 0x00, 0x05, 0x06, 0x07
559 };
560 #endif
561
562 void
563 mcd_configure(sc)
564 struct mcd_softc *sc;
565 {
566
567 outb(sc->iobase + mcd_config, sc->config);
568 }
569
570 int
571 mcdprobe(parent, match, aux)
572 struct device *parent;
573 void *match, *aux;
574 {
575 struct mcd_softc *sc = match;
576 struct isa_attach_args *ia = aux;
577 int iobase = ia->ia_iobase;
578 int i;
579 int st, check, version;
580
581 #ifdef notyet
582 /* Get irq/drq configuration word. */
583 sc->config = irqs[ia->ia_irq];
584 #endif
585 sc->iobase = iobase;
586
587 /* Send a reset. */
588 outb(iobase + mcd_reset, 0);
589 delay(1000000);
590 /* Get any pending status and throw away. */
591 for (i = 10; i; i--)
592 inb(iobase + mcd_status);
593 delay(1000);
594
595 /* Send get status command. */
596 outb(iobase + mcd_command, MCD_CMDGETSTAT);
597 st = mcd_getreply(sc, DELAY_GETREPLY);
598
599 if (st < 0) {
600 #ifdef DEBUG
601 printf("Mitsumi drive NOT detected\n");
602 #endif
603 return 0;
604 }
605
606 /*
607 * The following code uses the 0xDC command, it returns a M from the
608 * second byte and a number in the third.
609 * (I hope you have the right drive for that, most drives don't do!)
610 * Whole code entirely rewriten by veit (at) gmd.de, the changes accessed
611 * the drive in an illegal way. Proper way is to use the timeout
612 * driven routines mcd_getreply etc. rather than arbitrary delays.
613 */
614
615 delay(2000);
616 outb(iobase + mcd_command, MCD_CMDCONTINFO);
617 st = mcd_getreply(sc, DELAY_GETREPLY);
618
619 if (st < 0) {
620 #ifdef DEBUG
621 printf("Mitsumi drive error\n");
622 #endif
623 return 0;
624 }
625 check = mcd_getreply(sc, DELAY_GETREPLY);
626 if (check < 0)
627 return 0;
628 version = mcd_getreply(sc, DELAY_GETREPLY);
629 if (version < 0)
630 return 0;
631 /* Flush junk. */
632 (void) mcd_getreply(sc, DELAY_GETREPLY);
633
634 /*
635 * The following is code which is not guaranteed to work for all
636 * drives, because the meaning of the expected 'M' is not clear
637 * (M_itsumi is an obvious assumption, but I don't trust that).
638 * Also, the original hack had a bogus condition that always
639 * returned true.
640 */
641 if (check != 'D' && check != 'M') {
642 printf("%s: unrecognized drive version %c%02x; will try to use it anyway\n",
643 sc->sc_dev.dv_xname, check, version);
644 }
645
646 #ifdef DEBUG
647 printf("Mitsumi drive detected\n");
648 #endif
649 ia->ia_iosize = 4;
650 ia->ia_msize = 0;
651 return 1;
652 }
653
654 int
655 mcd_waitrdy(iobase, dly)
656 int iobase;
657 int dly;
658 {
659 int i;
660
661 /* Wait until xfer port senses data ready. */
662 for (i = dly; i; i--) {
663 if ((inb(iobase + mcd_xfer) & MCD_ST_BUSY) == 0)
664 return 0;
665 delay(1);
666 }
667 return -1;
668 }
669
670 int
671 mcd_getreply(sc, dly)
672 struct mcd_softc *sc;
673 int dly;
674 {
675 int iobase = sc->iobase;
676
677 /* Wait data to become ready. */
678 if (mcd_waitrdy(iobase, dly) < 0) {
679 printf("%s: timeout in getreply\n", sc->sc_dev.dv_xname);
680 return -1;
681 }
682
683 /* Get the data. */
684 return inb(iobase + mcd_status);
685 }
686
687 int
688 mcd_getstat(sc, sflg)
689 struct mcd_softc *sc;
690 int sflg;
691 {
692 int i;
693 int iobase = sc->iobase;
694
695 /* Get the status. */
696 if (sflg)
697 outb(iobase + mcd_command, MCD_CMDGETSTAT);
698 i = mcd_getreply(sc, DELAY_GETREPLY);
699 if (i < 0) {
700 printf("%s: timeout in getstat\n", sc->sc_dev.dv_xname);
701 return -1;
702 }
703
704 sc->status = i;
705
706 mcd_setflags(sc);
707 return sc->status;
708 }
709
710 void
711 mcd_setflags(sc)
712 struct mcd_softc *sc;
713 {
714
715 /* Check flags. */
716 if (sc->status & (MCDDSKCHNG | MCDDOOROPEN)) {
717 MCD_TRACE("getstat: sensed DSKCHNG or DOOROPEN\n", 0, 0, 0, 0);
718 sc->flags &= ~MCDVALID;
719 }
720
721 if (sc->status & MCDAUDIOBSY)
722 sc->audio_status = CD_AS_PLAY_IN_PROGRESS;
723 else if (sc->audio_status == CD_AS_PLAY_IN_PROGRESS)
724 sc->audio_status = CD_AS_PLAY_COMPLETED;
725 }
726
727 int
728 mcd_get(sc, buf, nmax)
729 struct mcd_softc *sc;
730 char *buf;
731 int nmax;
732 {
733 int i, k;
734
735 for (i = 0; i < nmax; i++) {
736 /* Wait for data. */
737 if ((k = mcd_getreply(sc, DELAY_GETREPLY)) < 0) {
738 printf("%s: timeout in get\n", sc->sc_dev.dv_xname);
739 return -1;
740 }
741 buf[i] = k;
742 }
743 return i;
744 }
745
746 int
747 mcd_send(sc, cmd, nretries)
748 struct mcd_softc *sc;
749 int cmd, nretries;
750 {
751 int i, k;
752 int iobase = sc->iobase;
753
754 MCD_TRACE("send: cmd=0x%x\n", cmd, 0, 0, 0);
755
756 for (i = nretries; i; i--) {
757 outb(iobase + mcd_command, cmd);
758 if ((k = mcd_getstat(sc, 0)) != -1)
759 break;
760 }
761 if (!i) {
762 printf("%s: send: retry count exceeded\n", sc->sc_dev.dv_xname);
763 return -1;
764 }
765
766 MCD_TRACE("send: status=0x%x\n", k, 0, 0, 0);
767
768 return 0;
769 }
770
771 int
772 bcd2bin(b)
773 bcd_t b;
774 {
775
776 return (b >> 4) * 10 + (b & 15);
777 }
778
779 bcd_t
780 bin2bcd(b)
781 int b;
782 {
783
784 return ((b / 10) << 4) | (b % 10);
785 }
786
787 void
788 hsg2msf(hsg, msf)
789 int hsg;
790 bcd_t *msf;
791 {
792
793 hsg += 150;
794 M_msf(msf) = bin2bcd(hsg / 4500);
795 hsg %= 4500;
796 S_msf(msf) = bin2bcd(hsg / 75);
797 F_msf(msf) = bin2bcd(hsg % 75);
798 }
799
800 int
801 msf2hsg(msf)
802 bcd_t *msf;
803 {
804
805 return (bcd2bin(M_msf(msf)) * 60 +
806 bcd2bin(S_msf(msf))) * 75 +
807 bcd2bin(F_msf(msf)) - 150;
808 }
809
810 int
811 mcd_volinfo(sc)
812 struct mcd_softc *sc;
813 {
814
815 MCD_TRACE("volinfo: enter\n", 0, 0, 0, 0);
816
817 /* Get the status, in case the disc has been changed. */
818 if (mcd_getstat(sc, 1) < 0)
819 return EIO;
820
821 /* Just return if we already have it. */
822 if (sc->flags & MCDVOLINFO)
823 return 0;
824
825 /* Send volume info command. */
826 if (mcd_send(sc, MCD_CMDGETVOLINFO, MCD_RETRIES) < 0)
827 return -1;
828
829 /* Get the data. */
830 if (mcd_get(sc, (char*) &sc->volinfo, sizeof(struct mcd_volinfo)) < 0) {
831 printf("%s: volinfo: error reading data\n",
832 sc->sc_dev.dv_xname);
833 return -1;
834 }
835
836 if (sc->volinfo.trk_low != 0 || sc->volinfo.trk_high != 0) {
837 /* Volinfo is OK. */
838 sc->flags |= MCDVOLINFO;
839 return 0;
840 }
841
842 return -1;
843 }
844
845 int
846 mcdintr(sc)
847 struct mcd_softc *sc;
848 {
849 int iobase = sc->iobase;
850
851 MCD_TRACE("stray interrupt xfer=0x%x\n", inb(iobase + mcd_xfer),
852 0, 0, 0);
853
854 /* Just read out status and ignore the rest. */
855 if (inb(iobase + mcd_xfer) != 0xff)
856 (void) inb(iobase + mcd_status);
857
858 return -1;
859 }
860
861 /*
862 * State machine to process read requests.
863 * Initialize with MCD_S_BEGIN: calculate sizes, and read status
864 * MCD_S_WAITSTAT: wait for status reply, set mode
865 * MCD_S_WAITMODE: waits for status reply from set mode, set read command
866 * MCD_S_WAITREAD: wait for read ready, read data.
867 */
868 void
869 mcd_doread(arg)
870 void *arg;
871 {
872 struct mcd_mbx *mbx = arg;
873 struct mcd_softc *sc = mbx->softc;
874 int iobase = sc->iobase;
875 struct buf *bp = mbx->bp;
876
877 int i, k;
878 struct mcd_read2 rbuf;
879 int blkno;
880 caddr_t addr;
881
882 loop:
883 switch (mbx->state) {
884 case MCD_S_BEGIN:
885 /* Get status. */
886 outb(iobase + mcd_command, MCD_CMDGETSTAT);
887
888 mbx->count = RDELAY_WAITSTAT;
889 mbx->state = MCD_S_WAITSTAT;
890 timeout(mcd_doread, mbx, hz / 100);
891 return;
892
893 case MCD_S_WAITSTAT:
894 untimeout(mcd_doread, mbx);
895 if (mbx->count-- < 0) {
896 printf("%s: timeout getting status\n",
897 sc->sc_dev.dv_xname);
898 goto readerr;
899 }
900 if (inb(iobase + mcd_xfer) & MCD_ST_BUSY) {
901 timeout(mcd_doread, mbx, hz / 100);
902 return;
903 }
904 mcd_setflags(sc);
905 MCD_TRACE("doread: got WAITSTAT delay=%d\n",
906 RDELAY_WAITSTAT - mbx->count, 0, 0, 0);
907
908 /* Reject, if audio active. */
909 if (sc->status & MCDAUDIOBSY) {
910 printf("%s: audio is active\n",
911 sc->sc_dev.dv_xname);
912 goto readerr;
913 }
914
915 mcd_put(iobase + mcd_command, MCD_CMDSETMODE);
916 mcd_put(iobase + mcd_command, MCD_MD_COOKED);
917
918 mbx->sz = sc->blksize;
919 mbx->count = RDELAY_WAITMODE;
920 mbx->state = MCD_S_WAITMODE;
921 timeout(mcd_doread, mbx, hz / 100);
922 return;
923
924 case MCD_S_WAITMODE:
925 untimeout(mcd_doread, mbx);
926 if (mbx->count-- < 0) {
927 printf("%s: timeout setting mode\n",
928 sc->sc_dev.dv_xname);
929 goto readerr;
930 }
931 if (inb(iobase + mcd_xfer) & MCD_ST_BUSY) {
932 timeout(mcd_doread, mbx, hz / 100);
933 return;
934 }
935 mcd_setflags(sc);
936 MCD_TRACE("doread: got WAITMODE delay=%d\n",
937 RDELAY_WAITMODE - mbx->count, 0, 0, 0);
938
939 /* For first block. */
940 mbx->nblk = (bp->b_bcount + (mbx->sz - 1)) / mbx->sz;
941 mbx->skip = 0;
942
943 nextblock:
944 blkno = (bp->b_blkno / (mbx->sz / DEV_BSIZE)) + mbx->p_offset +
945 (mbx->skip / mbx->sz);
946
947 MCD_TRACE("doread: read blkno=%d for bp=0x%x\n", blkno, bp, 0,
948 0);
949
950 /* Build parameter block. */
951 hsg2msf(blkno, rbuf.start_msf);
952
953 /* Send the read command. */
954 mcd_put(iobase + mcd_command, MCD_CMDREAD2);
955 mcd_put(iobase + mcd_command, rbuf.start_msf[0]);
956 mcd_put(iobase + mcd_command, rbuf.start_msf[1]);
957 mcd_put(iobase + mcd_command, rbuf.start_msf[2]);
958 mcd_put(iobase + mcd_command, 0);
959 mcd_put(iobase + mcd_command, 0);
960 mcd_put(iobase + mcd_command, 1);
961
962 mbx->count = RDELAY_WAITREAD;
963 mbx->state = MCD_S_WAITREAD;
964 timeout(mcd_doread, mbx, hz / 100);
965 return;
966
967 case MCD_S_WAITREAD:
968 untimeout(mcd_doread, mbx);
969 if (mbx->count-- < 0) {
970 printf("%s: timeout reading data\n",
971 sc->sc_dev.dv_xname);
972 goto readerr;
973 }
974
975 k = inb(iobase + mcd_xfer);
976 if ((k & 2) == 0) { /* XXX MCD_ST_AUDIOBSY? */
977 MCD_TRACE("doread: got data delay=%d\n",
978 RDELAY_WAITREAD - mbx->count, 0, 0, 0);
979 /* Data is ready. */
980 addr = bp->b_data + mbx->skip;
981 outb(iobase + mcd_ctl2, 0x04); /* XXX */
982 for (i = 0; i < mbx->sz; i++)
983 *addr++ = inb(iobase + mcd_rdata);
984 outb(iobase + mcd_ctl2, 0x0c); /* XXX */
985
986 if (--mbx->nblk > 0) {
987 mbx->skip += mbx->sz;
988 goto nextblock;
989 }
990
991 /* Return buffer. */
992 bp->b_resid = 0;
993 biodone(bp);
994
995 sc->flags &= ~MCDMBXBSY;
996 mcd_start(sc);
997 return;
998 }
999 if ((k & MCD_ST_BUSY) == 0)
1000 mcd_getstat(sc, 0);
1001 timeout(mcd_doread, mbx, hz / 100);
1002 return;
1003 }
1004
1005 readerr:
1006 if (mbx->retry-- > 0) {
1007 printf("%s: retrying\n", sc->sc_dev.dv_xname);
1008 mbx->state = MCD_S_BEGIN;
1009 goto loop;
1010 }
1011
1012 /* Invalidate the buffer. */
1013 bp->b_flags |= B_ERROR;
1014 bp->b_resid = bp->b_bcount;
1015 biodone(bp);
1016 mcd_start(sc);
1017
1018 #ifdef notyet
1019 printf("%s: unit timeout; resetting\n", sc->sc_dev.dv_xname);
1020 outb(mbx->iobase + mcd_reset, MCD_CMDRESET);
1021 delay(300000);
1022 (void)mcd_getstat(sc, 1);
1023 (void)mcd_getstat(sc, 1);
1024 /*sc->status &= ~MCDDSKCHNG; */
1025 sc->debug = 1; /* preventive set debug mode */
1026 #endif
1027 }
1028
1029 int
1030 mcd_setmode(sc, mode)
1031 struct mcd_softc *sc;
1032 int mode;
1033 {
1034 int iobase = sc->iobase;
1035 int retry;
1036
1037 printf("%s: setting mode to %d\n", sc->sc_dev.dv_xname, mode);
1038 for (retry = MCD_RETRIES; retry; retry--) {
1039 outb(iobase + mcd_command, MCD_CMDSETMODE);
1040 outb(iobase + mcd_command, mode);
1041 if (mcd_getstat(sc, 0) != -1)
1042 return 0;
1043 }
1044
1045 return -1;
1046 }
1047
1048 int
1049 mcd_toc_header(sc, th)
1050 struct mcd_softc *sc;
1051 struct ioc_toc_header *th;
1052 {
1053
1054 if (mcd_volinfo(sc) < 0)
1055 return ENXIO;
1056
1057 th->len = msf2hsg(sc->volinfo.vol_msf);
1058 th->starting_track = bcd2bin(sc->volinfo.trk_low);
1059 th->ending_track = bcd2bin(sc->volinfo.trk_high);
1060
1061 return 0;
1062 }
1063
1064 int
1065 mcd_read_toc(sc)
1066 struct mcd_softc *sc;
1067 {
1068 struct ioc_toc_header th;
1069 struct mcd_qchninfo q;
1070 int rc, trk, idx, retry;
1071
1072 /* Only read TOC if needed. */
1073 if (sc->flags & MCDTOC)
1074 return 0;
1075
1076 if (sc->debug)
1077 printf("%s: read_toc: reading toc header\n",
1078 sc->sc_dev.dv_xname);
1079 if (mcd_toc_header(sc, &th) != 0)
1080 return ENXIO;
1081
1082 if (sc->debug)
1083 printf("%s: read_toc: stopping play\n", sc->sc_dev.dv_xname);
1084 if ((rc = mcd_stop(sc)) != 0)
1085 return rc;
1086
1087 /* Try setting the mode twice. */
1088 if (mcd_setmode(sc, MCD_MD_TOC) != 0)
1089 return EIO;
1090 if (mcd_setmode(sc, MCD_MD_TOC) != 0)
1091 return EIO;
1092
1093 if (sc->debug)
1094 printf("%s: read_toc: reading qchannel info\n",
1095 sc->sc_dev.dv_xname);
1096 for (trk = th.starting_track; trk <= th.ending_track; trk++)
1097 sc->toc[trk].idx_no = 0;
1098 trk = th.ending_track - th.starting_track + 1;
1099 for (retry = 300; retry && trk > 0; retry--) {
1100 if (mcd_getqchan(sc, &q) < 0)
1101 break;
1102 idx = bcd2bin(q.idx_no);
1103 if (idx > 0 && idx < MCD_MAXTOCS && q.trk_no == 0 &&
1104 sc->toc[idx].idx_no == 0) {
1105 sc->toc[idx] = q;
1106 trk--;
1107 }
1108 }
1109
1110 if (mcd_setmode(sc, MCD_MD_COOKED) != 0)
1111 return EIO;
1112
1113 if (trk != 0)
1114 return ENXIO;
1115
1116 /* Add a fake last+1. */
1117 idx = th.ending_track + 1;
1118 sc->toc[idx].ctrl_adr = sc->toc[idx-1].ctrl_adr;
1119 sc->toc[idx].trk_no = 0;
1120 sc->toc[idx].idx_no = 0xaa;
1121 sc->toc[idx].hd_pos_msf[0] = sc->volinfo.vol_msf[0];
1122 sc->toc[idx].hd_pos_msf[1] = sc->volinfo.vol_msf[1];
1123 sc->toc[idx].hd_pos_msf[2] = sc->volinfo.vol_msf[2];
1124
1125 sc->flags |= MCDTOC;
1126 return 0;
1127 }
1128
1129 int
1130 mcd_toc_entry(sc, te)
1131 struct mcd_softc *sc;
1132 struct ioc_read_toc_entry *te;
1133 {
1134 struct ret_toc {
1135 struct ioc_toc_header th;
1136 struct cd_toc_entry rt;
1137 } ret_toc;
1138 struct ioc_toc_header th;
1139 int rc, i;
1140
1141 /* Make sure we have a valid TOC. */
1142 if ((rc = mcd_read_toc(sc)) != 0)
1143 return rc;
1144
1145 /* Find the TOC to copy. */
1146 i = te->starting_track;
1147 if (i == MCD_LASTPLUS1)
1148 i = bcd2bin(sc->volinfo.trk_high) + 1;
1149
1150 /* Verify starting track. */
1151 if (i < bcd2bin(sc->volinfo.trk_low) ||
1152 i > bcd2bin(sc->volinfo.trk_high) + 1)
1153 return EINVAL;
1154
1155 /* Do we have room? */
1156 if (te->data_len < sizeof(struct ioc_toc_header) +
1157 sizeof(struct cd_toc_entry))
1158 return EINVAL;
1159
1160 /* Copy the TOC header. */
1161 if (mcd_toc_header(sc, &th) < 0)
1162 return EIO;
1163 ret_toc.th = th;
1164
1165 /* Copy the TOC data. */
1166 ret_toc.rt.control = sc->toc[i].ctrl_adr;
1167 ret_toc.rt.addr_type = te->address_format;
1168 ret_toc.rt.track = i;
1169 if (te->address_format == CD_MSF_FORMAT) {
1170 ret_toc.rt.addr[1] = sc->toc[i].hd_pos_msf[0];
1171 ret_toc.rt.addr[2] = sc->toc[i].hd_pos_msf[1];
1172 ret_toc.rt.addr[3] = sc->toc[i].hd_pos_msf[2];
1173 }
1174
1175 /* Copy the data back. */
1176 copyout(&ret_toc, te->data,
1177 sizeof(struct cd_toc_entry) + sizeof(struct ioc_toc_header));
1178
1179 return 0;
1180 }
1181
1182 int
1183 mcd_stop(sc)
1184 struct mcd_softc *sc;
1185 {
1186
1187 if (mcd_send(sc, MCD_CMDSTOPAUDIO, MCD_RETRIES) < 0)
1188 return ENXIO;
1189 sc->audio_status = CD_AS_PLAY_COMPLETED;
1190 return 0;
1191 }
1192
1193 int
1194 mcd_getqchan(sc, q)
1195 struct mcd_softc *sc;
1196 struct mcd_qchninfo *q;
1197 {
1198
1199 if (mcd_send(sc, MCD_CMDGETQCHN, MCD_RETRIES) < 0)
1200 return -1;
1201 if (mcd_get(sc, (char *) q, sizeof(struct mcd_qchninfo)) < 0)
1202 return -1;
1203 if (sc->debug)
1204 printf("%s: getqchan: ctl=%d t=%d i=%d ttm=%d:%d.%d dtm=%d:%d.%d\n",
1205 sc->sc_dev.dv_xname, q->ctrl_adr, q->trk_no, q->idx_no,
1206 q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2],
1207 q->trk_size_msf[0], q->trk_size_msf[1], q->trk_size_msf[2]);
1208 return 0;
1209 }
1210
1211 int
1212 mcd_subchan(sc, ch)
1213 struct mcd_softc *sc;
1214 struct ioc_read_subchannel *ch;
1215 {
1216 struct mcd_qchninfo q;
1217 struct cd_sub_channel_info data;
1218
1219 if (sc->debug)
1220 printf("%s: subchan: af=%d df=%d\n", sc->sc_dev.dv_xname,
1221 ch->address_format, ch->data_format);
1222
1223 if (ch->address_format != CD_MSF_FORMAT)
1224 return EIO;
1225 if (ch->data_format != CD_CURRENT_POSITION)
1226 return EIO;
1227 if (mcd_getqchan(sc, &q) < 0)
1228 return EIO;
1229
1230 data.header.audio_status = sc->audio_status;
1231 data.what.position.data_format = CD_MSF_FORMAT;
1232 data.what.position.track_number = bcd2bin(q.trk_no);
1233
1234 if (copyout(&data, ch->data, sizeof(struct cd_sub_channel_info)) != 0)
1235 return EFAULT;
1236 return 0;
1237 }
1238
1239 int
1240 mcd_playtracks(sc, pt)
1241 struct mcd_softc *sc;
1242 struct ioc_play_track *pt;
1243 {
1244 struct mcd_read2 pb;
1245 int a = pt->start_track;
1246 int z = pt->end_track;
1247 int rc;
1248
1249 if ((rc = mcd_read_toc(sc)) != 0)
1250 return rc;
1251
1252 printf("%s: playtracks: from %d:%d to %d:%d\n", sc->sc_dev.dv_xname,
1253 a, pt->start_index, z, pt->end_index);
1254
1255 if (a < sc->volinfo.trk_low || a > sc->volinfo.trk_high || a > z ||
1256 z < sc->volinfo.trk_low || z > sc->volinfo.trk_high)
1257 return EINVAL;
1258
1259 pb.start_msf[0] = sc->toc[a].hd_pos_msf[0];
1260 pb.start_msf[1] = sc->toc[a].hd_pos_msf[1];
1261 pb.start_msf[2] = sc->toc[a].hd_pos_msf[2];
1262 pb.end_msf[0] = sc->toc[z+1].hd_pos_msf[0];
1263 pb.end_msf[1] = sc->toc[z+1].hd_pos_msf[1];
1264 pb.end_msf[2] = sc->toc[z+1].hd_pos_msf[2];
1265
1266 return mcd_play(sc, &pb);
1267 }
1268
1269 int
1270 mcd_play(sc, pb)
1271 struct mcd_softc *sc;
1272 struct mcd_read2 *pb;
1273 {
1274 int iobase = sc->iobase;
1275 int retry, st;
1276
1277 sc->lastpb = *pb;
1278 for (retry = MCD_RETRIES; retry; retry--) {
1279 outb(iobase + mcd_command, MCD_CMDREAD2);
1280 outb(iobase + mcd_command, pb->start_msf[0]);
1281 outb(iobase + mcd_command, pb->start_msf[1]);
1282 outb(iobase + mcd_command, pb->start_msf[2]);
1283 outb(iobase + mcd_command, pb->end_msf[0]);
1284 outb(iobase + mcd_command, pb->end_msf[1]);
1285 outb(iobase + mcd_command, pb->end_msf[2]);
1286 if ((st = mcd_getstat(sc, 0)) != -1)
1287 break;
1288 }
1289 if (sc->debug)
1290 printf("%s: play: retry=%d status=%d\n", sc->sc_dev.dv_xname,
1291 retry, st);
1292 if (!retry)
1293 return ENXIO;
1294
1295 sc->audio_status = CD_AS_PLAY_IN_PROGRESS;
1296 return 0;
1297 }
1298
1299 int
1300 mcd_pause(sc)
1301 struct mcd_softc *sc;
1302 {
1303 struct mcd_qchninfo q;
1304 int rc;
1305
1306 /* Verify current status. */
1307 if (sc->audio_status != CD_AS_PLAY_IN_PROGRESS) {
1308 printf("%s: pause: attempted when not playing\n",
1309 sc->sc_dev.dv_xname);
1310 return EINVAL;
1311 }
1312
1313 /* Get the current position. */
1314 if (mcd_getqchan(sc, &q) < 0)
1315 return EIO;
1316
1317 /* Copy it into lastpb. */
1318 sc->lastpb.start_msf[0] = q.hd_pos_msf[0];
1319 sc->lastpb.start_msf[1] = q.hd_pos_msf[1];
1320 sc->lastpb.start_msf[2] = q.hd_pos_msf[2];
1321
1322 /* Stop playing. */
1323 if ((rc = mcd_stop(sc)) != 0)
1324 return rc;
1325
1326 /* Set the proper status and exit. */
1327 sc->audio_status = CD_AS_PLAY_PAUSED;
1328 return 0;
1329 }
1330
1331 int
1332 mcd_resume(sc)
1333 struct mcd_softc *sc;
1334 {
1335
1336 if (sc->audio_status != CD_AS_PLAY_PAUSED)
1337 return EINVAL;
1338 return mcd_play(sc, &sc->lastpb);
1339 }
1340