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