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