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