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