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