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