mcd.c revision 1.28 1 /* $NetBSD: mcd.c,v 1.28 1995/03/27 15:45:20 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1993, 1994, 1995 Charles M. Hannum. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by Charles M. Hannum.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * Copyright 1993 by Holger Veit (data part)
21 * Copyright 1993 by Brian Moore (audio part)
22 * All rights reserved.
23 *
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
26 * are met:
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. Redistributions in binary form must reproduce the above copyright
30 * notice, this list of conditions and the following disclaimer in the
31 * documentation and/or other materials provided with the distribution.
32 * 3. All advertising materials mentioning features or use of this software
33 * must display the following acknowledgement:
34 * This software was developed by Holger Veit and Brian Moore
35 * for use with "386BSD" and similar operating systems.
36 * "Similar operating systems" includes mainly non-profit oriented
37 * systems for research and education, including but not restricted to
38 * "NetBSD", "FreeBSD", "Mach" (by CMU).
39 * 4. Neither the name of the developer(s) nor the name "386BSD"
40 * may be used to endorse or promote products derived from this
41 * software without specific prior written permission.
42 *
43 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY
44 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
46 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE
47 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
48 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
49 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
50 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
51 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
52 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
53 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54 */
55
56 /*static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore";*/
57
58 #include <sys/types.h>
59 #include <sys/param.h>
60 #include <sys/systm.h>
61 #include <sys/kernel.h>
62 #include <sys/proc.h>
63 #include <sys/conf.h>
64 #include <sys/file.h>
65 #include <sys/buf.h>
66 #include <sys/stat.h>
67 #include <sys/uio.h>
68 #include <sys/ioctl.h>
69 #include <sys/cdio.h>
70 #include <sys/errno.h>
71 #include <sys/disklabel.h>
72 #include <sys/device.h>
73 #include <sys/disk.h>
74
75 #include <machine/cpu.h>
76 #include <machine/pio.h>
77
78 #include <i386/isa/isavar.h>
79 #include <dev/isa/mcdreg.h>
80
81 #ifndef MCDDEBUG
82 #define MCD_TRACE(fmt,a,b,c,d)
83 #else
84 #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);}}
85 #endif
86
87 #define MCDPART(dev) DISKPART(dev)
88 #define MCDUNIT(dev) DISKUNIT(dev)
89
90 /* toc */
91 #define MCD_MAXTOCS 104 /* from the Linux driver */
92
93 struct mcd_mbx {
94 int retry, count;
95 struct buf *bp;
96 daddr_t blkno;
97 int nblk;
98 int sz;
99 u_long skip;
100 int state;
101 #define MCD_S_IDLE 0
102 #define MCD_S_BEGIN 1
103 #define MCD_S_WAITSTAT 2
104 #define MCD_S_WAITMODE 3
105 #define MCD_S_WAITREAD 4
106 int mode;
107 };
108
109 struct mcd_softc {
110 struct device sc_dev;
111 struct dkdevice sc_dk;
112 struct intrhand sc_ih;
113
114 int iobase;
115 int irq, drq;
116
117 char *type;
118 int flags;
119 #define MCDF_LOCKED 0x01
120 #define MCDF_WANTED 0x02
121 #define MCDF_WLABEL 0x04 /* label is writable */
122 #define MCDF_LABELLING 0x08 /* writing label */
123 #define MCDF_LOADED 0x10 /* parameters loaded */
124 #define MCDF_HAVETOC 0x20 /* already read toc */
125 short status;
126 short audio_status;
127 int blksize;
128 u_long disksize;
129 struct mcd_volinfo volinfo;
130 union mcd_qchninfo toc[MCD_MAXTOCS];
131 struct mcd_command lastpb;
132 struct mcd_mbx mbx;
133 int lastmode;
134 #define MCD_MD_UNKNOWN -1
135 int lastupc;
136 #define MCD_UPC_UNKNOWN -1
137 int debug;
138 struct buf buf_queue;
139 };
140
141 /* prototypes */
142 int mcdopen __P((dev_t, int, int, struct proc *));
143 int mcdclose __P((dev_t, int, int));
144 int mcdioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
145 int mcdsize __P((dev_t));
146
147 static int bcd2bin __P((bcd_t));
148 static bcd_t bin2bcd __P((int));
149 static void hsg2msf __P((int, bcd_t *));
150 static daddr_t msf2hsg __P((bcd_t *, int));
151
152 int mcd_playtracks __P((struct mcd_softc *, struct ioc_play_track *));
153 int mcd_playmsf __P((struct mcd_softc *, struct ioc_play_msf *));
154 int mcd_playblocks __P((struct mcd_softc *, struct ioc_play_blocks *));
155 int mcd_stop __P((struct mcd_softc *));
156 int mcd_eject __P((struct mcd_softc *));
157 int mcd_read_subchannel __P((struct mcd_softc *, struct ioc_read_subchannel *));
158 int mcd_pause __P((struct mcd_softc *));
159 int mcd_resume __P((struct mcd_softc *));
160 int mcd_toc_header __P((struct mcd_softc *, struct ioc_toc_header *));
161 int mcd_toc_entries __P((struct mcd_softc *, struct ioc_read_toc_entry *));
162
163 int mcd_getreply __P((struct mcd_softc *));
164 int mcd_getstat __P((struct mcd_softc *));
165 int mcd_getresult __P((struct mcd_softc *, struct mcd_result *));
166 void mcd_setflags __P((struct mcd_softc *));
167 int mcd_get __P((struct mcd_softc *, char *, int));
168 int mcd_send __P((struct mcd_softc *, struct mcd_mbox *, int));
169 int mcdintr __P((struct mcd_softc *));
170 void mcd_soft_reset __P((struct mcd_softc *));
171 int mcd_hard_reset __P((struct mcd_softc *));
172 int mcd_setmode __P((struct mcd_softc *, int));
173 int mcd_setupc __P((struct mcd_softc *, int));
174 void mcd_fsm __P((void *));
175 int mcd_read_toc __P((struct mcd_softc *));
176 int mcd_getqchan __P((struct mcd_softc *, union mcd_qchninfo *, int));
177 int mcd_setlock __P((struct mcd_softc *, int));
178
179 int mcdprobe __P((struct device *, void *, void *));
180 void mcdattach __P((struct device *, struct device *, void *));
181
182 struct cfdriver mcdcd = {
183 NULL, "mcd", mcdprobe, mcdattach, DV_DISK, sizeof(struct mcd_softc)
184 };
185
186 void mcdgetdisklabel __P((struct mcd_softc *));
187 int mcd_get_parms __P((struct mcd_softc *));
188 void mcdstrategy __P((struct buf *));
189 void mcdstart __P((struct mcd_softc *));
190
191 struct dkdriver mcddkdriver = { mcdstrategy };
192
193 #define MCD_RETRIES 3
194 #define MCD_RDRETRIES 3
195
196 /* several delays */
197 #define RDELAY_WAITSTAT 300
198 #define RDELAY_WAITMODE 300
199 #define RDELAY_WAITREAD 800
200
201 #define DELAY_GRANULARITY 25 /* 25us */
202 #define DELAY_GETREPLY 100000 /* 100000 * 25us */
203
204 void
205 mcdattach(parent, self, aux)
206 struct device *parent, *self;
207 void *aux;
208 {
209 struct mcd_softc *sc = (void *)self;
210 struct isa_attach_args *ia = aux;
211 struct mcd_mbox mbx;
212
213 printf(": model %s\n", sc->type != 0 ? sc->type : "unknown");
214
215 mbx.cmd.opcode = MCD_CMDSETLOCK;
216 mbx.cmd.length = sizeof(mbx.cmd.data.lockmode);
217 mbx.cmd.data.lockmode.mode = MCD_LK_UNLOCK;
218 mbx.res.length = 0;
219 (void) mcd_send(sc, &mbx, 0);
220
221 mbx.cmd.opcode = MCD_CMDCONFIGDRIVE;
222 mbx.cmd.length = sizeof(mbx.cmd.data.config) - 1;
223 mbx.cmd.data.config.subcommand = MCD_CF_IRQENABLE;
224 mbx.cmd.data.config.data1 = 0x01;
225 mbx.res.length = 0;
226 (void) mcd_send(sc, &mbx, 0);
227
228 mcd_soft_reset(sc);
229
230 sc->sc_dk.dk_driver = &mcddkdriver;
231
232 sc->sc_ih.ih_fun = mcdintr;
233 sc->sc_ih.ih_arg = sc;
234 sc->sc_ih.ih_level = IPL_BIO;
235 intr_establish(ia->ia_irq, IST_EDGE, &sc->sc_ih);
236 }
237
238 int
239 mcdlockwait(sc)
240 struct mcd_softc *sc;
241 {
242 int error;
243
244 while ((sc->flags & MCDF_LOCKED) != 0) {
245 sc->flags |= MCDF_WANTED;
246 if ((error = tsleep(sc, PRIBIO | PCATCH, "mcdlck", 0)) != 0)
247 return error;
248 }
249 return 0;
250 }
251
252 void
253 mcdunlock(sc)
254 struct mcd_softc *sc;
255 {
256
257 sc->flags &= ~MCDF_LOCKED;
258 if ((sc->flags & MCDF_WANTED) != 0) {
259 sc->flags &= ~MCDF_WANTED;
260 wakeup(sc);
261 }
262 }
263
264 int
265 mcdopen(dev, flag, fmt, p)
266 dev_t dev;
267 int flag, fmt;
268 struct proc *p;
269 {
270 int error;
271 int unit, part;
272 struct mcd_softc *sc;
273
274 unit = MCDUNIT(dev);
275 if (unit >= mcdcd.cd_ndevs)
276 return ENXIO;
277 sc = mcdcd.cd_devs[unit];
278 if (!sc)
279 return ENXIO;
280
281 part = MCDPART(dev);
282
283 if (error = mcdlockwait(sc))
284 return error;
285
286 if (sc->sc_dk.dk_openmask != 0) {
287 /*
288 * If any partition is open, but the disk has been invalidated,
289 * disallow further opens.
290 */
291 if ((sc->flags & MCDF_LOADED) == 0)
292 return ENXIO;
293 } else {
294 sc->flags |= MCDF_LOCKED;
295
296 /*
297 * Lock the drawer. This will also notice any pending disk
298 * change or door open indicator and clear the MCDF_LOADED bit
299 * if necessary.
300 */
301 (void) mcd_setlock(sc, MCD_LK_LOCK);
302
303 if ((sc->flags & MCDF_LOADED) == 0) {
304 /* Partially reset the state. */
305 sc->flags &= ~MCDF_HAVETOC;
306 sc->lastmode = MCD_MD_UNKNOWN;
307 sc->lastupc = MCD_UPC_UNKNOWN;
308
309 sc->flags |= MCDF_LOADED;
310
311 /* Set the mode, causing the disk to spin up. */
312 if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0)
313 goto bad2;
314
315 /* Load the physical device parameters. */
316 if (mcd_get_parms(sc) != 0) {
317 error = ENXIO;
318 goto bad2;
319 }
320
321 /* Fabricate a disk label. */
322 mcdgetdisklabel(sc);
323 }
324
325 mcdunlock(sc);
326 }
327
328 MCD_TRACE("open: partition=%d disksize=%d blksize=%d\n", part,
329 sc->disksize, sc->blksize, 0);
330
331 /* Check that the partition exists. */
332 if (part != RAW_PART &&
333 (part >= sc->sc_dk.dk_label.d_npartitions ||
334 sc->sc_dk.dk_label.d_partitions[part].p_fstype == FS_UNUSED)) {
335 error = ENXIO;
336 goto bad;
337 }
338
339 /* Insure only one open at a time. */
340 switch (fmt) {
341 case S_IFCHR:
342 sc->sc_dk.dk_copenmask |= (1 << part);
343 break;
344 case S_IFBLK:
345 sc->sc_dk.dk_bopenmask |= (1 << part);
346 break;
347 }
348 sc->sc_dk.dk_openmask = sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
349
350 return 0;
351
352 bad2:
353 sc->flags &= ~MCDF_LOADED;
354
355 bad:
356 if (sc->sc_dk.dk_openmask == 0) {
357 #if 0
358 (void) mcd_setmode(sc, MCD_MD_SLEEP);
359 #endif
360 (void) mcd_setlock(sc, MCD_LK_UNLOCK);
361
362 mcdunlock(sc);
363 }
364
365 return error;
366 }
367
368 int
369 mcdclose(dev, flag, fmt)
370 dev_t dev;
371 int flag, fmt;
372 {
373 struct mcd_softc *sc = mcdcd.cd_devs[MCDUNIT(dev)];
374 int part = MCDPART(dev);
375 int s;
376
377 MCD_TRACE("close: partition=%d\n", part, 0, 0, 0);
378
379 switch (fmt) {
380 case S_IFCHR:
381 sc->sc_dk.dk_copenmask &= ~(1 << part);
382 break;
383 case S_IFBLK:
384 sc->sc_dk.dk_bopenmask &= ~(1 << part);
385 break;
386 }
387 sc->sc_dk.dk_openmask = sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
388
389 if (sc->sc_dk.dk_openmask == 0) {
390 /*
391 * If we're closing the last partition, nobody else could be
392 * holding the lock, so don't bother to check.
393 */
394 sc->flags |= MCDF_LOCKED;
395
396 #if 0
397 s = splbio();
398 while (...) {
399 sc->flags |= MCDF_WAITING;
400 if ((error = tsleep(sc, PRIBIO | PCATCH, "mcdcls", 0)) != 0)
401 return error;
402 }
403 splx(s);
404 #endif
405
406 #if 0
407 (void) mcd_setmode(sc, MCD_MD_SLEEP);
408 #endif
409 (void) mcd_setlock(sc, MCD_LK_UNLOCK);
410
411 mcdunlock(sc);
412 }
413
414 return 0;
415 }
416
417 void
418 mcdstrategy(bp)
419 struct buf *bp;
420 {
421 struct mcd_softc *sc = mcdcd.cd_devs[MCDUNIT(bp->b_dev)];
422 int s;
423
424 /* Test validity. */
425 MCD_TRACE("strategy: buf=0x%lx blkno=%ld bcount=%ld\n", bp,
426 bp->b_blkno, bp->b_bcount, 0);
427 if (bp->b_blkno < 0 ||
428 (bp->b_bcount % sc->blksize) != 0) {
429 printf("%s: strategy: blkno = %d bcount = %d\n",
430 sc->sc_dev.dv_xname, bp->b_blkno, bp->b_bcount);
431 bp->b_error = EINVAL;
432 goto bad;
433 }
434
435 /* If device invalidated (e.g. media change, door open), error. */
436 if ((sc->flags & MCDF_LOADED) == 0) {
437 MCD_TRACE("strategy: drive not valid\n", 0, 0, 0, 0);
438 bp->b_error = EIO;
439 goto bad;
440 }
441
442 /* No data to read. */
443 if (bp->b_bcount == 0)
444 goto done;
445
446 /*
447 * Do bounds checking, adjust transfer. if error, process.
448 * If end of partition, just return.
449 */
450 if (bounds_check_with_label(bp, &sc->sc_dk.dk_label,
451 (sc->flags & (MCDF_WLABEL|MCDF_LABELLING)) != 0) <= 0)
452 goto done;
453
454 /* Queue it. */
455 s = splbio();
456 disksort(&sc->buf_queue, bp);
457 splx(s);
458 if (!sc->buf_queue.b_active)
459 mcdstart(sc);
460 return;
461
462 bad:
463 bp->b_flags |= B_ERROR;
464 done:
465 bp->b_resid = bp->b_bcount;
466 biodone(bp);
467 }
468
469 void
470 mcdstart(sc)
471 struct mcd_softc *sc;
472 {
473 struct buf *bp, *dp = &sc->buf_queue;
474 int s;
475
476 loop:
477 s = splbio();
478
479 bp = dp->b_actf;
480 if (bp == NULL) {
481 /* Nothing to do. */
482 dp->b_active = 0;
483 splx(s);
484 return;
485 }
486
487 /* Block found to process; dequeue. */
488 MCD_TRACE("start: found block bp=0x%x\n", bp, 0, 0, 0);
489 dp->b_actf = bp->b_actf;
490 splx(s);
491
492 /* Changed media? */
493 if ((sc->flags & MCDF_LOADED) == 0) {
494 MCD_TRACE("start: drive not valid\n", 0, 0, 0, 0);
495 bp->b_error = EIO;
496 bp->b_flags |= B_ERROR;
497 biodone(bp);
498 goto loop;
499 }
500
501 dp->b_active = 1;
502
503 sc->mbx.retry = MCD_RDRETRIES;
504 sc->mbx.bp = bp;
505 sc->mbx.blkno = bp->b_blkno / (sc->blksize / DEV_BSIZE);
506 if (MCDPART(bp->b_dev) != RAW_PART) {
507 struct partition *p;
508 p = &sc->sc_dk.dk_label.d_partitions[MCDPART(bp->b_dev)];
509 sc->mbx.blkno += p->p_offset;
510 }
511 sc->mbx.nblk = bp->b_bcount / sc->blksize;
512 sc->mbx.sz = sc->blksize;
513 sc->mbx.skip = 0;
514 sc->mbx.state = MCD_S_BEGIN;
515 sc->mbx.mode = MCD_MD_COOKED;
516
517 s = splbio();
518 mcd_fsm(sc);
519 splx(s);
520 }
521
522 int
523 mcdioctl(dev, cmd, addr, flag, p)
524 dev_t dev;
525 u_long cmd;
526 caddr_t addr;
527 int flag;
528 struct proc *p;
529 {
530 struct mcd_softc *sc = mcdcd.cd_devs[MCDUNIT(dev)];
531 int error;
532
533 MCD_TRACE("ioctl: cmd=0x%x\n", cmd, 0, 0, 0);
534
535 if ((sc->flags & MCDF_LOADED) == 0)
536 return EIO;
537
538 switch (cmd) {
539 case DIOCSBAD:
540 return EINVAL;
541
542 case DIOCGDINFO:
543 *(struct disklabel *)addr = sc->sc_dk.dk_label;
544 return 0;
545
546 case DIOCGPART:
547 ((struct partinfo *)addr)->disklab = &sc->sc_dk.dk_label;
548 ((struct partinfo *)addr)->part =
549 &sc->sc_dk.dk_label.d_partitions[MCDPART(dev)];
550 return 0;
551
552 case DIOCWDINFO:
553 case DIOCSDINFO:
554 if ((flag & FWRITE) == 0)
555 return EBADF;
556
557 if (error = mcdlockwait(sc))
558 return error;
559 sc->flags |= MCDF_LOCKED | MCDF_LABELLING;
560
561 error = setdisklabel(&sc->sc_dk.dk_label,
562 (struct disklabel *)addr, /*sc->sc_dk.dk_openmask : */0,
563 &sc->sc_dk.dk_cpulabel);
564 if (error == 0) {
565 }
566
567 sc->flags &= ~MCDF_LABELLING;
568 mcdunlock(sc);
569 return error;
570
571 case DIOCWLABEL:
572 return EBADF;
573
574 case CDIOCPLAYTRACKS:
575 return mcd_playtracks(sc, (struct ioc_play_track *)addr);
576 case CDIOCPLAYMSF:
577 return mcd_playmsf(sc, (struct ioc_play_msf *)addr);
578 case CDIOCPLAYBLOCKS:
579 return mcd_playblocks(sc, (struct ioc_play_blocks *)addr);
580 case CDIOCREADSUBCHANNEL:
581 return mcd_read_subchannel(sc, (struct ioc_read_subchannel *)addr);
582 case CDIOREADTOCHEADER:
583 return mcd_toc_header(sc, (struct ioc_toc_header *)addr);
584 case CDIOREADTOCENTRYS:
585 return mcd_toc_entries(sc, (struct ioc_read_toc_entry *)addr);
586 case CDIOCSETPATCH:
587 case CDIOCGETVOL:
588 case CDIOCSETVOL:
589 case CDIOCSETMONO:
590 case CDIOCSETSTEREO:
591 case CDIOCSETMUTE:
592 case CDIOCSETLEFT:
593 case CDIOCSETRIGHT:
594 return EINVAL;
595 case CDIOCRESUME:
596 return mcd_resume(sc);
597 case CDIOCPAUSE:
598 return mcd_pause(sc);
599 case CDIOCSTART:
600 return EINVAL;
601 case CDIOCSTOP:
602 return mcd_stop(sc);
603 case CDIOCEJECT:
604 return mcd_eject(sc);
605 case CDIOCALLOW:
606 return mcd_setlock(sc, MCD_LK_UNLOCK);
607 case CDIOCPREVENT:
608 return mcd_setlock(sc, MCD_LK_LOCK);
609 case CDIOCSETDEBUG:
610 sc->debug = 1;
611 return 0;
612 case CDIOCCLRDEBUG:
613 sc->debug = 0;
614 return 0;
615 case CDIOCRESET:
616 return mcd_hard_reset(sc);
617 default:
618 return ENOTTY;
619 }
620
621 #ifdef DIAGNOSTIC
622 panic("mcdioctl: impossible");
623 #endif
624 }
625
626 /*
627 * This could have been taken from scsi/cd.c, but it is not clear
628 * whether the scsi cd driver is linked in.
629 */
630 void
631 mcdgetdisklabel(sc)
632 struct mcd_softc *sc;
633 {
634
635 bzero(&sc->sc_dk.dk_label, sizeof(struct disklabel));
636 bzero(&sc->sc_dk.dk_cpulabel, sizeof(struct cpu_disklabel));
637
638 sc->sc_dk.dk_label.d_secsize = sc->blksize;
639 sc->sc_dk.dk_label.d_ntracks = 1;
640 sc->sc_dk.dk_label.d_nsectors = 100;
641 sc->sc_dk.dk_label.d_ncylinders = (sc->disksize / 100) + 1;
642 sc->sc_dk.dk_label.d_secpercyl =
643 sc->sc_dk.dk_label.d_ntracks * sc->sc_dk.dk_label.d_nsectors;
644
645 strncpy(sc->sc_dk.dk_label.d_typename, "Mitsumi CD-ROM", 16);
646 sc->sc_dk.dk_label.d_type = 0; /* XXX */
647 strncpy(sc->sc_dk.dk_label.d_packname, "fictitious", 16);
648 sc->sc_dk.dk_label.d_secperunit = sc->disksize;
649 sc->sc_dk.dk_label.d_rpm = 300;
650 sc->sc_dk.dk_label.d_interleave = 1;
651 sc->sc_dk.dk_label.d_flags = D_REMOVABLE;
652
653 sc->sc_dk.dk_label.d_partitions[0].p_offset = 0;
654 sc->sc_dk.dk_label.d_partitions[0].p_size =
655 sc->sc_dk.dk_label.d_secperunit *
656 (sc->sc_dk.dk_label.d_secsize / DEV_BSIZE);
657 sc->sc_dk.dk_label.d_partitions[0].p_fstype = FS_ISO9660;
658 sc->sc_dk.dk_label.d_npartitions = 1;
659
660 sc->sc_dk.dk_label.d_magic = DISKMAGIC;
661 sc->sc_dk.dk_label.d_magic2 = DISKMAGIC;
662 sc->sc_dk.dk_label.d_checksum = dkcksum(&sc->sc_dk.dk_label);
663 }
664
665 int
666 mcd_get_parms(sc)
667 struct mcd_softc *sc;
668 {
669 struct mcd_mbox mbx;
670 daddr_t size;
671 int error;
672
673 /* Send volume info command. */
674 mbx.cmd.opcode = MCD_CMDGETVOLINFO;
675 mbx.cmd.length = 0;
676 mbx.res.length = sizeof(mbx.res.data.volinfo);
677 if ((error = mcd_send(sc, &mbx, 1)) != 0)
678 return error;
679
680 if (mbx.res.data.volinfo.trk_low == 0x00 &&
681 mbx.res.data.volinfo.trk_high == 0x00)
682 return EINVAL;
683
684 /* Volinfo is OK. */
685 sc->volinfo = mbx.res.data.volinfo;
686 sc->blksize = MCD_BLKSIZE_COOKED;
687 size = msf2hsg(sc->volinfo.vol_msf, 0);
688 sc->disksize = size * (MCD_BLKSIZE_COOKED / DEV_BSIZE);
689 return 0;
690 }
691
692 int
693 mcdsize(dev)
694 dev_t dev;
695 {
696
697 /* CD-ROMs are read-only. */
698 return -1;
699 }
700
701 int
702 mcddump()
703 {
704
705 /* Not implemented. */
706 return EINVAL;
707 }
708
709 int
710 mcdprobe(parent, match, aux)
711 struct device *parent;
712 void *match, *aux;
713 {
714 struct mcd_softc *sc = match;
715 struct isa_attach_args *ia = aux;
716 int iobase = ia->ia_iobase;
717 int irq;
718 int i;
719 struct mcd_mbox mbx;
720
721 sc->iobase = iobase;
722
723 /* Send a reset. */
724 outb(iobase + MCD_RESET, 0);
725 delay(1000000);
726 /* Get any pending status and throw away. */
727 for (i = 10; i; i--)
728 inb(iobase + MCD_STATUS);
729 delay(1000);
730
731 /* Send get status command. */
732 mbx.cmd.opcode = MCD_CMDGETSTAT;
733 mbx.cmd.length = 0;
734 mbx.res.length = 0;
735 if (mcd_send(sc, &mbx, 0) != 0)
736 return 0;
737
738 /* Get info about the drive. */
739 mbx.cmd.opcode = MCD_CMDCONTINFO;
740 mbx.cmd.length = 0;
741 mbx.res.length = sizeof(mbx.res.data.continfo);
742 if (mcd_send(sc, &mbx, 0) != 0)
743 return 0;
744
745 /*
746 * The following is code which is not guaranteed to work for all
747 * drives, because the meaning of the expected 'M' is not clear
748 * (M_itsumi is an obvious assumption, but I don't trust that).
749 * Also, the original hack had a bogus condition that always
750 * returned true.
751 *
752 * Note: Which models support interrupts? >=LU005S?
753 */
754 switch (mbx.res.data.continfo.code) {
755 case 'M':
756 if (mbx.res.data.continfo.version <= 2)
757 sc->type = "LU002S";
758 else if (mbx.res.data.continfo.version <= 5)
759 sc->type = "LU005S";
760 else
761 sc->type = "LU006S";
762 break;
763 case 'F':
764 sc->type = "FX001";
765 break;
766 case 'D':
767 sc->type = "FX001D";
768 break;
769 default:
770 printf("%s: unrecognized drive version %c%02x; will try to use it anyway\n",
771 sc->sc_dev.dv_xname,
772 mbx.res.data.continfo.code, mbx.res.data.continfo.version);
773 sc->type = 0;
774 break;
775 }
776
777 ia->ia_iosize = 4;
778 ia->ia_msize = 0;
779 return 1;
780 }
781
782 int
783 mcd_getreply(sc)
784 struct mcd_softc *sc;
785 {
786 int iobase = sc->iobase;
787 int i;
788
789 /* Wait until xfer port senses data ready. */
790 for (i = DELAY_GETREPLY; i; i--) {
791 if ((inb(iobase + MCD_XFER) & MCD_XF_STATUSUNAVAIL) == 0)
792 break;
793 delay(DELAY_GRANULARITY);
794 }
795 if (!i)
796 return -1;
797
798 /* Get the data. */
799 return inb(iobase + MCD_STATUS);
800 }
801
802 int
803 mcd_getstat(sc)
804 struct mcd_softc *sc;
805 {
806 struct mcd_mbox mbx;
807
808 mbx.cmd.opcode = MCD_CMDGETSTAT;
809 mbx.cmd.length = 0;
810 mbx.res.length = 0;
811 return mcd_send(sc, &mbx, 1);
812 }
813
814 int
815 mcd_getresult(sc, res)
816 struct mcd_softc *sc;
817 struct mcd_result *res;
818 {
819 int i, x;
820
821 if (sc->debug)
822 printf("%s: mcd_getresult: %d", sc->sc_dev.dv_xname,
823 res->length);
824
825 if ((x = mcd_getreply(sc)) < 0) {
826 if (sc->debug)
827 printf(" timeout\n");
828 else
829 printf("%s: timeout in getresult\n", sc->sc_dev.dv_xname);
830 return EIO;
831 }
832 if (sc->debug)
833 printf(" %02x", (u_int)x);
834 sc->status = x;
835 mcd_setflags(sc);
836
837 if ((sc->status & MCD_ST_CMDCHECK) != 0)
838 return EINVAL;
839
840 for (i = 0; i < res->length; i++) {
841 if ((x = mcd_getreply(sc)) < 0) {
842 if (sc->debug)
843 printf(" timeout\n");
844 else
845 printf("%s: timeout in getresult\n", sc->sc_dev.dv_xname);
846 return EIO;
847 }
848 if (sc->debug)
849 printf(" %02x", (u_int)x);
850 res->data.raw.data[i] = x;
851 }
852
853 if (sc->debug)
854 printf(" succeeded\n");
855
856 #if 1
857 delay(10);
858 while ((inb(sc->iobase + MCD_XFER) & MCD_XF_STATUSUNAVAIL) == 0) {
859 x = inb(sc->iobase + MCD_STATUS);
860 printf("%s: got extra byte %02x during getstatus\n",
861 sc->sc_dev.dv_xname, (u_int)x);
862 delay(10);
863 }
864 #endif
865
866 return 0;
867 }
868
869 void
870 mcd_setflags(sc)
871 struct mcd_softc *sc;
872 {
873
874 /* Check flags. */
875 if ((sc->flags & MCDF_LOADED) != 0 &&
876 (sc->status & (MCD_ST_DSKCHNG | MCD_ST_DSKIN | MCD_ST_DOOROPEN)) !=
877 MCD_ST_DSKIN) {
878 if ((sc->status & MCD_ST_DOOROPEN) != 0)
879 printf("%s: door open\n", sc->sc_dev.dv_xname);
880 else if ((sc->status & MCD_ST_DSKIN) == 0)
881 printf("%s: no disk present\n", sc->sc_dev.dv_xname);
882 else if ((sc->status & MCD_ST_DSKCHNG) != 0)
883 printf("%s: media change\n", sc->sc_dev.dv_xname);
884 sc->flags &= ~MCDF_LOADED;
885 }
886
887 if ((sc->status & MCD_ST_AUDIOBSY) != 0)
888 sc->audio_status = CD_AS_PLAY_IN_PROGRESS;
889 else if (sc->audio_status == CD_AS_PLAY_IN_PROGRESS ||
890 sc->audio_status == CD_AS_AUDIO_INVALID)
891 sc->audio_status = CD_AS_PLAY_COMPLETED;
892 }
893
894 int
895 mcd_send(sc, mbx, diskin)
896 struct mcd_softc *sc;
897 struct mcd_mbox *mbx;
898 int diskin;
899 {
900 int iobase = sc->iobase;
901 int retry, i, error;
902
903 if (sc->debug) {
904 printf("%s: mcd_send: %d %02x", sc->sc_dev.dv_xname,
905 mbx->cmd.length, (u_int)mbx->cmd.opcode);
906 for (i = 0; i < mbx->cmd.length; i++)
907 printf(" %02x", (u_int)mbx->cmd.data.raw.data[i]);
908 printf("\n");
909 }
910
911 for (retry = MCD_RETRIES; retry; retry--) {
912 outb(iobase + MCD_COMMAND, mbx->cmd.opcode);
913 for (i = 0; i < mbx->cmd.length; i++)
914 outb(iobase + MCD_COMMAND, mbx->cmd.data.raw.data[i]);
915 if ((error = mcd_getresult(sc, &mbx->res)) == 0)
916 break;
917 if (error == EINVAL)
918 return error;
919 }
920 if (!retry)
921 return error;
922 if (diskin && (sc->flags & MCDF_LOADED) == 0)
923 return EIO;
924
925 return 0;
926 }
927
928 static int
929 bcd2bin(b)
930 bcd_t b;
931 {
932
933 return (b >> 4) * 10 + (b & 15);
934 }
935
936 static bcd_t
937 bin2bcd(b)
938 int b;
939 {
940
941 return ((b / 10) << 4) | (b % 10);
942 }
943
944 static void
945 hsg2msf(hsg, msf)
946 int hsg;
947 bcd_t *msf;
948 {
949
950 hsg += 150;
951 F_msf(msf) = bin2bcd(hsg % 75);
952 hsg /= 75;
953 S_msf(msf) = bin2bcd(hsg % 60);
954 hsg /= 60;
955 M_msf(msf) = bin2bcd(hsg);
956 }
957
958 static daddr_t
959 msf2hsg(msf, relative)
960 bcd_t *msf;
961 int relative;
962 {
963 daddr_t blkno;
964
965 blkno = bcd2bin(M_msf(msf)) * 75 * 60 +
966 bcd2bin(S_msf(msf)) * 75 +
967 bcd2bin(F_msf(msf));
968 if (!relative)
969 blkno -= 150;
970 return blkno;
971 }
972
973 int
974 mcdintr(sc)
975 struct mcd_softc *sc;
976 {
977
978 mcd_fsm(sc);
979 return -1;
980 }
981
982 void
983 mcd_pseudointr(sc)
984 struct mcd_softc *sc;
985 {
986 int s;
987
988 s = splbio();
989 mcd_fsm(sc);
990 splx(s);
991 }
992
993 /*
994 * State machine to process read requests.
995 * Initialize with MCD_S_BEGIN: calculate sizes, and read status
996 * MCD_S_WAITSTAT: wait for status reply, set mode
997 * MCD_S_WAITMODE: waits for status reply from set mode, set read command
998 * MCD_S_WAITREAD: wait for read ready, read data.
999 */
1000 void
1001 mcd_fsm(arg)
1002 void *arg;
1003 {
1004 struct mcd_softc *sc = arg;
1005 struct mcd_mbx *mbx = &sc->mbx;
1006 int iobase = sc->iobase;
1007 struct buf *bp = mbx->bp;
1008
1009 int i;
1010 u_char x;
1011 bcd_t msf[3];
1012
1013 switch (mbx->state) {
1014 case MCD_S_IDLE:
1015 return;
1016
1017 case MCD_S_BEGIN:
1018 tryagain:
1019 /* Get status. */
1020 outb(iobase + MCD_COMMAND, MCD_CMDGETSTAT);
1021
1022 mbx->count = RDELAY_WAITSTAT;
1023 mbx->state = MCD_S_WAITSTAT;
1024
1025 case MCD_S_WAITSTAT:
1026 untimeout(mcd_pseudointr, sc);
1027 for (i = 20; i; i--) {
1028 x = inb(iobase + MCD_XFER);
1029 if ((x & MCD_XF_STATUSUNAVAIL) == 0)
1030 break;
1031 delay(50);
1032 }
1033 if (i == 0)
1034 goto hold;
1035 sc->status = inb(iobase + MCD_STATUS);
1036 mcd_setflags(sc);
1037 if ((sc->flags & MCDF_LOADED) == 0)
1038 goto changed;
1039 MCD_TRACE("doread: got WAITSTAT delay=%d\n",
1040 RDELAY_WAITSTAT - mbx->count, 0, 0, 0);
1041
1042 /* Reject, if audio active. */
1043 if ((sc->status & MCD_ST_AUDIOBSY) != 0) {
1044 printf("%s: audio is active\n",
1045 sc->sc_dev.dv_xname);
1046 goto harderr;
1047 }
1048
1049 if (mbx->mode == sc->lastmode)
1050 goto firstblock;
1051
1052 sc->lastmode = MCD_MD_UNKNOWN;
1053 outb(iobase + MCD_COMMAND, MCD_CMDSETMODE);
1054 outb(iobase + MCD_COMMAND, mbx->mode);
1055
1056 mbx->count = RDELAY_WAITMODE;
1057 mbx->state = MCD_S_WAITMODE;
1058
1059 case MCD_S_WAITMODE:
1060 untimeout(mcd_pseudointr, sc);
1061 for (i = 20; i; i--) {
1062 x = inb(iobase + MCD_XFER);
1063 if ((x & MCD_XF_STATUSUNAVAIL) == 0)
1064 break;
1065 delay(50);
1066 }
1067 if (i == 0)
1068 goto hold;
1069 sc->status = inb(iobase + MCD_STATUS);
1070 mcd_setflags(sc);
1071 if ((sc->flags & MCDF_LOADED) == 0)
1072 goto changed;
1073 MCD_TRACE("doread: got WAITMODE delay=%d\n",
1074 RDELAY_WAITMODE - mbx->count, 0, 0, 0);
1075
1076 sc->lastmode = mbx->mode;
1077
1078 firstblock:
1079 MCD_TRACE("doread: read blkno=%d for bp=0x%x\n", mbx->blkno,
1080 bp, 0, 0);
1081
1082 /* Build parameter block. */
1083 hsg2msf(mbx->blkno, msf);
1084
1085 /* Send the read command. */
1086 outb(iobase + MCD_COMMAND, MCD_CMDREAD2);
1087 outb(iobase + MCD_COMMAND, msf[0]);
1088 outb(iobase + MCD_COMMAND, msf[1]);
1089 outb(iobase + MCD_COMMAND, msf[2]);
1090 outb(iobase + MCD_COMMAND, 0);
1091 outb(iobase + MCD_COMMAND, 0);
1092 outb(iobase + MCD_COMMAND, mbx->nblk);
1093
1094 mbx->count = RDELAY_WAITREAD;
1095 mbx->state = MCD_S_WAITREAD;
1096
1097 case MCD_S_WAITREAD:
1098 untimeout(mcd_pseudointr, sc);
1099 nextblock:
1100 loop:
1101 for (i = 20; i; i--) {
1102 x = inb(iobase + MCD_XFER);
1103 if ((x & MCD_XF_DATAUNAVAIL) == 0)
1104 goto gotblock;
1105 if ((x & MCD_XF_STATUSUNAVAIL) == 0)
1106 break;
1107 delay(50);
1108 }
1109 if (i == 0)
1110 goto hold;
1111 sc->status = inb(iobase + MCD_STATUS);
1112 mcd_setflags(sc);
1113 if ((sc->flags & MCDF_LOADED) == 0)
1114 goto changed;
1115 #if 0
1116 printf("%s: got status byte %02x during read\n",
1117 sc->sc_dev.dv_xname, (u_int)sc->status);
1118 #endif
1119 goto loop;
1120
1121 gotblock:
1122 MCD_TRACE("doread: got data delay=%d\n",
1123 RDELAY_WAITREAD - mbx->count, 0, 0, 0);
1124
1125 /* Data is ready. */
1126 outb(iobase + MCD_CTL2, 0x04); /* XXX */
1127 insb(iobase + MCD_RDATA, bp->b_data + mbx->skip, mbx->sz);
1128 outb(iobase + MCD_CTL2, 0x0c); /* XXX */
1129 mbx->blkno += 1;
1130 mbx->skip += mbx->sz;
1131 if (--mbx->nblk > 0)
1132 goto nextblock;
1133
1134 mbx->state = MCD_S_IDLE;
1135
1136 /* Return buffer. */
1137 bp->b_resid = 0;
1138 biodone(bp);
1139
1140 mcdstart(sc);
1141 return;
1142
1143 hold:
1144 if (mbx->count-- < 0) {
1145 printf("%s: timeout in state %d",
1146 sc->sc_dev.dv_xname, mbx->state);
1147 goto readerr;
1148 }
1149
1150 #if 0
1151 printf("%s: sleep in state %d\n", sc->sc_dev.dv_xname,
1152 mbx->state);
1153 #endif
1154 timeout(mcd_pseudointr, sc, hz / 100);
1155 return;
1156 }
1157
1158 readerr:
1159 if (mbx->retry-- > 0) {
1160 printf("; retrying\n");
1161 goto tryagain;
1162 } else
1163 printf("; giving up\n");
1164
1165 changed:
1166 harderr:
1167 /* Invalidate the buffer. */
1168 bp->b_flags |= B_ERROR;
1169 bp->b_resid = bp->b_bcount - mbx->skip;
1170 biodone(bp);
1171
1172 mcdstart(sc);
1173 return;
1174
1175 #ifdef notyet
1176 printf("%s: unit timeout; resetting\n", sc->sc_dev.dv_xname);
1177 outb(mbx->iobase + MCD_RESET, MCD_CMDRESET);
1178 delay(300000);
1179 (void) mcd_getstat(sc, 1);
1180 (void) mcd_getstat(sc, 1);
1181 /*sc->status &= ~MCD_ST_DSKCHNG; */
1182 sc->debug = 1; /* preventive set debug mode */
1183 #endif
1184 }
1185
1186 void
1187 mcd_soft_reset(sc)
1188 struct mcd_softc *sc;
1189 {
1190
1191 sc->debug = 1;
1192 sc->flags = 0;
1193 sc->lastmode = MCD_MD_UNKNOWN;
1194 sc->lastupc = MCD_UPC_UNKNOWN;
1195 sc->audio_status = CD_AS_AUDIO_INVALID;
1196 outb(sc->iobase + MCD_CTL2, 0x0c); /* XXX */
1197 }
1198
1199 int
1200 mcd_hard_reset(sc)
1201 struct mcd_softc *sc;
1202 {
1203 struct mcd_mbox mbx;
1204
1205 mcd_soft_reset(sc);
1206
1207 mbx.cmd.opcode = MCD_CMDRESET;
1208 mbx.cmd.length = 0;
1209 mbx.res.length = 0;
1210 return mcd_send(sc, &mbx, 0);
1211 }
1212
1213 int
1214 mcd_setmode(sc, mode)
1215 struct mcd_softc *sc;
1216 int mode;
1217 {
1218 struct mcd_mbox mbx;
1219 int error;
1220
1221 if (sc->lastmode == mode)
1222 return 0;
1223 if (sc->debug)
1224 printf("%s: setting mode to %d\n", sc->sc_dev.dv_xname, mode);
1225 sc->lastmode = MCD_MD_UNKNOWN;
1226
1227 mbx.cmd.opcode = MCD_CMDSETMODE;
1228 mbx.cmd.length = sizeof(mbx.cmd.data.datamode);
1229 mbx.cmd.data.datamode.mode = mode;
1230 mbx.res.length = 0;
1231 if ((error = mcd_send(sc, &mbx, 1)) != 0)
1232 return error;
1233
1234 sc->lastmode = mode;
1235 return 0;
1236 }
1237
1238 int
1239 mcd_setupc(sc, upc)
1240 struct mcd_softc *sc;
1241 int upc;
1242 {
1243 struct mcd_mbox mbx;
1244 int error;
1245
1246 if (sc->lastupc == upc)
1247 return 0;
1248 if (sc->debug)
1249 printf("%s: setting upc to %d\n", sc->sc_dev.dv_xname, upc);
1250 sc->lastupc = MCD_UPC_UNKNOWN;
1251
1252 mbx.cmd.opcode = MCD_CMDCONFIGDRIVE;
1253 mbx.cmd.length = sizeof(mbx.cmd.data.config) - 1;
1254 mbx.cmd.data.config.subcommand = MCD_CF_READUPC;
1255 mbx.cmd.data.config.data1 = upc;
1256 mbx.res.length = 0;
1257 if ((error = mcd_send(sc, &mbx, 1)) != 0)
1258 return error;
1259
1260 sc->lastupc = upc;
1261 return 0;
1262 }
1263
1264 int
1265 mcd_toc_header(sc, th)
1266 struct mcd_softc *sc;
1267 struct ioc_toc_header *th;
1268 {
1269
1270 if (sc->debug)
1271 printf("%s: mcd_toc_header: reading toc header\n",
1272 sc->sc_dev.dv_xname);
1273
1274 th->len = msf2hsg(sc->volinfo.vol_msf, 0);
1275 th->starting_track = bcd2bin(sc->volinfo.trk_low);
1276 th->ending_track = bcd2bin(sc->volinfo.trk_high);
1277
1278 return 0;
1279 }
1280
1281 int
1282 mcd_read_toc(sc)
1283 struct mcd_softc *sc;
1284 {
1285 struct ioc_toc_header th;
1286 union mcd_qchninfo q;
1287 int error, trk, idx, retry;
1288
1289 /* Only read TOC if needed. */
1290 if ((sc->flags & MCDF_HAVETOC) != 0)
1291 return 0;
1292
1293 if ((error = mcd_toc_header(sc, &th)) != 0)
1294 return error;
1295
1296 if ((error = mcd_stop(sc)) != 0)
1297 return error;
1298
1299 if (sc->debug)
1300 printf("%s: read_toc: reading qchannel info\n",
1301 sc->sc_dev.dv_xname);
1302
1303 for (trk = th.starting_track; trk <= th.ending_track; trk++)
1304 sc->toc[trk].toc.idx_no = 0x00;
1305 trk = th.ending_track - th.starting_track + 1;
1306 for (retry = 300; retry && trk > 0; retry--) {
1307 if (mcd_getqchan(sc, &q, CD_TRACK_INFO) != 0)
1308 break;
1309 if (q.toc.trk_no != 0x00 || q.toc.idx_no == 0x00)
1310 continue;
1311 idx = bcd2bin(q.toc.idx_no);
1312 if (idx < MCD_MAXTOCS &&
1313 sc->toc[idx].toc.idx_no == 0x00) {
1314 sc->toc[idx] = q;
1315 trk--;
1316 }
1317 }
1318
1319 if (trk != 0)
1320 return EINVAL;
1321
1322 /* Add a fake last+1 for mcd_playtracks(). */
1323 idx = th.ending_track + 1;
1324 sc->toc[idx].toc.control = sc->toc[idx-1].toc.control;
1325 sc->toc[idx].toc.addr_type = sc->toc[idx-1].toc.addr_type;
1326 sc->toc[idx].toc.trk_no = 0x00;
1327 sc->toc[idx].toc.idx_no = 0xaa;
1328 sc->toc[idx].toc.absolute_pos[0] = sc->volinfo.vol_msf[0];
1329 sc->toc[idx].toc.absolute_pos[1] = sc->volinfo.vol_msf[1];
1330 sc->toc[idx].toc.absolute_pos[2] = sc->volinfo.vol_msf[2];
1331
1332 sc->flags |= MCDF_HAVETOC;
1333 return 0;
1334 }
1335
1336 int
1337 mcd_toc_entries(sc, te)
1338 struct mcd_softc *sc;
1339 struct ioc_read_toc_entry *te;
1340 {
1341 int len = te->data_len;
1342 struct ret_toc {
1343 struct ioc_toc_header header;
1344 struct cd_toc_entry entries[MCD_MAXTOCS];
1345 } data;
1346 u_char trk;
1347 daddr_t lba;
1348 int error, n;
1349
1350 if (len > sizeof(data.entries) ||
1351 len < sizeof(struct cd_toc_entry))
1352 return EINVAL;
1353 if (te->address_format != CD_MSF_FORMAT &&
1354 te->address_format != CD_LBA_FORMAT)
1355 return EINVAL;
1356
1357 /* Copy the TOC header. */
1358 if ((error = mcd_toc_header(sc, &data.header)) != 0)
1359 return error;
1360
1361 /* Verify starting track. */
1362 trk = te->starting_track;
1363 if (trk == 0x00)
1364 trk = data.header.starting_track;
1365 else if (trk == 0xaa)
1366 trk = data.header.ending_track + 1;
1367 else if (trk < data.header.starting_track ||
1368 trk > data.header.ending_track + 1)
1369 return EINVAL;
1370
1371 /* Make sure we have a valid TOC. */
1372 if ((error = mcd_read_toc(sc)) != 0)
1373 return error;
1374
1375 /* Copy the TOC data. */
1376 for (n = 0; trk <= data.header.ending_track + 1; trk++) {
1377 if (sc->toc[trk].toc.idx_no == 0x00)
1378 continue;
1379 data.entries[n].control = sc->toc[trk].toc.control;
1380 data.entries[n].addr_type = sc->toc[trk].toc.addr_type;
1381 data.entries[n].track = bcd2bin(sc->toc[trk].toc.idx_no);
1382 switch (te->address_format) {
1383 case CD_MSF_FORMAT:
1384 data.entries[n].addr[0] = 0;
1385 data.entries[n].addr[1] = bcd2bin(sc->toc[trk].toc.absolute_pos[0]);
1386 data.entries[n].addr[2] = bcd2bin(sc->toc[trk].toc.absolute_pos[1]);
1387 data.entries[n].addr[3] = bcd2bin(sc->toc[trk].toc.absolute_pos[2]);
1388 break;
1389 case CD_LBA_FORMAT:
1390 lba = msf2hsg(sc->toc[trk].toc.absolute_pos, 0);
1391 data.entries[n].addr[0] = lba >> 24;
1392 data.entries[n].addr[1] = lba >> 16;
1393 data.entries[n].addr[2] = lba >> 8;
1394 data.entries[n].addr[3] = lba;
1395 break;
1396 }
1397 n++;
1398 }
1399
1400 len = min(len, n * sizeof(struct cd_toc_entry));
1401
1402 /* Copy the data back. */
1403 return copyout(&data.entries[0], te->data, len);
1404 }
1405
1406 int
1407 mcd_stop(sc)
1408 struct mcd_softc *sc;
1409 {
1410 struct mcd_mbox mbx;
1411 int error;
1412
1413 if (sc->debug)
1414 printf("%s: mcd_stop: stopping play\n", sc->sc_dev.dv_xname);
1415
1416 mbx.cmd.opcode = MCD_CMDSTOPAUDIO;
1417 mbx.cmd.length = 0;
1418 mbx.res.length = 0;
1419 if ((error = mcd_send(sc, &mbx, 1)) != 0)
1420 return error;
1421
1422 sc->audio_status = CD_AS_PLAY_COMPLETED;
1423 return 0;
1424 }
1425
1426 int
1427 mcd_getqchan(sc, q, qchn)
1428 struct mcd_softc *sc;
1429 union mcd_qchninfo *q;
1430 int qchn;
1431 {
1432 struct mcd_mbox mbx;
1433 int error;
1434
1435 if (qchn == CD_TRACK_INFO) {
1436 if ((error = mcd_setmode(sc, MCD_MD_TOC)) != 0)
1437 return error;
1438 } else {
1439 if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0)
1440 return error;
1441 }
1442 if (qchn == CD_MEDIA_CATALOG) {
1443 if ((error = mcd_setupc(sc, MCD_UPC_ENABLE)) != 0)
1444 return error;
1445 } else {
1446 if ((error = mcd_setupc(sc, MCD_UPC_DISABLE)) != 0)
1447 return error;
1448 }
1449
1450 mbx.cmd.opcode = MCD_CMDGETQCHN;
1451 mbx.cmd.length = 0;
1452 mbx.res.length = sizeof(mbx.res.data.qchninfo);
1453 if ((error = mcd_send(sc, &mbx, 1)) != 0)
1454 return error;
1455
1456 *q = mbx.res.data.qchninfo;
1457 return 0;
1458 }
1459
1460 int
1461 mcd_read_subchannel(sc, ch)
1462 struct mcd_softc *sc;
1463 struct ioc_read_subchannel *ch;
1464 {
1465 int len = ch->data_len;
1466 union mcd_qchninfo q;
1467 struct cd_sub_channel_info data;
1468 daddr_t lba;
1469 int error;
1470
1471 if (sc->debug)
1472 printf("%s: subchan: af=%d df=%d\n", sc->sc_dev.dv_xname,
1473 ch->address_format, ch->data_format);
1474
1475 if (len > sizeof(data) ||
1476 len < sizeof(struct cd_sub_channel_header))
1477 return EINVAL;
1478 if (ch->address_format != CD_MSF_FORMAT &&
1479 ch->address_format != CD_LBA_FORMAT)
1480 return EINVAL;
1481 if (ch->data_format != CD_CURRENT_POSITION &&
1482 ch->data_format != CD_MEDIA_CATALOG)
1483 return EINVAL;
1484
1485 if ((error = mcd_getqchan(sc, &q, ch->data_format)) != 0)
1486 return error;
1487
1488 data.header.audio_status = sc->audio_status;
1489 data.what.media_catalog.data_format = ch->data_format;
1490
1491 switch (ch->data_format) {
1492 case CD_MEDIA_CATALOG:
1493 data.what.media_catalog.mc_valid = 1;
1494 #if 0
1495 data.what.media_catalog.mc_number =
1496 #endif
1497 break;
1498
1499 case CD_CURRENT_POSITION:
1500 data.what.position.track_number = bcd2bin(q.current.trk_no);
1501 data.what.position.index_number = bcd2bin(q.current.idx_no);
1502 switch (ch->address_format) {
1503 case CD_MSF_FORMAT:
1504 data.what.position.reladdr[0] = 0;
1505 data.what.position.reladdr[1] = bcd2bin(q.current.relative_pos[0]);
1506 data.what.position.reladdr[2] = bcd2bin(q.current.relative_pos[1]);
1507 data.what.position.reladdr[3] = bcd2bin(q.current.relative_pos[2]);
1508 data.what.position.absaddr[0] = 0;
1509 data.what.position.absaddr[1] = bcd2bin(q.current.absolute_pos[0]);
1510 data.what.position.absaddr[2] = bcd2bin(q.current.absolute_pos[1]);
1511 data.what.position.absaddr[3] = bcd2bin(q.current.absolute_pos[2]);
1512 break;
1513 case CD_LBA_FORMAT:
1514 lba = msf2hsg(q.current.relative_pos, 1);
1515 /*
1516 * Pre-gap has index number of 0, and decreasing MSF
1517 * address. Must be converted to negative LBA, per
1518 * SCSI spec.
1519 */
1520 if (data.what.position.index_number == 0x00)
1521 lba = -lba;
1522 data.what.position.reladdr[0] = lba >> 24;
1523 data.what.position.reladdr[1] = lba >> 16;
1524 data.what.position.reladdr[2] = lba >> 8;
1525 data.what.position.reladdr[3] = lba;
1526 lba = msf2hsg(q.current.absolute_pos, 0);
1527 data.what.position.absaddr[0] = lba >> 24;
1528 data.what.position.absaddr[1] = lba >> 16;
1529 data.what.position.absaddr[2] = lba >> 8;
1530 data.what.position.absaddr[3] = lba;
1531 break;
1532 }
1533 break;
1534 }
1535
1536 return copyout(&data, ch->data, len);
1537 }
1538
1539 int
1540 mcd_playtracks(sc, p)
1541 struct mcd_softc *sc;
1542 struct ioc_play_track *p;
1543 {
1544 struct mcd_mbox mbx;
1545 int a = p->start_track;
1546 int z = p->end_track;
1547 int error;
1548
1549 if ((error = mcd_read_toc(sc)) != 0)
1550 return error;
1551
1552 if (sc->debug)
1553 printf("%s: playtracks: from %d:%d to %d:%d\n",
1554 sc->sc_dev.dv_xname,
1555 a, p->start_index, z, p->end_index);
1556
1557 if (a < bcd2bin(sc->volinfo.trk_low) ||
1558 a > bcd2bin(sc->volinfo.trk_high) ||
1559 a > z ||
1560 z < bcd2bin(sc->volinfo.trk_low) ||
1561 z > bcd2bin(sc->volinfo.trk_high))
1562 return EINVAL;
1563
1564 if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0)
1565 return error;
1566
1567 mbx.cmd.opcode = MCD_CMDREAD2;
1568 mbx.cmd.length = sizeof(mbx.cmd.data.play);
1569 mbx.cmd.data.play.start_msf[0] = sc->toc[a].toc.absolute_pos[0];
1570 mbx.cmd.data.play.start_msf[1] = sc->toc[a].toc.absolute_pos[1];
1571 mbx.cmd.data.play.start_msf[2] = sc->toc[a].toc.absolute_pos[2];
1572 mbx.cmd.data.play.end_msf[0] = sc->toc[z+1].toc.absolute_pos[0];
1573 mbx.cmd.data.play.end_msf[1] = sc->toc[z+1].toc.absolute_pos[1];
1574 mbx.cmd.data.play.end_msf[2] = sc->toc[z+1].toc.absolute_pos[2];
1575 sc->lastpb = mbx.cmd;
1576 mbx.res.length = 0;
1577 return mcd_send(sc, &mbx, 1);
1578 }
1579
1580 int
1581 mcd_playmsf(sc, p)
1582 struct mcd_softc *sc;
1583 struct ioc_play_msf *p;
1584 {
1585 struct mcd_mbox mbx;
1586 int error;
1587
1588 if ((error = mcd_read_toc(sc)) != 0)
1589 return error;
1590
1591 if (sc->debug)
1592 printf("%s: playmsf: from %d:%d.%d to %d:%d.%d\n",
1593 sc->sc_dev.dv_xname,
1594 p->start_m, p->start_s, p->start_f,
1595 p->end_m, p->end_s, p->end_f);
1596
1597 if ((p->start_m * 60 * 75 + p->start_s * 75 + p->start_f) >=
1598 (p->end_m * 60 * 75 + p->end_s * 75 + p->end_f))
1599 return EINVAL;
1600
1601 if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0)
1602 return error;
1603
1604 mbx.cmd.opcode = MCD_CMDREAD2;
1605 mbx.cmd.length = sizeof(mbx.cmd.data.play);
1606 mbx.cmd.data.play.start_msf[0] = bin2bcd(p->start_m);
1607 mbx.cmd.data.play.start_msf[1] = bin2bcd(p->start_s);
1608 mbx.cmd.data.play.start_msf[2] = bin2bcd(p->start_f);
1609 mbx.cmd.data.play.end_msf[0] = bin2bcd(p->end_m);
1610 mbx.cmd.data.play.end_msf[1] = bin2bcd(p->end_s);
1611 mbx.cmd.data.play.end_msf[2] = bin2bcd(p->end_f);
1612 sc->lastpb = mbx.cmd;
1613 mbx.res.length = 0;
1614 return mcd_send(sc, &mbx, 1);
1615 }
1616
1617 int
1618 mcd_playblocks(sc, p)
1619 struct mcd_softc *sc;
1620 struct ioc_play_blocks *p;
1621 {
1622 struct mcd_mbox mbx;
1623 int error;
1624
1625 if ((error = mcd_read_toc(sc)) != 0)
1626 return error;
1627
1628 if (sc->debug)
1629 printf("%s: playblocks: blkno %d length %d\n",
1630 sc->sc_dev.dv_xname, p->blk, p->len);
1631
1632 if (p->blk > sc->disksize || p->len > sc->disksize ||
1633 (p->blk + p->len) > sc->disksize)
1634 return 0;
1635
1636 if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0)
1637 return error;
1638
1639 mbx.cmd.opcode = MCD_CMDREAD2;
1640 mbx.cmd.length = sizeof(mbx.cmd.data.play);
1641 hsg2msf(p->blk, mbx.cmd.data.play.start_msf);
1642 hsg2msf(p->blk + p->len, mbx.cmd.data.play.end_msf);
1643 sc->lastpb = mbx.cmd;
1644 mbx.res.length = 0;
1645 return mcd_send(sc, &mbx, 1);
1646 }
1647
1648 int
1649 mcd_pause(sc)
1650 struct mcd_softc *sc;
1651 {
1652 union mcd_qchninfo q;
1653 int error;
1654
1655 /* Verify current status. */
1656 if (sc->audio_status != CD_AS_PLAY_IN_PROGRESS) {
1657 printf("%s: pause: attempted when not playing\n",
1658 sc->sc_dev.dv_xname);
1659 return EINVAL;
1660 }
1661
1662 /* Get the current position. */
1663 if ((error = mcd_getqchan(sc, &q, CD_CURRENT_POSITION)) != 0)
1664 return error;
1665
1666 /* Copy it into lastpb. */
1667 sc->lastpb.data.seek.start_msf[0] = q.current.absolute_pos[0];
1668 sc->lastpb.data.seek.start_msf[1] = q.current.absolute_pos[1];
1669 sc->lastpb.data.seek.start_msf[2] = q.current.absolute_pos[2];
1670
1671 /* Stop playing. */
1672 if ((error = mcd_stop(sc)) != 0)
1673 return error;
1674
1675 /* Set the proper status and exit. */
1676 sc->audio_status = CD_AS_PLAY_PAUSED;
1677 return 0;
1678 }
1679
1680 int
1681 mcd_resume(sc)
1682 struct mcd_softc *sc;
1683 {
1684 struct mcd_mbox mbx;
1685 int error;
1686
1687 if (sc->audio_status != CD_AS_PLAY_PAUSED)
1688 return EINVAL;
1689
1690 if ((error = mcd_setmode(sc, MCD_MD_COOKED)) != 0)
1691 return error;
1692
1693 mbx.cmd = sc->lastpb;
1694 mbx.res.length = 0;
1695 return mcd_send(sc, &mbx, 1);
1696 }
1697
1698 int
1699 mcd_eject(sc)
1700 struct mcd_softc *sc;
1701 {
1702 struct mcd_mbox mbx;
1703
1704 mbx.cmd.opcode = MCD_CMDEJECTDISK;
1705 mbx.cmd.length = 0;
1706 mbx.res.length = 0;
1707 return mcd_send(sc, &mbx, 0);
1708 }
1709
1710 int
1711 mcd_setlock(sc, mode)
1712 struct mcd_softc *sc;
1713 int mode;
1714 {
1715 struct mcd_mbox mbx;
1716
1717 mbx.cmd.opcode = MCD_CMDSETLOCK;
1718 mbx.cmd.length = sizeof(mbx.cmd.data.lockmode);
1719 mbx.cmd.data.lockmode.mode = mode;
1720 mbx.res.length = 0;
1721 return mcd_send(sc, &mbx, 1);
1722 }
1723