mcd.c revision 2762
1/* 2 * Copyright 1993 by Holger Veit (data part) 3 * Copyright 1993 by Brian Moore (audio part) 4 * Changes Copyright 1993 by Gary Clark II 5 * 6 * Rewrote probe routine to work on newer Mitsumi drives. 7 * Additional changes (C) 1994 by Jordan K. Hubbard 8 * 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This software was developed by Holger Veit and Brian Moore 22 * for use with "386BSD" and similar operating systems. 23 * "Similar operating systems" includes mainly non-profit oriented 24 * systems for research and education, including but not restricted to 25 * "NetBSD", "FreeBSD", "Mach" (by CMU). 26 * 4. Neither the name of the developer(s) nor the name "386BSD" 27 * may be used to endorse or promote products derived from this 28 * software without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER(S) ``AS IS'' AND ANY 31 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 33 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPER(S) BE 34 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 35 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 36 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 37 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 38 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 39 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 40 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 41 * 42 * $Id: mcd.c,v 1.24 1994/09/06 21:56:09 se Exp $ 43 */ 44static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore"; 45 46#include "mcd.h" 47#if NMCD > 0 48#include <sys/types.h> 49#include <sys/param.h> 50#include <sys/systm.h> 51#include <sys/conf.h> 52#include <sys/file.h> 53#include <sys/buf.h> 54#include <sys/stat.h> 55#include <sys/uio.h> 56#include <sys/ioctl.h> 57#include <sys/cdio.h> 58#include <sys/errno.h> 59#include <sys/dkbad.h> 60#include <sys/disklabel.h> 61#include <i386/isa/isa.h> 62#include <i386/isa/isa_device.h> 63#include <i386/isa/mcdreg.h> 64 65#define MCD_TRACE(fmt,a,b,c,d) {if (mcd_data[unit].debug) {printf("mcd%d: status=0x%02x: ",unit,mcd_data[unit].status); printf(fmt,a,b,c,d);}} 66 67#define mcd_part(dev) ((minor(dev)) & 7) 68#define mcd_unit(dev) (((minor(dev)) & 0x38) >> 3) 69#define mcd_phys(dev) (((minor(dev)) & 0x40) >> 6) 70#define RAW_PART 2 71 72/* flags */ 73#define MCDOPEN 0x0001 /* device opened */ 74#define MCDVALID 0x0002 /* parameters loaded */ 75#define MCDINIT 0x0004 /* device is init'd */ 76#define MCDNEWMODEL 0x0008 /* device is new model */ 77#define MCDLABEL 0x0010 /* label is read */ 78#define MCDPROBING 0x0020 /* probing */ 79#define MCDREADRAW 0x0040 /* read raw mode (2352 bytes) */ 80#define MCDVOLINFO 0x0080 /* already read volinfo */ 81#define MCDTOC 0x0100 /* already read toc */ 82#define MCDMBXBSY 0x0200 /* local mbx is busy */ 83 84/* status */ 85#define MCDAUDIOBSY MCD_ST_AUDIOBSY /* playing audio */ 86#define MCDDSKCHNG MCD_ST_DSKCHNG /* sensed change of disk */ 87#define MCDDSKIN MCD_ST_DSKIN /* sensed disk in drive */ 88#define MCDDOOROPEN MCD_ST_DOOROPEN /* sensed door open */ 89 90/* These are apparently the different states a mitsumi can get up to */ 91#define MCDCDABSENT 0x0030 92#define MCDCDPRESENT 0x0020 93#define MCDSCLOSED 0x0080 94#define MCDSOPEN 0x00a0 95 96#define MCD_MD_UNKNOWN (-1) 97 98/* toc */ 99#define MCD_MAXTOCS 104 /* from the Linux driver */ 100#define MCD_LASTPLUS1 170 /* special toc entry */ 101 102struct mcd_mbx { 103 short unit; 104 short port; 105 short retry; 106 short nblk; 107 int sz; 108 u_long skip; 109 struct buf *bp; 110 int p_offset; 111 short count; 112 short mode; 113}; 114 115struct mcd_data { 116 short config; 117 short flags; 118 short status; 119 int blksize; 120 u_long disksize; 121 int iobase; 122 struct disklabel dlabel; 123 int partflags[MAXPARTITIONS]; 124 int openflags; 125 struct mcd_volinfo volinfo; 126 struct mcd_qchninfo toc[MCD_MAXTOCS]; 127 short audio_status; 128 short curr_mode; 129 struct mcd_read2 lastpb; 130 short debug; 131 struct buf head; /* head of buf queue */ 132 struct mcd_mbx mbx; 133} mcd_data[NMCD]; 134 135/* reader state machine */ 136#define MCD_S_BEGIN 0 137#define MCD_S_BEGIN1 1 138#define MCD_S_WAITSTAT 2 139#define MCD_S_WAITMODE 3 140#define MCD_S_WAITREAD 4 141 142/* prototypes */ 143int mcdopen(dev_t dev); 144int mcdclose(dev_t dev); 145void mcdstrategy(struct buf *bp); 146int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags); 147int mcdsize(dev_t dev); 148static void mcd_done(struct mcd_mbx *mbx); 149static void mcd_start(int unit); 150static int mcd_getdisklabel(int unit); 151static void mcd_configure(struct mcd_data *cd); 152static int mcd_get(int unit, char *buf, int nmax); 153static int mcd_setflags(int unit,struct mcd_data *cd); 154static int mcd_getstat(int unit,int sflg); 155static int mcd_send(int unit, int cmd,int nretrys); 156static int bcd2bin(bcd_t b); 157static bcd_t bin2bcd(int b); 158static void hsg2msf(int hsg, bcd_t *msf); 159static int msf2hsg(bcd_t *msf); 160static int mcd_volinfo(int unit); 161static int mcd_waitrdy(int port,int dly); 162static void mcd_doread(int state, struct mcd_mbx *mbxin); 163static void mcd_soft_reset(int unit); 164static int mcd_hard_reset(int unit); 165static int mcd_setmode(int unit, int mode); 166static int mcd_getqchan(int unit, struct mcd_qchninfo *q); 167static int mcd_subchan(int unit, struct ioc_read_subchannel *sc); 168static int mcd_toc_header(int unit, struct ioc_toc_header *th); 169static int mcd_read_toc(int unit); 170static int mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te); 171static int mcd_stop(int unit); 172static int mcd_eject(int unit); 173static int mcd_playtracks(int unit, struct ioc_play_track *pt); 174static int mcd_play(int unit, struct mcd_read2 *pb); 175static int mcd_playmsf(int unit, struct ioc_play_msf *pt); 176static int mcd_pause(int unit); 177static int mcd_resume(int unit); 178 179extern int hz; 180extern int mcd_probe(struct isa_device *dev); 181extern int mcd_attach(struct isa_device *dev); 182struct isa_driver mcddriver = { mcd_probe, mcd_attach, "mcd" }; 183 184#define mcd_put(port,byte) outb(port,byte) 185 186#define MCD_RETRYS 5 187#define MCD_RDRETRYS 8 188 189#define MCDBLK 2048 /* for cooked mode */ 190#define MCDRBLK 2352 /* for raw mode */ 191 192/* several delays */ 193#define RDELAY_WAITSTAT 300 194#define RDELAY_WAITMODE 300 195#define RDELAY_WAITREAD 800 196 197#define MIN_DELAY 15 198#define DELAY_GETREPLY 1300000 199 200int mcd_attach(struct isa_device *dev) 201{ 202 struct mcd_data *cd = mcd_data + dev->id_unit; 203 int i; 204 205 cd->iobase = dev->id_iobase; 206 cd->flags |= MCDINIT; 207 mcd_soft_reset(dev->id_unit); 208 209#ifdef NOTYET 210 /* wire controller for interrupts and dma */ 211 mcd_configure(cd); 212#endif 213 214 return 1; 215} 216 217int mcdopen(dev_t dev) 218{ 219 int unit,part,phys; 220 struct mcd_data *cd; 221 222 unit = mcd_unit(dev); 223 if (unit >= NMCD) 224 return ENXIO; 225 226 cd = mcd_data + unit; 227 part = mcd_part(dev); 228 phys = mcd_phys(dev); 229 230 /* not initialized*/ 231 if (!(cd->flags & MCDINIT)) 232 return ENXIO; 233 234 /* invalidated in the meantime? mark all open part's invalid */ 235 if (!(cd->flags & MCDVALID) && cd->openflags) 236 return ENXIO; 237 238 if (mcd_getstat(unit,1) == -1) /* detect disk change too */ 239 return ENXIO; 240 241 if (cd->status & MCDDOOROPEN) { 242 printf("mcd%d: door is open\n"); 243 return ENXIO; 244 } 245 if (!(cd->status & MCDDSKIN)) { 246 printf("mcd%d: no CD inside\n"); 247 return ENXIO; 248 } 249 250 if (mcdsize(dev) < 0) { 251 printf("mcd%d: failed to get disk size\n",unit); 252 return ENXIO; 253 } else 254 cd->flags |= MCDVALID; 255 256 /* XXX get a default disklabel */ 257 mcd_getdisklabel(unit); 258 259MCD_TRACE("open: partition=%d, disksize = %d, blksize=%d\n", 260 part,cd->disksize,cd->blksize,0); 261 262 if (part == RAW_PART || 263 (part < cd->dlabel.d_npartitions && 264 cd->dlabel.d_partitions[part].p_fstype != FS_UNUSED)) { 265 cd->partflags[part] |= MCDOPEN; 266 cd->openflags |= (1<<part); 267 if (part == RAW_PART && phys != 0) 268 cd->partflags[part] |= MCDREADRAW; 269 return 0; 270 } 271 272 return ENXIO; 273} 274 275int mcdclose(dev_t dev) 276{ 277 int unit,part,phys; 278 struct mcd_data *cd; 279 280 unit = mcd_unit(dev); 281 if (unit >= NMCD) 282 return ENXIO; 283 284 cd = mcd_data + unit; 285 part = mcd_part(dev); 286 phys = mcd_phys(dev); 287 288 if (!(cd->flags & MCDINIT)) 289 return ENXIO; 290 291 if (mcd_getstat(unit,1) == -2) 292 return 0; 293 294 /* close channel */ 295 cd->partflags[part] &= ~(MCDOPEN|MCDREADRAW); 296 cd->openflags &= ~(1<<part); 297 MCD_TRACE("close: partition=%d\n",part,0,0,0); 298 299 return 0; 300} 301 302void 303mcdstrategy(struct buf *bp) 304{ 305 struct mcd_data *cd; 306 struct buf *qp; 307 int s; 308 309 int unit = mcd_unit(bp->b_dev); 310 311 cd = mcd_data + unit; 312 313 /* test validity */ 314/*MCD_TRACE("strategy: buf=0x%lx, unit=%ld, block#=%ld bcount=%ld\n", 315 bp,unit,bp->b_blkno,bp->b_bcount);*/ 316 if (unit >= NMCD || bp->b_blkno < 0) { 317 printf("mcdstrategy: unit = %d, blkno = %d, bcount = %d\n", 318 unit, bp->b_blkno, bp->b_bcount); 319 pg("mcd: mcdstratregy failure"); 320 bp->b_error = EINVAL; 321 bp->b_flags |= B_ERROR; 322 goto bad; 323 } 324 325 /* if device invalidated (e.g. media change, door open), error */ 326 if (!(cd->flags & MCDVALID)) { 327MCD_TRACE("strategy: drive not valid\n",0,0,0,0); 328 bp->b_error = EIO; 329 goto bad; 330 } 331 332 /* read only */ 333 if (!(bp->b_flags & B_READ)) { 334 bp->b_error = EROFS; 335 goto bad; 336 } 337 338 /* no data to read */ 339 if (bp->b_bcount == 0) 340 goto done; 341 342 /* for non raw access, check partition limits */ 343 if (mcd_part(bp->b_dev) != RAW_PART) { 344 if (!(cd->flags & MCDLABEL)) { 345 bp->b_error = EIO; 346 goto bad; 347 } 348 /* adjust transfer if necessary */ 349 if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) { 350 goto done; 351 } 352 } else { 353 bp->b_pblkno = bp->b_blkno; 354 bp->b_resid = 0; 355 } 356 357 /* queue it */ 358 qp = &cd->head; 359 s = splbio(); 360 disksort(qp,bp); 361 splx(s); 362 363 /* now check whether we can perform processing */ 364 mcd_start(unit); 365 return; 366 367bad: 368 bp->b_flags |= B_ERROR; 369done: 370 bp->b_resid = bp->b_bcount; 371 biodone(bp); 372 return; 373} 374 375static void mcd_start(int unit) 376{ 377 struct mcd_data *cd = mcd_data + unit; 378 struct buf *bp, *qp = &cd->head; 379 struct partition *p; 380 int part; 381 register s = splbio(); 382 383 if (cd->flags & MCDMBXBSY) { 384 splx(s); 385 return; 386 } 387 388 if ((bp = qp->b_actf) != 0) { 389 /* block found to process, dequeue */ 390 /*MCD_TRACE("mcd_start: found block bp=0x%x\n",bp,0,0,0);*/ 391 qp->b_actf = bp->b_actf; /* changed from: bp->av_forw <se> */ 392 splx(s); 393 } else { 394 /* nothing to do */ 395 splx(s); 396 return; 397 } 398 399 /* changed media? */ 400 if (!(cd->flags & MCDVALID)) { 401 MCD_TRACE("mcd_start: drive not valid\n",0,0,0,0); 402 return; 403 } 404 405 p = cd->dlabel.d_partitions + mcd_part(bp->b_dev); 406 407 cd->flags |= MCDMBXBSY; 408 if (cd->partflags[mcd_part(bp->b_dev)] & MCDREADRAW) 409 cd->flags |= MCDREADRAW; 410 cd->mbx.unit = unit; 411 cd->mbx.port = cd->iobase; 412 cd->mbx.retry = MCD_RETRYS; 413 cd->mbx.bp = bp; 414 cd->mbx.p_offset = p->p_offset; 415 416 /* calling the read routine */ 417 mcd_doread(MCD_S_BEGIN,&(cd->mbx)); 418 /* triggers mcd_start, when successful finished */ 419 return; 420} 421 422int mcdioctl(dev_t dev, int cmd, caddr_t addr, int flags) 423{ 424 struct mcd_data *cd; 425 int unit,part; 426 427 unit = mcd_unit(dev); 428 part = mcd_part(dev); 429 cd = mcd_data + unit; 430 431 if (mcd_getstat(unit, 1) < 0) /* detect disk change too */ 432 return EIO; 433 if (!(cd->flags & MCDVALID)) 434 return EIO; 435MCD_TRACE("ioctl called 0x%x\n",cmd,0,0,0); 436 437 switch (cmd) { 438 case DIOCSBAD: 439 return EINVAL; 440 case DIOCGDINFO: 441 *(struct disklabel *) addr = cd->dlabel; 442 return 0; 443 case DIOCGPART: 444 ((struct partinfo *) addr)->disklab = &cd->dlabel; 445 ((struct partinfo *) addr)->part = 446 &cd->dlabel.d_partitions[mcd_part(dev)]; 447 return 0; 448 449 /* 450 * a bit silly, but someone might want to test something on a 451 * section of cdrom. 452 */ 453 case DIOCWDINFO: 454 case DIOCSDINFO: 455 if ((flags & FWRITE) == 0) 456 return EBADF; 457 else { 458 return setdisklabel(&cd->dlabel, 459 (struct disklabel *) addr, 460 0, 461 0); 462 } 463 case DIOCWLABEL: 464 return EBADF; 465 case CDIOCPLAYTRACKS: 466 return mcd_playtracks(unit, (struct ioc_play_track *) addr); 467 case CDIOCPLAYBLOCKS: 468 return EINVAL; 469 case CDIOCPLAYMSF: 470 return mcd_playmsf(unit, (struct ioc_play_msf *) addr); 471 case CDIOCREADSUBCHANNEL: 472 return mcd_subchan(unit, (struct ioc_read_subchannel *) addr); 473 case CDIOREADTOCHEADER: 474 return mcd_toc_header(unit, (struct ioc_toc_header *) addr); 475 case CDIOREADTOCENTRYS: 476 return mcd_toc_entrys(unit, (struct ioc_read_toc_entry *) addr); 477 case CDIOCSETPATCH: 478 case CDIOCGETVOL: 479 case CDIOCSETVOL: 480 case CDIOCSETMONO: 481 case CDIOCSETSTERIO: 482 case CDIOCSETMUTE: 483 case CDIOCSETLEFT: 484 case CDIOCSETRIGHT: 485 return EINVAL; 486 case CDIOCRESUME: 487 return mcd_resume(unit); 488 case CDIOCPAUSE: 489 return mcd_pause(unit); 490 case CDIOCSTART: 491 return EINVAL; 492 case CDIOCSTOP: 493 return mcd_stop(unit); 494 case CDIOCEJECT: 495 return mcd_eject(unit); 496 case CDIOCSETDEBUG: 497 cd->debug = 1; 498 return 0; 499 case CDIOCCLRDEBUG: 500 cd->debug = 0; 501 return 0; 502 case CDIOCRESET: 503 return mcd_hard_reset(unit); 504 default: 505 return ENOTTY; 506 } 507 /*NOTREACHED*/ 508} 509 510/* this could have been taken from scsi/cd.c, but it is not clear 511 * whether the scsi cd driver is linked in 512 */ 513static int mcd_getdisklabel(int unit) 514{ 515 struct mcd_data *cd = mcd_data + unit; 516 517 if (cd->flags & MCDLABEL) 518 return -1; 519 520 bzero(&cd->dlabel,sizeof(struct disklabel)); 521 strncpy(cd->dlabel.d_typename,"Mitsumi CD ROM ",16); 522 strncpy(cd->dlabel.d_packname,"unknown ",16); 523 cd->dlabel.d_secsize = cd->blksize; 524 cd->dlabel.d_nsectors = 100; 525 cd->dlabel.d_ntracks = 1; 526 cd->dlabel.d_ncylinders = (cd->disksize/100)+1; 527 cd->dlabel.d_secpercyl = 100; 528 cd->dlabel.d_secperunit = cd->disksize; 529 cd->dlabel.d_rpm = 300; 530 cd->dlabel.d_interleave = 1; 531 cd->dlabel.d_flags = D_REMOVABLE; 532 cd->dlabel.d_npartitions= 1; 533 cd->dlabel.d_partitions[0].p_offset = 0; 534 cd->dlabel.d_partitions[0].p_size = cd->disksize; 535 cd->dlabel.d_partitions[0].p_fstype = 9; 536 cd->dlabel.d_magic = DISKMAGIC; 537 cd->dlabel.d_magic2 = DISKMAGIC; 538 cd->dlabel.d_checksum = dkcksum(&cd->dlabel); 539 540 cd->flags |= MCDLABEL; 541 return 0; 542} 543 544int mcdsize(dev_t dev) 545{ 546 int size; 547 int unit = mcd_unit(dev); 548 struct mcd_data *cd = mcd_data + unit; 549 550 if (mcd_volinfo(unit) == 0) { 551 cd->blksize = MCDBLK; 552 size = msf2hsg(cd->volinfo.vol_msf); 553 cd->disksize = size * (MCDBLK/DEV_BSIZE); 554 return 0; 555 } 556 return -1; 557} 558 559/*************************************************************** 560 * lower level of driver starts here 561 **************************************************************/ 562 563#ifdef NOTDEF 564static char 565irqs[] = { 566 0x00,0x00,0x10,0x20,0x00,0x30,0x00,0x00, 567 0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00 568}; 569 570static char 571drqs[] = { 572 0x00,0x01,0x00,0x03,0x00,0x05,0x06,0x07, 573}; 574#endif 575 576static void 577mcd_configure(struct mcd_data *cd) 578{ 579 outb(cd->iobase+mcd_config,cd->config); 580} 581 582/* Wait for non-busy - return 0 on timeout */ 583static int 584twiddle_thumbs(int port, int unit, int count, char *whine) 585{ 586 int i; 587 588 for (i = 0; i < count; i++) { 589 if (!(inb(port+MCD_FLAGS) & MCD_ST_BUSY)) { 590 return 1; 591 } 592 } 593 printf("mcd%d: timeout %s\n", unit, whine); 594 return 0; 595} 596 597/* check to see if a Mitsumi CD-ROM is attached to the ISA bus */ 598 599int 600mcd_probe(struct isa_device *dev) 601{ 602 int port = dev->id_iobase; 603 int unit = dev->id_unit; 604 int i, j; 605 int status; 606 unsigned char stbytes[3]; 607 608 mcd_data[unit].flags = MCDPROBING; 609 610#ifdef NOTDEF 611 /* get irq/drq configuration word */ 612 mcd_data[unit].config = irqs[dev->id_irq]; /* | drqs[dev->id_drq];*/ 613#else 614 mcd_data[unit].config = 0; 615#endif 616 617 /* send a reset */ 618 outb(port+MCD_FLAGS, M_RESET); 619 620 /* 621 * delay awhile by getting any pending garbage (old data) and 622 * throwing it away. 623 */ 624 for (i = 1000000; i != 0; i--) 625 inb(port+MCD_FLAGS); 626 627 /* Get status */ 628 outb(port+MCD_DATA, MCD_CMDGETSTAT); 629 if (!twiddle_thumbs(port, unit, 1000000, "getting status")) 630 return 0; /* Timeout */ 631 status = inb(port+MCD_DATA); 632 if (status != MCDCDABSENT && status != MCDCDPRESENT && 633 status != MCDSOPEN && status != MCDSCLOSED) 634 return 0; /* Not actually a Mitsumi drive here */ 635 /* Get version information */ 636 outb(port+MCD_DATA, MCD_CMDCONTINFO); 637 for (j = 0; j < 3; j++) { 638 if (!twiddle_thumbs(port, unit, 3000, "getting version info")) 639 return 0; 640 stbytes[j] = (inb(port+MCD_DATA) & 0xFF); 641 } 642 printf("mcd%d: version information is %x %c %x\n", unit, 643 stbytes[0], stbytes[1], stbytes[2]); 644 if (stbytes[1] >= 4) { 645 outb(port+MCD_CTRL, M_PICKLE); 646 mcd_data[unit].flags |= MCDNEWMODEL; 647 printf("mcd%d: Adjusted for newer drive model\n", unit); 648 } 649 return 4; 650} 651 652 653static int 654mcd_waitrdy(int port,int dly) 655{ 656 int i; 657 658 /* wait until xfer port senses data ready */ 659 for (i=0; i<dly; i+=MIN_DELAY) { 660 if ((inb(port+mcd_xfer) & MCD_ST_BUSY)==0) 661 return 0; 662 DELAY(MIN_DELAY); 663 } 664 return -1; 665} 666 667static int 668mcd_getreply(int unit,int dly) 669{ 670 int i; 671 struct mcd_data *cd = mcd_data + unit; 672 int port = cd->iobase; 673 674 /* wait data to become ready */ 675 if (mcd_waitrdy(port,dly)<0) { 676 printf("mcd%d: timeout getreply\n",unit); 677 return -1; 678 } 679 680 /* get the data */ 681 return inb(port+mcd_status) & 0xFF; 682} 683 684static int 685mcd_getstat(int unit,int sflg) 686{ 687 int i; 688 struct mcd_data *cd = mcd_data + unit; 689 int port = cd->iobase; 690 691 /* get the status */ 692 if (sflg) 693 outb(port+mcd_command, MCD_CMDGETSTAT); 694 i = mcd_getreply(unit,DELAY_GETREPLY); 695 if (i<0) return -1; 696 697 cd->status = i; 698 699 if (mcd_setflags(unit,cd) < 0) 700 return -2; 701 return cd->status; 702} 703 704static int 705mcd_setflags(int unit, struct mcd_data *cd) 706{ 707 /* check flags */ 708 if ( (cd->status & (MCDDSKCHNG|MCDDOOROPEN)) 709 || !(cd->status & MCDDSKIN)) { 710 MCD_TRACE("setflags: sensed DSKCHNG or DOOROPEN or !DSKIN\n",0,0,0,0); 711 mcd_soft_reset(unit); 712 return -1; 713 } 714 715 if (cd->status & MCDAUDIOBSY) 716 cd->audio_status = CD_AS_PLAY_IN_PROGRESS; 717 else if (cd->audio_status == CD_AS_PLAY_IN_PROGRESS) 718 cd->audio_status = CD_AS_PLAY_COMPLETED; 719 return 0; 720} 721 722static int 723mcd_get(int unit, char *buf, int nmax) 724{ 725 int port = mcd_data[unit].iobase; 726 int i,k; 727 728 for (i=0; i<nmax; i++) { 729 /* wait for data */ 730 if ((k = mcd_getreply(unit,DELAY_GETREPLY)) < 0) { 731 printf("mcd%d: timeout mcd_get\n",unit); 732 return -1; 733 } 734 buf[i] = k; 735 } 736 return i; 737} 738 739static int 740mcd_send(int unit, int cmd,int nretrys) 741{ 742 int i,k=0; 743 int port = mcd_data[unit].iobase; 744 745/*MCD_TRACE("mcd_send: command = 0x%02x\n",cmd,0,0,0);*/ 746 for (i=0; i<nretrys; i++) { 747 outb(port+mcd_command, cmd); 748 if ((k=mcd_getstat(unit,0)) != -1) 749 break; 750 } 751 if (k == -2) { 752 printf("mcd%d: media changed\n",unit); 753 return -1; 754 } 755 if (i == nretrys) { 756 printf("mcd%d: mcd_send retry cnt exceeded\n",unit); 757 return -1; 758 } 759/*MCD_TRACE("mcd_send: done\n",0,0,0,0);*/ 760 return 0; 761} 762 763static int 764bcd2bin(bcd_t b) 765{ 766 return (b >> 4) * 10 + (b & 15); 767} 768 769static bcd_t 770bin2bcd(int b) 771{ 772 return ((b / 10) << 4) | (b % 10); 773} 774 775static void 776hsg2msf(int hsg, bcd_t *msf) 777{ 778 hsg += 150; 779 M_msf(msf) = bin2bcd(hsg / 4500); 780 hsg %= 4500; 781 S_msf(msf) = bin2bcd(hsg / 75); 782 F_msf(msf) = bin2bcd(hsg % 75); 783} 784 785static int 786msf2hsg(bcd_t *msf) 787{ 788 return (bcd2bin(M_msf(msf)) * 60 + 789 bcd2bin(S_msf(msf))) * 75 + 790 bcd2bin(F_msf(msf)) - 150; 791} 792 793static int 794mcd_volinfo(int unit) 795{ 796 struct mcd_data *cd = mcd_data + unit; 797 int i; 798 799 /* Just return if we already have it */ 800 if (cd->flags & MCDVOLINFO) return 0; 801 802/*MCD_TRACE("mcd_volinfo: enter\n",0,0,0,0);*/ 803 804 /* send volume info command */ 805 if (mcd_send(unit,MCD_CMDGETVOLINFO,MCD_RETRYS) < 0) 806 return EIO; 807 808 /* get data */ 809 if (mcd_get(unit,(char*) &cd->volinfo,sizeof(struct mcd_volinfo)) < 0) { 810 printf("mcd%d: mcd_volinfo: error read data\n",unit); 811 return EIO; 812 } 813 814 if (cd->volinfo.trk_low != 0 || cd->volinfo.trk_high != 0) { 815 cd->flags |= MCDVOLINFO; /* volinfo is OK */ 816 return 0; 817 } 818 819 return EINVAL; 820} 821 822void 823mcdintr(unit) 824 int unit; 825{ 826 MCD_TRACE("stray interrupt\n",0,0,0,0); 827} 828 829/* state machine to process read requests 830 * initialize with MCD_S_BEGIN: calculate sizes, and read status 831 * MCD_S_WAITSTAT: wait for status reply, set mode 832 * MCD_S_WAITMODE: waits for status reply from set mode, set read command 833 * MCD_S_WAITREAD: wait for read ready, read data 834 */ 835static struct mcd_mbx *mbxsave; 836 837static void 838mcd_doread(int state, struct mcd_mbx *mbxin) 839{ 840 struct mcd_mbx *mbx = (state!=MCD_S_BEGIN) ? mbxsave : mbxin; 841 int unit = mbx->unit; 842 int port = mbx->port; 843 int com_port = mbx->port + mcd_command; 844 int data_port = mbx->port + mcd_rdata; 845 struct buf *bp = mbx->bp; 846 struct mcd_data *cd = mcd_data + unit; 847 848 int rm,i,k; 849 struct mcd_read2 rbuf; 850 int blknum; 851 caddr_t addr; 852 853loop: 854 switch (state) { 855 case MCD_S_BEGIN: 856 mbx = mbxsave = mbxin; 857 858 case MCD_S_BEGIN1: 859 /* get status */ 860 outb(com_port, MCD_CMDGETSTAT); 861 mbx->count = RDELAY_WAITSTAT; 862 timeout((timeout_func_t)mcd_doread, 863 (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */ 864 return; 865 case MCD_S_WAITSTAT: 866 untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITSTAT); 867 if (mbx->count-- >= 0) { 868 if (inb(port+mcd_xfer) & MCD_ST_BUSY) { 869 timeout((timeout_func_t)mcd_doread, 870 (caddr_t)MCD_S_WAITSTAT,hz/100); /* XXX */ 871 return; 872 } 873 cd->status = inb(port+mcd_status) & 0xFF; 874 if (mcd_setflags(unit,cd) < 0) 875 goto changed; 876 MCD_TRACE("got WAITSTAT delay=%d\n", 877 RDELAY_WAITSTAT-mbx->count,0,0,0); 878 /* reject, if audio active */ 879 if (cd->status & MCDAUDIOBSY) { 880 printf("mcd%d: audio is active\n",unit); 881 goto readerr; 882 } 883 884 /* to check for raw/cooked mode */ 885 if (cd->flags & MCDREADRAW) { 886 rm = (cd->flags & MCDNEWMODEL) ? 887 MCD_MD_BIN_RAW : MCD_MD_RAW; 888 mbx->sz = MCDRBLK; 889 } else { 890 rm = (cd->flags & MCDNEWMODEL) ? 891 MCD_MD_BIN_COOKED : MCD_MD_COOKED; 892 mbx->sz = cd->blksize; 893 } 894 895 if (rm == cd->curr_mode) 896 goto modedone; 897 898 mbx->count = RDELAY_WAITMODE; 899 900 cd->curr_mode = MCD_MD_UNKNOWN; 901 mbx->mode = rm; 902 mcd_put(com_port, MCD_CMDSETMODE); 903 mcd_put(com_port, rm); 904 905 timeout((timeout_func_t)mcd_doread, 906 (caddr_t)MCD_S_WAITMODE,hz/100); /* XXX */ 907 return; 908 } else { 909 printf("mcd%d: timeout getstatus\n",unit); 910 goto readerr; 911 } 912 913 case MCD_S_WAITMODE: 914 untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE); 915 if (mbx->count-- < 0) { 916 printf("mcd%d: timeout set mode\n",unit); 917 goto readerr; 918 } 919 if (inb(port+mcd_xfer) & MCD_ST_BUSY) { 920 timeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITMODE,hz/100); 921 return; 922 } 923 cd->status = inb(port+mcd_status) & 0xFF; 924 if (mcd_setflags(unit,cd) < 0) 925 goto changed; 926 cd->curr_mode = mbx->mode; 927 MCD_TRACE("got WAITMODE delay=%d\n", 928 RDELAY_WAITMODE-mbx->count,0,0,0); 929modedone: 930 /* for first block */ 931 mbx->nblk = (bp->b_bcount + (mbx->sz-1)) / mbx->sz; 932 mbx->skip = 0; 933 934nextblock: 935 blknum = (bp->b_blkno / (mbx->sz/DEV_BSIZE)) 936 + mbx->p_offset + mbx->skip/mbx->sz; 937 938 MCD_TRACE("mcd_doread: read blknum=%d for bp=0x%x\n", 939 blknum,bp,0,0); 940 941 /* build parameter block */ 942 hsg2msf(blknum,rbuf.start_msf); 943 944 /* send the read command */ 945 disable_intr(); 946 mcd_put(com_port,MCD_CMDREAD2); 947 mcd_put(com_port,rbuf.start_msf[0]); 948 mcd_put(com_port,rbuf.start_msf[1]); 949 mcd_put(com_port,rbuf.start_msf[2]); 950 mcd_put(com_port,0); 951 mcd_put(com_port,0); 952 mcd_put(com_port,1); 953 enable_intr(); 954 955 /* Spin briefly (<= 2ms) to avoid missing next block */ 956 for (i = 0; i < 20; i++) { 957 k = inb(port+mcd_xfer); 958 if (!(k & 2)) 959 goto got_it; 960 DELAY(100); 961 } 962 963 mbx->count = RDELAY_WAITREAD; 964 timeout((timeout_func_t)mcd_doread, 965 (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */ 966 return; 967 case MCD_S_WAITREAD: 968 untimeout((timeout_func_t)mcd_doread,(caddr_t)MCD_S_WAITREAD); 969 if (mbx->count-- > 0) { 970 k = inb(port+mcd_xfer); 971 if (!(k & 2)) { /* XXX */ 972 MCD_TRACE("got data delay=%d\n", 973 RDELAY_WAITREAD-mbx->count,0,0,0); 974 got_it: 975 /* data is ready */ 976 addr = bp->b_un.b_addr + mbx->skip; 977 978 outb(port+mcd_ctl2,0x04); /* XXX */ 979 for (i=0; i<mbx->sz; i++) 980 *addr++ = inb(data_port); 981 outb(port+mcd_ctl2,0x0c); /* XXX */ 982 983 k = inb(port+mcd_xfer); 984 /* If we still have some junk, read it too */ 985 if (!(k & 2)) { 986 outb(port+mcd_ctl2,0x04); /* XXX */ 987 (void)inb(data_port); 988 (void)inb(data_port); 989 outb(port+mcd_ctl2,0x0c); /* XXX */ 990 } 991 992 if (--mbx->nblk > 0) { 993 mbx->skip += mbx->sz; 994 goto nextblock; 995 } 996 997 /* return buffer */ 998 bp->b_resid = 0; 999 biodone(bp); 1000 1001 cd->flags &= ~(MCDMBXBSY|MCDREADRAW); 1002 mcd_start(mbx->unit); 1003 return; 1004 } 1005 if (!(k & MCD_ST_BUSY)) { 1006 cd->status = inb(port+mcd_status) & 0xFF; 1007 if (mcd_setflags(unit,cd) < 0) 1008 goto changed; 1009 } 1010 timeout((timeout_func_t)mcd_doread, 1011 (caddr_t)MCD_S_WAITREAD,hz/100); /* XXX */ 1012 return; 1013 } else { 1014 printf("mcd%d: timeout read data\n",unit); 1015 goto readerr; 1016 } 1017 } 1018 1019readerr: 1020 if (mbx->retry-- > 0) { 1021 printf("mcd%d: retrying\n",unit); 1022 state = MCD_S_BEGIN1; 1023 goto loop; 1024 } 1025harderr: 1026 /* invalidate the buffer */ 1027 bp->b_flags |= B_ERROR; 1028 bp->b_resid = bp->b_bcount; 1029 biodone(bp); 1030 1031 cd->flags &= ~(MCDMBXBSY|MCDREADRAW); 1032 mcd_start(mbx->unit); 1033 return; 1034 1035changed: 1036 printf("mcd%d: media changed\n", unit); 1037 goto harderr; 1038 1039#ifdef NOTDEF 1040 printf("mcd%d: unit timeout, resetting\n",mbx->unit); 1041 outb(mbx->port+mcd_reset,MCD_CMDRESET); 1042 DELAY(300000); 1043 (void)mcd_getstat(mbx->unit,1); 1044 (void)mcd_getstat(mbx->unit,1); 1045 /*cd->status &= ~MCDDSKCHNG; */ 1046 cd->debug = 1; /* preventive set debug mode */ 1047 1048#endif 1049 1050} 1051 1052static int 1053mcd_eject(int unit) 1054{ 1055 struct mcd_data *cd = mcd_data + unit; 1056 int port = cd->iobase; 1057 1058 outb(port+mcd_command, MCD_CMDEJECTDISK); 1059 if (mcd_getstat(unit,0) == -1) return EIO; 1060 return 0; 1061} 1062 1063static int 1064mcd_hard_reset(int unit) 1065{ 1066 struct mcd_data *cd = mcd_data + unit; 1067 int port = cd->iobase; 1068 1069 outb(port+mcd_reset,MCD_CMDRESET); 1070 cd->curr_mode = MCD_MD_UNKNOWN; 1071 cd->audio_status = CD_AS_AUDIO_INVALID; 1072 return 0; 1073} 1074 1075static void 1076mcd_soft_reset(int unit) 1077{ 1078 struct mcd_data *cd = mcd_data + unit; 1079 int i; 1080 1081 cd->openflags = 0; 1082 cd->flags &= (MCDINIT|MCDPROBING|MCDNEWMODEL); 1083 cd->curr_mode = MCD_MD_UNKNOWN; 1084 for (i=0; i<MAXPARTITIONS; i++) cd->partflags[i] = 0; 1085 cd->audio_status = CD_AS_AUDIO_INVALID; 1086} 1087 1088static int 1089mcd_setmode(int unit, int mode) 1090{ 1091 struct mcd_data *cd = mcd_data + unit; 1092 int port = cd->iobase; 1093 int retry, st; 1094 1095 if (cd->curr_mode == mode) 1096 return 0; 1097 if (cd->debug) 1098 printf("mcd%d: setting mode to %d\n", unit, mode); 1099 for(retry=0; retry<MCD_RETRYS; retry++) 1100 { 1101 cd->curr_mode = MCD_MD_UNKNOWN; 1102 outb(port+mcd_command, MCD_CMDSETMODE); 1103 outb(port+mcd_command, mode); 1104 if ((st = mcd_getstat(unit, 0)) >= 0) { 1105 cd->curr_mode = mode; 1106 return 0; 1107 } 1108 if (st == -2) { 1109 printf("mcd%d: media changed\n", unit); 1110 break; 1111 } 1112 } 1113 1114 return -1; 1115} 1116 1117static int 1118mcd_toc_header(int unit, struct ioc_toc_header *th) 1119{ 1120 struct mcd_data *cd = mcd_data + unit; 1121 int r; 1122 1123 if ((r = mcd_volinfo(unit)) != 0) 1124 return r; 1125 1126 th->len = msf2hsg(cd->volinfo.vol_msf); 1127 th->starting_track = bcd2bin(cd->volinfo.trk_low); 1128 th->ending_track = bcd2bin(cd->volinfo.trk_high); 1129 1130 return 0; 1131} 1132 1133static int 1134mcd_read_toc(int unit) 1135{ 1136 struct mcd_data *cd = mcd_data + unit; 1137 struct ioc_toc_header th; 1138 struct mcd_qchninfo q; 1139 int rc, trk, idx, retry; 1140 1141 /* Only read TOC if needed */ 1142 if (cd->flags & MCDTOC) 1143 return 0; 1144 1145 if (cd->debug) 1146 printf("mcd%d: reading toc header\n", unit); 1147 1148 if ((rc = mcd_toc_header(unit, &th)) != 0) 1149 return rc; 1150 1151 if (mcd_setmode(unit, MCD_MD_TOC) != 0) 1152 return EIO; 1153 1154 if (cd->debug) 1155 printf("mcd%d: get_toc reading qchannel info\n",unit); 1156 1157 for(trk=th.starting_track; trk<=th.ending_track; trk++) 1158 cd->toc[trk].idx_no = 0; 1159 trk = th.ending_track - th.starting_track + 1; 1160 for(retry=0; retry<300 && trk>0; retry++) 1161 { 1162 if (mcd_getqchan(unit, &q) < 0) break; 1163 idx = bcd2bin(q.idx_no); 1164 if (idx>=th.starting_track && idx<=th.ending_track && q.trk_no==0) { 1165 if (cd->toc[idx].idx_no == 0) { 1166 cd->toc[idx] = q; 1167 trk--; 1168 } 1169 } 1170 } 1171 1172 if (mcd_setmode(unit, MCD_MD_COOKED) != 0) 1173 return EIO; 1174 1175 if (trk != 0) 1176 return ENXIO; 1177 1178 /* add a fake last+1 */ 1179 idx = th.ending_track + 1; 1180 cd->toc[idx].ctrl_adr = cd->toc[idx-1].ctrl_adr; 1181 cd->toc[idx].trk_no = 0; 1182 cd->toc[idx].idx_no = 0xAA; 1183 cd->toc[idx].hd_pos_msf[0] = cd->volinfo.vol_msf[0]; 1184 cd->toc[idx].hd_pos_msf[1] = cd->volinfo.vol_msf[1]; 1185 cd->toc[idx].hd_pos_msf[2] = cd->volinfo.vol_msf[2]; 1186 1187 if (cd->debug) 1188 { int i; 1189 for (i = th.starting_track; i <= idx; i++) 1190 printf("mcd%d: trk %d idx %d pos %d %d %d\n", 1191 unit, i, 1192 cd->toc[i].idx_no, 1193 bcd2bin(cd->toc[i].hd_pos_msf[0]), 1194 bcd2bin(cd->toc[i].hd_pos_msf[1]), 1195 bcd2bin(cd->toc[i].hd_pos_msf[2])); 1196 } 1197 1198 cd->flags |= MCDTOC; 1199 1200 return 0; 1201} 1202 1203static int 1204mcd_toc_entrys(int unit, struct ioc_read_toc_entry *te) 1205{ 1206 struct mcd_data *cd = mcd_data + unit; 1207 struct cd_toc_entry entries[MCD_MAXTOCS]; 1208 struct ioc_toc_header th; 1209 int rc, i, len = te->data_len; 1210 1211 /* Make sure we have a valid toc */ 1212 if ((rc=mcd_read_toc(unit)) != 0) 1213 return rc; 1214 1215 /* find the toc to copy*/ 1216 i = te->starting_track; 1217 if (i == MCD_LASTPLUS1) 1218 i = bcd2bin(cd->volinfo.trk_high) + 1; 1219 1220 /* verify starting track */ 1221 if (i < bcd2bin(cd->volinfo.trk_low) || 1222 i > bcd2bin(cd->volinfo.trk_high)+1) { 1223 return EINVAL; 1224 } 1225 1226 /* do we have room */ 1227 if ( len > sizeof(entries) 1228 || len < sizeof(struct cd_toc_entry) 1229 || (len % sizeof(struct cd_toc_entry)) != 0 1230 ) 1231 return EINVAL; 1232 1233 /* Copy the toc header */ 1234 if ((rc = mcd_toc_header(unit, &th)) != 0) 1235 return rc; 1236 1237 do { 1238 /* copy the toc data */ 1239 entries[i-1].control = cd->toc[i].ctrl_adr; 1240 entries[i-1].addr_type = te->address_format; 1241 entries[i-1].track = i; 1242 if (te->address_format == CD_MSF_FORMAT) { 1243 entries[i-1].addr.msf.unused = 0; 1244 entries[i-1].addr.msf.minute = bcd2bin(cd->toc[i].hd_pos_msf[0]); 1245 entries[i-1].addr.msf.second = bcd2bin(cd->toc[i].hd_pos_msf[1]); 1246 entries[i-1].addr.msf.frame = bcd2bin(cd->toc[i].hd_pos_msf[2]); 1247 } 1248 len -= sizeof(struct cd_toc_entry); 1249 i++; 1250 } 1251 while (len > 0 && i <= th.ending_track + 2); 1252 1253 /* copy the data back */ 1254 if (copyout(entries, te->data, (i - 1) * sizeof(struct cd_toc_entry)) != 0) 1255 return EFAULT; 1256 1257 return 0; 1258} 1259 1260static int 1261mcd_stop(int unit) 1262{ 1263 struct mcd_data *cd = mcd_data + unit; 1264 1265 if (mcd_send(unit, MCD_CMDSTOPAUDIO, MCD_RETRYS) < 0) 1266 return ENXIO; 1267 cd->audio_status = CD_AS_PLAY_COMPLETED; 1268 return 0; 1269} 1270 1271static int 1272mcd_getqchan(int unit, struct mcd_qchninfo *q) 1273{ 1274 struct mcd_data *cd = mcd_data + unit; 1275 1276 if (mcd_send(unit, MCD_CMDGETQCHN, MCD_RETRYS) < 0) 1277 return -1; 1278 if (mcd_get(unit, (char *) q, sizeof(struct mcd_qchninfo)) < 0) 1279 return -1; 1280 if (cd->debug) { 1281 printf("mcd%d: qchannel ctl=%d trk=%d ind=%d pos=%d:%d.%d\n", 1282 unit, 1283 q->ctrl_adr, bcd2bin(q->trk_no), bcd2bin(q->idx_no), 1284 bcd2bin(q->hd_pos_msf[0]), bcd2bin(q->hd_pos_msf[1]), 1285 bcd2bin(q->hd_pos_msf[2])); 1286 } 1287 return 0; 1288} 1289 1290static int 1291mcd_subchan(int unit, struct ioc_read_subchannel *sc) 1292{ 1293 struct mcd_data *cd = mcd_data + unit; 1294 struct mcd_qchninfo q; 1295 struct cd_sub_channel_info data; 1296 1297 if (cd->debug) 1298 printf("mcd%d: subchan af=%d, df=%d\n", unit, 1299 sc->address_format, 1300 sc->data_format); 1301 1302 if (sc->address_format != CD_MSF_FORMAT) 1303 return EINVAL; 1304 1305 if (sc->data_format != CD_CURRENT_POSITION) 1306 return EINVAL; 1307 1308 if (mcd_setmode(unit, MCD_MD_COOKED) != 0) 1309 return EIO; 1310 1311 if (mcd_getqchan(unit, &q) < 0) 1312 return EIO; 1313 1314 data.header.audio_status = cd->audio_status; 1315 data.what.position.data_format = CD_MSF_FORMAT; 1316 data.what.position.track_number = bcd2bin(q.trk_no); 1317 1318 if (copyout(&data, sc->data, sizeof(struct cd_sub_channel_info))!=0) 1319 return EFAULT; 1320 return 0; 1321} 1322 1323static int 1324mcd_playmsf(int unit, struct ioc_play_msf *pt) 1325{ 1326 struct mcd_read2 pb; 1327 1328 if (mcd_setmode(unit, MCD_MD_COOKED) != 0) 1329 return EIO; 1330 1331 pb.start_msf[0] = bin2bcd(pt->start_m); 1332 pb.start_msf[1] = bin2bcd(pt->start_s); 1333 pb.start_msf[2] = bin2bcd(pt->start_f); 1334 pb.end_msf[0] = bin2bcd(pt->end_m); 1335 pb.end_msf[1] = bin2bcd(pt->end_s); 1336 pb.end_msf[2] = bin2bcd(pt->end_f); 1337 1338 return mcd_play(unit, &pb); 1339} 1340 1341static int 1342mcd_playtracks(int unit, struct ioc_play_track *pt) 1343{ 1344 struct mcd_data *cd = mcd_data + unit; 1345 struct mcd_read2 pb; 1346 int a = pt->start_track; 1347 int z = pt->end_track; 1348 int rc; 1349 1350 if ((rc = mcd_read_toc(unit)) != 0) 1351 return rc; 1352 1353 if (cd->debug) 1354 printf("mcd%d: playtracks from %d:%d to %d:%d\n", unit, 1355 a, pt->start_index, z, pt->end_index); 1356 1357 if ( a < bcd2bin(cd->volinfo.trk_low) 1358 || a > bcd2bin(cd->volinfo.trk_high) 1359 || a > z 1360 || z < bcd2bin(cd->volinfo.trk_low) 1361 || z > bcd2bin(cd->volinfo.trk_high)) 1362 return EINVAL; 1363 1364 pb.start_msf[0] = cd->toc[a].hd_pos_msf[0]; 1365 pb.start_msf[1] = cd->toc[a].hd_pos_msf[1]; 1366 pb.start_msf[2] = cd->toc[a].hd_pos_msf[2]; 1367 pb.end_msf[0] = cd->toc[z+1].hd_pos_msf[0]; 1368 pb.end_msf[1] = cd->toc[z+1].hd_pos_msf[1]; 1369 pb.end_msf[2] = cd->toc[z+1].hd_pos_msf[2]; 1370 1371 return mcd_play(unit, &pb); 1372} 1373 1374static int 1375mcd_play(int unit, struct mcd_read2 *pb) 1376{ 1377 struct mcd_data *cd = mcd_data + unit; 1378 int com_port = cd->iobase + mcd_command; 1379 int retry, st = -1, status; 1380 1381 cd->lastpb = *pb; 1382 for(retry=0; retry<MCD_RETRYS; retry++) { 1383 1384 disable_intr(); 1385 outb(com_port, MCD_CMDREAD2); 1386 outb(com_port, pb->start_msf[0]); 1387 outb(com_port, pb->start_msf[1]); 1388 outb(com_port, pb->start_msf[2]); 1389 outb(com_port, pb->end_msf[0]); 1390 outb(com_port, pb->end_msf[1]); 1391 outb(com_port, pb->end_msf[2]); 1392 enable_intr(); 1393 1394 status=mcd_getstat(unit, 0); 1395 if (status == -1) 1396 continue; 1397 else if (status != -2) 1398 st = 0; 1399 break; 1400 } 1401 1402 if (status == -2) { 1403 printf("mcd%d: media changed\n", unit); 1404 return ENXIO; 1405 } 1406 if (cd->debug) 1407 printf("mcd%d: mcd_play retry=%d, status=0x%02x\n", unit, retry, status); 1408 if (st < 0) 1409 return ENXIO; 1410 cd->audio_status = CD_AS_PLAY_IN_PROGRESS; 1411 return 0; 1412} 1413 1414static int 1415mcd_pause(int unit) 1416{ 1417 struct mcd_data *cd = mcd_data + unit; 1418 struct mcd_qchninfo q; 1419 int rc; 1420 1421 /* Verify current status */ 1422 if (cd->audio_status != CD_AS_PLAY_IN_PROGRESS) { 1423 if (cd->debug) 1424 printf("mcd%d: pause attempted when not playing\n", unit); 1425 return EINVAL; 1426 } 1427 1428 /* Get the current position */ 1429 if (mcd_getqchan(unit, &q) < 0) 1430 return EIO; 1431 1432 /* Copy it into lastpb */ 1433 cd->lastpb.start_msf[0] = q.hd_pos_msf[0]; 1434 cd->lastpb.start_msf[1] = q.hd_pos_msf[1]; 1435 cd->lastpb.start_msf[2] = q.hd_pos_msf[2]; 1436 1437 /* Stop playing */ 1438 if ((rc=mcd_stop(unit)) != 0) 1439 return rc; 1440 1441 /* Set the proper status and exit */ 1442 cd->audio_status = CD_AS_PLAY_PAUSED; 1443 return 0; 1444} 1445 1446static int 1447mcd_resume(int unit) 1448{ 1449 struct mcd_data *cd = mcd_data + unit; 1450 1451 if (cd->audio_status != CD_AS_PLAY_PAUSED) 1452 return EINVAL; 1453 return mcd_play(unit, &cd->lastpb); 1454} 1455#endif /* NMCD > 0 */ 1456