static int mcd_tray_move(struct cdrom_device_info * cdi, int position) { int i; if (position) { /* Eject */ /* all drives can at least stop! */ if (audioStatus == CDROM_AUDIO_PLAY) { outb(MCMD_STOP, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); } audioStatus = CDROM_AUDIO_NO_STATUS; outb(MCMD_EJECT, MCDPORT(0)); /* * the status (i) shows failure on all but the FX drives. * But nothing we can do about that in software! * So just read the status and forget it. - Jon. */ i = getMcdStatus(MCD_STATUS_DELAY); return 0; } else return -EINVAL; }
static int mcdStatus(void) { int i; int st; st = inb(MCDPORT(1)) & MFL_STATUS; if (!st) { i = inb(MCDPORT(0)) & 0xFF; return i; } else return -1; }
static void mcd_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int st; st = inb(MCDPORT(1)) & 0xFF; test1(printk("<int1-%02X>", st)); if (!(st & MFL_STATUS)) { st = inb(MCDPORT(0)) & 0xFF; test1(printk("<int0-%02X>", st)); if ((st & 0xFF) != 0xFF) mcd_error = st ? st & 0xFF : -1; } }
int GetQChannelInfo(struct mcd_Toc *qp) { unsigned char notUsed; int retry; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_GET_Q_CHANNEL, MCDPORT(0)); if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; } if (retry >= MCD_RETRY_ATTEMPTS) return -1; if (getValue(&qp -> ctrl_addr) < 0) return -1; if (getValue(&qp -> track) < 0) return -1; if (getValue(&qp -> pointIndex) < 0) return -1; if (getValue(&qp -> trackTime.min) < 0) return -1; if (getValue(&qp -> trackTime.sec) < 0) return -1; if (getValue(&qp -> trackTime.frame) < 0) return -1; if (getValue(¬Used) < 0) return -1; if (getValue(&qp -> diskTime.min) < 0) return -1; if (getValue(&qp -> diskTime.sec) < 0) return -1; if (getValue(&qp -> diskTime.frame) < 0) return -1; return 0; }
static int getMcdStatus(int timeout) { int st; McdTimeout = timeout; SET_TIMER(mcdStatTimer, 1); sleep_on(&mcd_waitq); if (McdTimeout <= 0) return -1; st = inb(MCDPORT(0)) & 0xFF; if (st == 0xFF) return -1; if ((st & MST_BUSY) == 0 && audioStatus == CDROM_AUDIO_PLAY) /* XXX might be an error? look at q-channel? */ audioStatus = CDROM_AUDIO_COMPLETED; if (st & MST_DSK_CHG) { mcdDiskChanged = 1; tocUpToDate = 0; audioStatus = CDROM_AUDIO_NO_STATUS; } return st; }
static int getValue(unsigned char *result) { int count; int s; for (count = 0; count < 2000; count++) if (!(inb(MCDPORT(1)) & MFL_STATUS)) break; if (count >= 2000) { printk("mcd: getValue timeout\n"); return -1; } s = inb(MCDPORT(0)) & 0xFF; *result = (unsigned char) s; return 0; }
static void sendMcdCmd(int cmd, struct mcd_Play_msf *params) { outb(cmd, MCDPORT(0)); outb(params->start.min, MCDPORT(0)); outb(params->start.sec, MCDPORT(0)); outb(params->start.frame, MCDPORT(0)); outb(params->end.min, MCDPORT(0)); outb(params->end.sec, MCDPORT(0)); outb(params->end.frame, MCDPORT(0)); }
static void mcdStatTimer(unsigned long dummy) { if (!(inb(MCDPORT(1)) & MFL_STATUS)) { wake_up(&mcd_waitq); return; } McdTimeout--; if (McdTimeout <= 0) { wake_up(&mcd_waitq); return; } mcd_timer.function = mcdStatTimer; mod_timer(&mcd_timer, jiffies + 1); }
static int statusCmd(void) { int st = -1, retry; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { /* send get-status cmd */ outb(MCMD_GET_STATUS, MCDPORT(0)); st = getMcdStatus(MCD_STATUS_DELAY); if (st != -1) break; } return st; }
static int GetDiskInfo(void) { int retry; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_GET_DISK_INFO, MCDPORT(0)); if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; } if (retry >= MCD_RETRY_ATTEMPTS) return -1; if (getValue(&DiskInfo.first) < 0) return -1; if (getValue(&DiskInfo.last) < 0) return -1; DiskInfo.first = bcd2bin(DiskInfo.first); DiskInfo.last = bcd2bin(DiskInfo.last); #ifdef MCD_DEBUG printk ("Disk Info: first %d last %d length %02x:%02x.%02x first %02x:%02x.%02x\n", DiskInfo.first, DiskInfo.last, DiskInfo.diskLength.min, DiskInfo.diskLength.sec, DiskInfo.diskLength.frame, DiskInfo.firstTrack.min, DiskInfo.firstTrack.sec, DiskInfo.firstTrack.frame); #endif if (getValue(&DiskInfo.diskLength.min) < 0) return -1; if (getValue(&DiskInfo.diskLength.sec) < 0) return -1; if (getValue(&DiskInfo.diskLength.frame) < 0) return -1; if (getValue(&DiskInfo.firstTrack.min) < 0) return -1; if (getValue(&DiskInfo.firstTrack.sec) < 0) return -1; if (getValue(&DiskInfo.firstTrack.frame) < 0) return -1; return 0; }
static void mcdStatTimer(void) { if (!(inb(MCDPORT(1)) & MFL_STATUS)) { wake_up(&mcd_waitq); return; } McdTimeout--; if (McdTimeout <= 0) { wake_up(&mcd_waitq); return; } SET_TIMER(mcdStatTimer, 1); }
static int GetToc() { int i, px; int limit; int retry; struct mcd_Toc qInfo; for (i = 0; i < MAX_TRACKS; i++) Toc[i].pointIndex = 0; i = DiskInfo.last + 3; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_STOP, MCDPORT(0)); if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; } if (retry >= MCD_RETRY_ATTEMPTS) return -1; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_SET_MODE, MCDPORT(0)); outb(0x05, MCDPORT(0)); /* mode: toc */ mcd_mode = 0x05; if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; } if (retry >= MCD_RETRY_ATTEMPTS) return -1; for (limit = 300; limit > 0; limit--) { if (GetQChannelInfo(&qInfo) < 0) break; px = bcd2bin(qInfo.pointIndex); if (px > 0 && px < MAX_TRACKS && qInfo.track == 0) if (Toc[px].pointIndex == 0) { Toc[px] = qInfo; i--; } if (i <= 0) break; } Toc[DiskInfo.last + 1].diskTime = DiskInfo.diskLength; for (retry = 0; retry < MCD_RETRY_ATTEMPTS; retry++) { outb(MCMD_SET_MODE, MCDPORT(0)); outb(0x01, MCDPORT(0)); mcd_mode = 1; if (getMcdStatus(MCD_STATUS_DELAY) != -1) break; } #ifdef MCD_DEBUG for (i = 1; i <= DiskInfo.last; i++) printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n", i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); for (i = 100; i < 103; i++) printk("i = %2d ctl-adr = %02X track %2d px %02X %02X:%02X.%02X %02X:%02X.%02X\n", i, Toc[i].ctrl_addr, Toc[i].track, Toc[i].pointIndex, Toc[i].trackTime.min, Toc[i].trackTime.sec, Toc[i].trackTime.frame, Toc[i].diskTime.min, Toc[i].diskTime.sec, Toc[i].diskTime.frame); #endif return limit > 0 ? 0 : -1; }
__initfunc(int mcd_init(void)) { int count; unsigned char result[3]; char msg[80]; if (mcd_port <= 0 || mcd_irq <= 0) { printk("skip mcd_init\n"); return -EIO; } if (register_blkdev(MAJOR_NR, "mcd", &cdrom_fops) != 0) { printk("Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR); return -EIO; } if (check_region(mcd_port, 4)) { cleanup(1); printk("Init failed, I/O port (%X) already in use\n", mcd_port); return -EIO; } blksize_size[MAJOR_NR] = mcd_blocksizes; blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; read_ahead[MAJOR_NR] = 4; /* check for card */ outb(0, MCDPORT(1)); /* send reset */ for (count = 0; count < 2000000; count++) (void) inb(MCDPORT(1)); /* delay a bit */ outb(0x40, MCDPORT(0)); /* send get-stat cmd */ for (count = 0; count < 2000000; count++) if (!(inb(MCDPORT(1)) & MFL_STATUS)) break; if (count >= 2000000) { printk("Init failed. No mcd device at 0x%x irq %d\n", mcd_port, mcd_irq); cleanup(1); return -EIO; } count = inb(MCDPORT(0)); /* pick up the status */ outb(MCMD_GET_VERSION,MCDPORT(0)); for(count=0;count<3;count++) if(getValue(result+count)) { printk("mitsumi get version failed at 0x%d\n", mcd_port); cleanup(1); return -EIO; } if (result[0] == result[1] && result[1] == result[2]) { cleanup(1); return -EIO; } mcdVersion=result[2]; if (mcdVersion >=4) outb(4,MCDPORT(2)); /* magic happens */ /* don't get the IRQ until we know for sure the drive is there */ if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) { printk("Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq); cleanup(1); return -EIO; } if (result[1] == 'D') { sprintf(msg, " mcd: Mitsumi Double Speed CD-ROM at port=0x%x," " irq=%d\n", mcd_port, mcd_irq); MCMD_DATA_READ = MCMD_2X_READ; mcd_info.speed = 2; /* Added flag to drop to 1x speed if too many errors */ mcdDouble = 1; } else { sprintf(msg, " mcd: Mitsumi Single Speed CD-ROM at port=0x%x," " irq=%d\n", mcd_port, mcd_irq); mcd_info.speed = 2; } request_region(mcd_port, 4, "mcd"); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); outb(0x02,MCDPORT(0)); outb(0x00,MCDPORT(0)); getValue(result); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); outb(0x10,MCDPORT(0)); outb(0x04,MCDPORT(0)); getValue(result); mcd_invalidate_buffers(); mcdPresent = 1; mcd_info.dev = MKDEV(MAJOR_NR,0); if (register_cdrom(&mcd_info) != 0) { printk("Cannot register Mitsumi CD-ROM!\n"); cleanup(3); return -EIO; } printk(msg); return 0; }
int __init mcd_init(void) { struct gendisk *disk = alloc_disk(1); int count; unsigned char result[3]; char msg[80]; if (!disk) { printk(KERN_INFO "mcd: can't allocated disk.\n"); return -ENOMEM; } if (mcd_port <= 0 || mcd_irq <= 0) { printk(KERN_INFO "mcd: not probing.\n"); put_disk(disk); return -EIO; } if (register_blkdev(MAJOR_NR, "mcd")) { put_disk(disk); return -EIO; } if (!request_region(mcd_port, 4, "mcd")) { printk(KERN_ERR "mcd: Initialization failed, I/O port (%X) already in use\n", mcd_port); goto out_region; } mcd_queue = blk_init_queue(do_mcd_request, &mcd_spinlock); if (!mcd_queue) goto out_queue; /* check for card */ outb(0, MCDPORT(1)); /* send reset */ for (count = 0; count < 2000000; count++) (void) inb(MCDPORT(1)); /* delay a bit */ outb(0x40, MCDPORT(0)); /* send get-stat cmd */ for (count = 0; count < 2000000; count++) if (!(inb(MCDPORT(1)) & MFL_STATUS)) break; if (count >= 2000000) { printk(KERN_INFO "mcd: initialisation failed - No mcd device at 0x%x irq %d\n", mcd_port, mcd_irq); goto out_probe; } count = inb(MCDPORT(0)); /* pick up the status */ outb(MCMD_GET_VERSION, MCDPORT(0)); for (count = 0; count < 3; count++) if (getValue(result + count)) { printk(KERN_ERR "mcd: mitsumi get version failed at 0x%x\n", mcd_port); goto out_probe; } if (result[0] == result[1] && result[1] == result[2]) goto out_probe; mcdVersion = result[2]; if (mcdVersion >= 4) outb(4, MCDPORT(2)); /* magic happens */ /* don't get the IRQ until we know for sure the drive is there */ if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) { printk(KERN_ERR "mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq); goto out_probe; } if (result[1] == 'D') { MCMD_DATA_READ = MCMD_2X_READ; /* Added flag to drop to 1x speed if too many errors */ mcdDouble = 1; } else mcd_info.speed = 1; sprintf(msg, " mcd: Mitsumi %s Speed CD-ROM at port=0x%x," " irq=%d\n", mcd_info.speed == 1 ? "Single" : "Double", mcd_port, mcd_irq); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); outb(0x02, MCDPORT(0)); outb(0x00, MCDPORT(0)); getValue(result); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); outb(0x10, MCDPORT(0)); outb(0x04, MCDPORT(0)); getValue(result); mcd_invalidate_buffers(); mcdPresent = 1; disk->major = MAJOR_NR; disk->first_minor = 0; sprintf(disk->disk_name, "mcd"); disk->fops = &mcd_bdops; disk->flags = GENHD_FL_CD; mcd_gendisk = disk; if (register_cdrom(&mcd_info) != 0) { printk(KERN_ERR "mcd: Unable to register Mitsumi CD-ROM.\n"); goto out_cdrom; } disk->queue = mcd_queue; add_disk(disk); printk(msg); return 0; out_cdrom: free_irq(mcd_irq, NULL); out_queue: release_region(mcd_port, 4); out_probe: blk_cleanup_queue(mcd_queue); out_region: unregister_blkdev(MAJOR_NR, "mcd"); put_disk(disk); return -EIO; }
static int mcd_ioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg) { int i, st; struct mcd_Toc qInfo; struct cdrom_ti ti; struct cdrom_tochdr tocHdr; struct cdrom_msf msf; struct cdrom_tocentry entry; struct mcd_Toc *tocPtr; struct cdrom_subchnl subchnl; struct cdrom_volctrl volctrl; if (!ip) return -EINVAL; st = statusCmd(); if (st < 0) return -EIO; if (!tocUpToDate) { i = updateToc(); if (i < 0) /* [email protected]: We _can_ open the door even without a CD */ if (cmd != CDROMEJECT) return i; /* error reading TOC */ } switch (cmd) { case CDROMSTART: /* Spin up the drive */ /* Don't think we can do this. Even if we could, * I think the drive times out and stops after a while * anyway. For now, ignore it. */ return 0; case CDROMSTOP: /* Spin down the drive */ outb(MCMD_STOP, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); /* should we do anything if it fails? */ audioStatus = CDROM_AUDIO_NO_STATUS; return 0; case CDROMPAUSE: /* Pause the drive */ if (audioStatus != CDROM_AUDIO_PLAY) return -EINVAL; outb(MCMD_STOP, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); if (GetQChannelInfo(&qInfo) < 0) { /* didn't get q channel info */ audioStatus = CDROM_AUDIO_NO_STATUS; return 0; } mcd_Play.start = qInfo.diskTime; /* remember restart point */ audioStatus = CDROM_AUDIO_PAUSED; return 0; case CDROMRESUME: /* Play it again, Sam */ if (audioStatus != CDROM_AUDIO_PAUSED) return -EINVAL; /* restart the drive at the saved position. */ i = mcdPlay(&mcd_Play); if (i < 0) { audioStatus = CDROM_AUDIO_ERROR; return -EIO; } audioStatus = CDROM_AUDIO_PLAY; return 0; case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ st = verify_area(VERIFY_READ, (void *) arg, sizeof ti); if (st) return st; memcpy_fromfs(&ti, (void *) arg, sizeof ti); if (ti.cdti_trk0 < DiskInfo.first || ti.cdti_trk0 > DiskInfo.last || ti.cdti_trk1 < ti.cdti_trk0) { return -EINVAL; } if (ti.cdti_trk1 > DiskInfo.last) ti. cdti_trk1 = DiskInfo.last; mcd_Play.start = Toc[ti.cdti_trk0].diskTime; mcd_Play.end = Toc[ti.cdti_trk1 + 1].diskTime; #ifdef MCD_DEBUG printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n", mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame, mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame); #endif i = mcdPlay(&mcd_Play); if (i < 0) { audioStatus = CDROM_AUDIO_ERROR; return -EIO; } audioStatus = CDROM_AUDIO_PLAY; return 0; case CDROMPLAYMSF: /* Play starting at the given MSF address. */ if (audioStatus == CDROM_AUDIO_PLAY) { outb(MCMD_STOP, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); audioStatus = CDROM_AUDIO_NO_STATUS; } st = verify_area(VERIFY_READ, (void *) arg, sizeof msf); if (st) return st; memcpy_fromfs(&msf, (void *) arg, sizeof msf); /* convert to bcd */ bin2bcd(&msf.cdmsf_min0); bin2bcd(&msf.cdmsf_sec0); bin2bcd(&msf.cdmsf_frame0); bin2bcd(&msf.cdmsf_min1); bin2bcd(&msf.cdmsf_sec1); bin2bcd(&msf.cdmsf_frame1); mcd_Play.start.min = msf.cdmsf_min0; mcd_Play.start.sec = msf.cdmsf_sec0; mcd_Play.start.frame = msf.cdmsf_frame0; mcd_Play.end.min = msf.cdmsf_min1; mcd_Play.end.sec = msf.cdmsf_sec1; mcd_Play.end.frame = msf.cdmsf_frame1; #ifdef MCD_DEBUG printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n", mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame, mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame); #endif i = mcdPlay(&mcd_Play); if (i < 0) { audioStatus = CDROM_AUDIO_ERROR; return -EIO; } audioStatus = CDROM_AUDIO_PLAY; return 0; case CDROMREADTOCHDR: /* Read the table of contents header */ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof tocHdr); if (st) return st; tocHdr.cdth_trk0 = DiskInfo.first; tocHdr.cdth_trk1 = DiskInfo.last; memcpy_tofs((void *) arg, &tocHdr, sizeof tocHdr); return 0; case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry); if (st) return st; memcpy_fromfs(&entry, (void *) arg, sizeof entry); if (entry.cdte_track == CDROM_LEADOUT) /* XXX */ tocPtr = &Toc[DiskInfo.last + 1]; else if (entry.cdte_track > DiskInfo.last || entry.cdte_track < DiskInfo.first) return -EINVAL; else tocPtr = &Toc[entry.cdte_track]; entry.cdte_adr = tocPtr -> ctrl_addr; entry.cdte_ctrl = tocPtr -> ctrl_addr >> 4; if (entry.cdte_format == CDROM_LBA) entry.cdte_addr.lba = msf2hsg(&tocPtr -> diskTime); else if (entry.cdte_format == CDROM_MSF) { entry.cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min); entry.cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec); entry.cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame); } else return -EINVAL; memcpy_tofs((void *) arg, &entry, sizeof entry); return 0; case CDROMSUBCHNL: /* Get subchannel info */ st = verify_area(VERIFY_WRITE, (void *) arg, sizeof subchnl); if (st) return st; memcpy_fromfs(&subchnl, (void *) arg, sizeof subchnl); if (GetQChannelInfo(&qInfo) < 0) return -EIO; subchnl.cdsc_audiostatus = audioStatus; subchnl.cdsc_adr = qInfo.ctrl_addr; subchnl.cdsc_ctrl = qInfo.ctrl_addr >> 4; subchnl.cdsc_trk = bcd2bin(qInfo.track); subchnl.cdsc_ind = bcd2bin(qInfo.pointIndex); if (subchnl.cdsc_format == CDROM_LBA) { subchnl.cdsc_absaddr.lba = msf2hsg(&qInfo.diskTime); subchnl.cdsc_reladdr.lba = msf2hsg(&qInfo.trackTime); } else if (subchnl.cdsc_format == CDROM_MSF) { subchnl.cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min); subchnl.cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec); subchnl.cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame); subchnl.cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min); subchnl.cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec); subchnl.cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame); } else return -EINVAL; memcpy_tofs((void *) arg, &subchnl, sizeof subchnl); return 0; case CDROMVOLCTRL: /* Volume control */ st = verify_area(VERIFY_READ, (void *) arg, sizeof(volctrl)); if (st) return st; memcpy_fromfs(&volctrl, (char *) arg, sizeof(volctrl)); outb(MCMD_SET_VOLUME, MCDPORT(0)); outb(volctrl.channel0, MCDPORT(0)); outb(255, MCDPORT(0)); outb(volctrl.channel1, MCDPORT(0)); outb(255, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); if (i < 0) return -EIO; { char a, b, c, d; getValue(&a); getValue(&b); getValue(&c); getValue(&d); } return 0; case CDROMEJECT: /* all drives can at least stop! */ if (audioStatus == CDROM_AUDIO_PLAY) { outb(MCMD_STOP, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); } audioStatus = CDROM_AUDIO_NO_STATUS; outb(MCMD_EJECT, MCDPORT(0)); /* * the status (i) shows failure on all but the FX drives. * But nothing we can do about that in software! * So just read the status and forget it. - Jon. */ i = getMcdStatus(MCD_STATUS_DELAY); return 0; default: return -EINVAL; } }
int mcd_audio_ioctl(struct cdrom_device_info * cdi, unsigned int cmd, void * arg) { int i, st; struct mcd_Toc qInfo; struct cdrom_ti *ti; struct cdrom_tochdr *tocHdr; struct cdrom_msf *msf; struct cdrom_subchnl *subchnl; struct cdrom_tocentry *entry; struct mcd_Toc *tocPtr; struct cdrom_volctrl *volctrl; st = statusCmd(); if (st < 0) return -EIO; if (!tocUpToDate) { i = updateToc(); if (i < 0) return i; /* error reading TOC */ } switch (cmd) { case CDROMSTART: /* Spin up the drive */ /* Don't think we can do this. Even if we could, * I think the drive times out and stops after a while * anyway. For now, ignore it. */ return 0; case CDROMSTOP: /* Spin down the drive */ outb(MCMD_STOP, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); /* should we do anything if it fails? */ audioStatus = CDROM_AUDIO_NO_STATUS; return 0; case CDROMPAUSE: /* Pause the drive */ if (audioStatus != CDROM_AUDIO_PLAY) return -EINVAL; outb(MCMD_STOP, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); if (GetQChannelInfo(&qInfo) < 0) { /* didn't get q channel info */ audioStatus = CDROM_AUDIO_NO_STATUS; return 0; } mcd_Play.start = qInfo.diskTime; /* remember restart point */ audioStatus = CDROM_AUDIO_PAUSED; return 0; case CDROMRESUME: /* Play it again, Sam */ if (audioStatus != CDROM_AUDIO_PAUSED) return -EINVAL; /* restart the drive at the saved position. */ i = mcdPlay(&mcd_Play); if (i < 0) { audioStatus = CDROM_AUDIO_ERROR; return -EIO; } audioStatus = CDROM_AUDIO_PLAY; return 0; case CDROMPLAYTRKIND: /* Play a track. This currently ignores index. */ ti=(struct cdrom_ti *) arg; if (ti->cdti_trk0 < DiskInfo.first || ti->cdti_trk0 > DiskInfo.last || ti->cdti_trk1 < ti->cdti_trk0) { return -EINVAL; } if (ti->cdti_trk1 > DiskInfo.last) ti->cdti_trk1 = DiskInfo.last; mcd_Play.start = Toc[ti->cdti_trk0].diskTime; mcd_Play.end = Toc[ti->cdti_trk1 + 1].diskTime; #ifdef MCD_DEBUG printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n", mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame, mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame); #endif i = mcdPlay(&mcd_Play); if (i < 0) { audioStatus = CDROM_AUDIO_ERROR; return -EIO; } audioStatus = CDROM_AUDIO_PLAY; return 0; case CDROMPLAYMSF: /* Play starting at the given MSF address. */ if (audioStatus == CDROM_AUDIO_PLAY) { outb(MCMD_STOP, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); audioStatus = CDROM_AUDIO_NO_STATUS; } msf=(struct cdrom_msf *) arg; /* convert to bcd */ bin2bcd(&msf->cdmsf_min0); bin2bcd(&msf->cdmsf_sec0); bin2bcd(&msf->cdmsf_frame0); bin2bcd(&msf->cdmsf_min1); bin2bcd(&msf->cdmsf_sec1); bin2bcd(&msf->cdmsf_frame1); mcd_Play.start.min = msf->cdmsf_min0; mcd_Play.start.sec = msf->cdmsf_sec0; mcd_Play.start.frame = msf->cdmsf_frame0; mcd_Play.end.min = msf->cdmsf_min1; mcd_Play.end.sec = msf->cdmsf_sec1; mcd_Play.end.frame = msf->cdmsf_frame1; #ifdef MCD_DEBUG printk("play: %02x:%02x.%02x to %02x:%02x.%02x\n", mcd_Play.start.min, mcd_Play.start.sec, mcd_Play.start.frame, mcd_Play.end.min, mcd_Play.end.sec, mcd_Play.end.frame); #endif i = mcdPlay(&mcd_Play); if (i < 0) { audioStatus = CDROM_AUDIO_ERROR; return -EIO; } audioStatus = CDROM_AUDIO_PLAY; return 0; case CDROMREADTOCHDR: /* Read the table of contents header */ tocHdr=(struct cdrom_tochdr *) arg; tocHdr->cdth_trk0 = DiskInfo.first; tocHdr->cdth_trk1 = DiskInfo.last; return 0; case CDROMREADTOCENTRY: /* Read an entry in the table of contents */ entry=(struct cdrom_tocentry *) arg; if (entry->cdte_track == CDROM_LEADOUT) tocPtr = &Toc[DiskInfo.last - DiskInfo.first + 1]; else if (entry->cdte_track > DiskInfo.last || entry->cdte_track < DiskInfo.first) return -EINVAL; else tocPtr = &Toc[entry->cdte_track]; entry->cdte_adr = tocPtr -> ctrl_addr; entry->cdte_ctrl = tocPtr -> ctrl_addr >> 4; if (entry->cdte_format == CDROM_LBA) entry->cdte_addr.lba = msf2hsg(&tocPtr -> diskTime); else if (entry->cdte_format == CDROM_MSF) { entry->cdte_addr.msf.minute = bcd2bin(tocPtr -> diskTime.min); entry->cdte_addr.msf.second = bcd2bin(tocPtr -> diskTime.sec); entry->cdte_addr.msf.frame = bcd2bin(tocPtr -> diskTime.frame); } else return -EINVAL; return 0; case CDROMSUBCHNL: /* Get subchannel info */ subchnl=(struct cdrom_subchnl *) arg; if (GetQChannelInfo(&qInfo) < 0) return -EIO; subchnl->cdsc_audiostatus = audioStatus; subchnl->cdsc_adr = qInfo.ctrl_addr; subchnl->cdsc_ctrl = qInfo.ctrl_addr >> 4; subchnl->cdsc_trk = bcd2bin(qInfo.track); subchnl->cdsc_ind = bcd2bin(qInfo.pointIndex); subchnl->cdsc_absaddr.msf.minute = bcd2bin(qInfo.diskTime.min); subchnl->cdsc_absaddr.msf.second = bcd2bin(qInfo.diskTime.sec); subchnl->cdsc_absaddr.msf.frame = bcd2bin(qInfo.diskTime.frame); subchnl->cdsc_reladdr.msf.minute = bcd2bin(qInfo.trackTime.min); subchnl->cdsc_reladdr.msf.second = bcd2bin(qInfo.trackTime.sec); subchnl->cdsc_reladdr.msf.frame = bcd2bin(qInfo.trackTime.frame); return(0); case CDROMVOLCTRL: /* Volume control */ volctrl=(struct cdrom_volctrl *) arg; outb(MCMD_SET_VOLUME, MCDPORT(0)); outb(volctrl->channel0, MCDPORT(0)); outb(255, MCDPORT(0)); outb(volctrl->channel1, MCDPORT(0)); outb(255, MCDPORT(0)); i = getMcdStatus(MCD_STATUS_DELAY); if (i < 0) return -EIO; { char a, b, c, d; getValue(&a); getValue(&b); getValue(&c); getValue(&d); } return 0; default: return -EINVAL; } }
static void mcd_poll(void) { int st; if (mcd_error) { if (mcd_error & 0xA5) { printk("mcd: I/O error 0x%02x", mcd_error); if (mcd_error & 0x80) printk(" (Door open)"); if (mcd_error & 0x20) printk(" (Disk changed)"); if (mcd_error & 0x04) { printk(" (Read error)"); /* Bitch about the problem. */ /* Time to get fancy! If at 2x speed and 1 error, drop to 1x speed! */ /* Interesting how it STAYS at MCD_RETRY_ATTEMPTS on first error! */ /* But I find that rather HANDY!!! */ /* Neat! it REALLY WORKS on those LOW QUALITY CD's!!! Smile! :) */ /* AJK [06/17/95] */ /* Slap the CD down to single speed! */ if (mcdDouble == 1 && McdTries == MCD_RETRY_ATTEMPTS && MCMD_DATA_READ == MCMD_2X_READ) { MCMD_DATA_READ = MCMD_PLAY_READ; /* Uhhh, Ummmm, muhuh-huh! */ mcd1xhold = SINGLE_HOLD_SECTORS; /* Hey Beavis! */ printk(" Speed now 1x"); /* Pull my finger! */ } } printk("\n"); mcd_invalidate_buffers(); #ifdef WARN_IF_READ_FAILURE if (McdTries == MCD_RETRY_ATTEMPTS) printk("mcd: read of block %d failed\n", mcd_next_bn); #endif if (!McdTries--) { /* Nuts! This cd is ready for recycling! */ /* When WAS the last time YOU cleaned it CORRECTLY?! */ printk("mcd: read of block %d failed, giving up\n", mcd_next_bn); if (mcd_transfer_is_active) { McdTries = 0; goto ret; } if (CURRENT_VALID) end_request(0); McdTries = MCD_RETRY_ATTEMPTS; } } mcd_error = 0; mcd_state = MCD_S_STOP; } /* Switch back to Double speed if enough GOOD sectors were read! */ /* Are we a double speed with a crappy CD?! */ if (mcdDouble == 1 && McdTries == MCD_RETRY_ATTEMPTS && MCMD_DATA_READ == MCMD_PLAY_READ) { /* We ARE a double speed and we ARE bitching! */ if (mcd1xhold == 0) /* Okay, Like are we STILL at single speed? */ { /* We need to switch back to double speed now... */ MCMD_DATA_READ = MCMD_2X_READ; /* Uhhh... BACK You GO! */ printk("mcd: Switching back to 2X speed!\n"); /* Tell 'em! */ } else mcd1xhold--; /* No?! Count down the good reads some more... */ /* and try, try again! */ } immediately: switch (mcd_state) { case MCD_S_IDLE: #ifdef TEST3 printk("MCD_S_IDLE\n"); #endif return; case MCD_S_START: #ifdef TEST3 printk("MCD_S_START\n"); #endif outb(MCMD_GET_STATUS, MCDPORT(0)); mcd_state = mcd_mode == 1 ? MCD_S_READ : MCD_S_MODE; McdTimeout = 3000; break; case MCD_S_MODE: #ifdef TEST3 printk("MCD_S_MODE\n"); #endif if ((st = mcdStatus()) != -1) { if (st & MST_DSK_CHG) { mcdDiskChanged = 1; tocUpToDate = 0; mcd_invalidate_buffers(); } set_mode_immediately: if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) { mcdDiskChanged = 1; tocUpToDate = 0; if (mcd_transfer_is_active) { mcd_state = MCD_S_START; goto immediately; } printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n"); mcd_state = MCD_S_IDLE; while (CURRENT_VALID) end_request(0); return; } outb(MCMD_SET_MODE, MCDPORT(0)); outb(1, MCDPORT(0)); mcd_mode = 1; mcd_state = MCD_S_READ; McdTimeout = 3000; } break; case MCD_S_READ: #ifdef TEST3 printk("MCD_S_READ\n"); #endif if ((st = mcdStatus()) != -1) { if (st & MST_DSK_CHG) { mcdDiskChanged = 1; tocUpToDate = 0; mcd_invalidate_buffers(); } read_immediately: if ((st & MST_DOOR_OPEN) || !(st & MST_READY)) { mcdDiskChanged = 1; tocUpToDate = 0; if (mcd_transfer_is_active) { mcd_state = MCD_S_START; goto immediately; } printk((st & MST_DOOR_OPEN) ? "mcd: door open\n" : "mcd: disk removed\n"); mcd_state = MCD_S_IDLE; while (CURRENT_VALID) end_request(0); return; } if (CURRENT_VALID) { struct mcd_Play_msf msf; mcd_next_bn = CURRENT -> sector / 4; hsg2msf(mcd_next_bn, &msf.start); msf.end.min = ~0; msf.end.sec = ~0; msf.end.frame = ~0; sendMcdCmd(MCMD_DATA_READ, &msf); mcd_state = MCD_S_DATA; McdTimeout = READ_TIMEOUT; } else { mcd_state = MCD_S_STOP; goto immediately; } } break; case MCD_S_DATA: #ifdef TEST3 printk("MCD_S_DATA\n"); #endif st = inb(MCDPORT(1)) & (MFL_STATUSorDATA); data_immediately: #ifdef TEST5 printk("Status %02x\n",st); #endif switch (st) { case MFL_DATA: #ifdef WARN_IF_READ_FAILURE if (McdTries == 5) printk("mcd: read of block %d failed\n", mcd_next_bn); #endif if (!McdTries--) { printk("mcd: read of block %d failed, giving up\n", mcd_next_bn); if (mcd_transfer_is_active) { McdTries = 0; break; } if (CURRENT_VALID) end_request(0); McdTries = 5; } mcd_state = MCD_S_START; McdTimeout = READ_TIMEOUT; goto immediately; case MFL_STATUSorDATA: break; default: McdTries = 5; if (!CURRENT_VALID && mcd_buf_in == mcd_buf_out) { mcd_state = MCD_S_STOP; goto immediately; } mcd_buf_bn[mcd_buf_in] = -1; READ_DATA(MCDPORT(0), mcd_buf + 2048 * mcd_buf_in, 2048); mcd_buf_bn[mcd_buf_in] = mcd_next_bn++; if (mcd_buf_out == -1) mcd_buf_out = mcd_buf_in; mcd_buf_in = mcd_buf_in + 1 == MCD_BUF_SIZ ? 0 : mcd_buf_in + 1; if (!mcd_transfer_is_active) { while (CURRENT_VALID) { mcd_transfer(); if (CURRENT -> nr_sectors == 0) end_request(1); else break; } } if (CURRENT_VALID && (CURRENT -> sector / 4 < mcd_next_bn || CURRENT -> sector / 4 > mcd_next_bn + 16)) { mcd_state = MCD_S_STOP; goto immediately; } McdTimeout = READ_TIMEOUT; #ifdef DOUBLE_QUICK_ONLY if (MCMD_DATA_READ != MCMD_PLAY_READ) #endif { int count= QUICK_LOOP_COUNT; while (count--) { QUICK_LOOP_DELAY; if ((st = (inb(MCDPORT(1))) & (MFL_STATUSorDATA)) != (MFL_STATUSorDATA)) { # ifdef TEST4 /* printk("Quickloop success at %d\n",QUICK_LOOP_COUNT-count); */ printk(" %d ",QUICK_LOOP_COUNT-count); # endif goto data_immediately; } } # ifdef TEST4 /* printk("Quickloop ended at %d\n",QUICK_LOOP_COUNT); */ printk("ended "); # endif } break; } break; case MCD_S_STOP: #ifdef TEST3 printk("MCD_S_STOP\n"); #endif #ifdef WORK_AROUND_MITSUMI_BUG_93 if (!mitsumi_bug_93_wait) goto do_not_work_around_mitsumi_bug_93_1; McdTimeout = mitsumi_bug_93_wait; mcd_state = 9+3+1; break; case 9+3+1: if (McdTimeout) break; do_not_work_around_mitsumi_bug_93_1: #endif /* WORK_AROUND_MITSUMI_BUG_93 */ outb(MCMD_STOP, MCDPORT(0)); #ifdef WORK_AROUND_MITSUMI_BUG_92 if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) { int i = 4096; do { inb(MCDPORT(0)); } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i); outb(MCMD_STOP, MCDPORT(0)); if ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS) { i = 4096; do { inb(MCDPORT(0)); } while ((inb(MCDPORT(1)) & MFL_STATUSorDATA) == MFL_STATUS && --i); outb(MCMD_STOP, MCDPORT(0)); } } #endif /* WORK_AROUND_MITSUMI_BUG_92 */ mcd_state = MCD_S_STOPPING; McdTimeout = 1000; break; case MCD_S_STOPPING: #ifdef TEST3 printk("MCD_S_STOPPING\n"); #endif if ((st = mcdStatus()) == -1 && McdTimeout) break; if ((st != -1) && (st & MST_DSK_CHG)) { mcdDiskChanged = 1; tocUpToDate = 0; mcd_invalidate_buffers(); } #ifdef WORK_AROUND_MITSUMI_BUG_93 if (!mitsumi_bug_93_wait) goto do_not_work_around_mitsumi_bug_93_2; McdTimeout = mitsumi_bug_93_wait; mcd_state = 9+3+2; break; case 9+3+2: if (McdTimeout) break; st = -1; do_not_work_around_mitsumi_bug_93_2: #endif /* WORK_AROUND_MITSUMI_BUG_93 */ #ifdef TEST3 printk("CURRENT_VALID %d mcd_mode %d\n", CURRENT_VALID, mcd_mode); #endif if (CURRENT_VALID) { if (st != -1) { if (mcd_mode == 1) goto read_immediately; else goto set_mode_immediately; } else { mcd_state = MCD_S_START; McdTimeout = 1; } } else { mcd_state = MCD_S_IDLE; return; } break; default: printk("mcd: invalid state %d\n", mcd_state); return; } ret: if (!McdTimeout--) { printk("mcd: timeout in state %d\n", mcd_state); mcd_state = MCD_S_STOP; } SET_TIMER(mcd_poll, 1); }
int __init mcd_init(void) { int count; unsigned char result[3]; char msg[80]; if (mcd_port <= 0 || mcd_irq <= 0) { printk(KERN_INFO "mcd: not probing.\n"); return -EIO; } if (devfs_register_blkdev(MAJOR_NR, "mcd", &mcd_bdops) != 0) { printk(KERN_ERR "mcd: Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR); return -EIO; } if (check_region(mcd_port, 4)) { cleanup(1); printk(KERN_ERR "mcd: Initialization failed, I/O port (%X) already in use\n", mcd_port); return -EIO; } blksize_size[MAJOR_NR] = mcd_blocksizes; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST, &mcd_spinlock); read_ahead[MAJOR_NR] = 4; /* check for card */ outb(0, MCDPORT(1)); /* send reset */ for (count = 0; count < 2000000; count++) (void) inb(MCDPORT(1)); /* delay a bit */ outb(0x40, MCDPORT(0)); /* send get-stat cmd */ for (count = 0; count < 2000000; count++) if (!(inb(MCDPORT(1)) & MFL_STATUS)) break; if (count >= 2000000) { printk(KERN_INFO "mcd: initialisation failed - No mcd device at 0x%x irq %d\n", mcd_port, mcd_irq); cleanup(1); return -EIO; } count = inb(MCDPORT(0)); /* pick up the status */ outb(MCMD_GET_VERSION, MCDPORT(0)); for (count = 0; count < 3; count++) if (getValue(result + count)) { printk(KERN_ERR "mcd: mitsumi get version failed at 0x%x\n", mcd_port); cleanup(1); return -EIO; } if (result[0] == result[1] && result[1] == result[2]) { cleanup(1); return -EIO; } mcdVersion = result[2]; if (mcdVersion >= 4) outb(4, MCDPORT(2)); /* magic happens */ /* don't get the IRQ until we know for sure the drive is there */ if (request_irq(mcd_irq, mcd_interrupt, SA_INTERRUPT, "Mitsumi CD", NULL)) { printk(KERN_ERR "mcd: Unable to get IRQ%d for Mitsumi CD-ROM\n", mcd_irq); cleanup(1); return -EIO; } if (result[1] == 'D') { MCMD_DATA_READ = MCMD_2X_READ; /* Added flag to drop to 1x speed if too many errors */ mcdDouble = 1; } else mcd_info.speed = 1; sprintf(msg, " mcd: Mitsumi %s Speed CD-ROM at port=0x%x," " irq=%d\n", mcd_info.speed == 1 ? "Single" : "Double", mcd_port, mcd_irq); request_region(mcd_port, 4, "mcd"); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); outb(0x02, MCDPORT(0)); outb(0x00, MCDPORT(0)); getValue(result); outb(MCMD_CONFIG_DRIVE, MCDPORT(0)); outb(0x10, MCDPORT(0)); outb(0x04, MCDPORT(0)); getValue(result); mcd_invalidate_buffers(); mcdPresent = 1; mcd_info.dev = mk_kdev(MAJOR_NR, 0); if (register_cdrom(&mcd_info) != 0) { printk(KERN_ERR "mcd: Unable to register Mitsumi CD-ROM.\n"); cleanup(3); return -EIO; } devfs_plain_cdrom(&mcd_info, &mcd_bdops); printk(msg); return 0; }